|
import json |
|
import html |
|
import os |
|
import platform |
|
import sys |
|
|
|
import gradio as gr |
|
import subprocess as sp |
|
|
|
from modules import call_queue, shared |
|
from modules.generation_parameters_copypaste import image_from_url_text |
|
import modules.images |
|
from modules.ui_components import ToolButton |
|
import modules.generation_parameters_copypaste as parameters_copypaste |
|
|
|
folder_symbol = '\U0001f4c2' |
|
refresh_symbol = '\U0001f504' |
|
|
|
|
|
def update_generation_info(generation_info, html_info, img_index): |
|
try: |
|
generation_info = json.loads(generation_info) |
|
if img_index < 0 or img_index >= len(generation_info["infotexts"]): |
|
return html_info, gr.update() |
|
return plaintext_to_html(generation_info["infotexts"][img_index]), gr.update() |
|
except Exception: |
|
pass |
|
|
|
return html_info, gr.update() |
|
|
|
|
|
def plaintext_to_html(text, classname=None): |
|
content = "<br>\n".join(html.escape(x) for x in text.split('\n')) |
|
|
|
return f"<p class='{classname}'>{content}</p>" if classname else f"<p>{content}</p>" |
|
|
|
|
|
def save_files(js_data, images, do_make_zip, index): |
|
import csv |
|
filenames = [] |
|
fullfns = [] |
|
|
|
|
|
class MyObject: |
|
def __init__(self, d=None): |
|
if d is not None: |
|
for key, value in d.items(): |
|
setattr(self, key, value) |
|
|
|
data = json.loads(js_data) |
|
|
|
p = MyObject(data) |
|
path = shared.opts.outdir_save |
|
save_to_dirs = shared.opts.use_save_to_dirs_for_ui |
|
extension: str = shared.opts.samples_format |
|
start_index = 0 |
|
only_one = False |
|
|
|
if index > -1 and shared.opts.save_selected_only and (index >= data["index_of_first_image"]): |
|
only_one = True |
|
images = [images[index]] |
|
start_index = index |
|
|
|
os.makedirs(shared.opts.outdir_save, exist_ok=True) |
|
|
|
with open(os.path.join(shared.opts.outdir_save, "log.csv"), "a", encoding="utf8", newline='') as file: |
|
at_start = file.tell() == 0 |
|
writer = csv.writer(file) |
|
if at_start: |
|
writer.writerow(["prompt", "seed", "width", "height", "sampler", "cfgs", "steps", "filename", "negative_prompt"]) |
|
|
|
for image_index, filedata in enumerate(images, start_index): |
|
image = image_from_url_text(filedata) |
|
|
|
is_grid = image_index < p.index_of_first_image |
|
i = 0 if is_grid else (image_index - p.index_of_first_image) |
|
|
|
p.batch_index = image_index-1 |
|
fullfn, txt_fullfn = modules.images.save_image(image, path, "", seed=p.all_seeds[i], prompt=p.all_prompts[i], extension=extension, info=p.infotexts[image_index], grid=is_grid, p=p, save_to_dirs=save_to_dirs) |
|
|
|
filename = os.path.relpath(fullfn, path) |
|
filenames.append(filename) |
|
fullfns.append(fullfn) |
|
if txt_fullfn: |
|
filenames.append(os.path.basename(txt_fullfn)) |
|
fullfns.append(txt_fullfn) |
|
|
|
writer.writerow([data["prompt"], data["seed"], data["width"], data["height"], data["sampler_name"], data["cfg_scale"], data["steps"], filenames[0], data["negative_prompt"]]) |
|
|
|
|
|
if do_make_zip: |
|
zip_fileseed = p.all_seeds[index-1] if only_one else p.all_seeds[0] |
|
namegen = modules.images.FilenameGenerator(p, zip_fileseed, p.all_prompts[0], image, True) |
|
zip_filename = namegen.apply(shared.opts.grid_zip_filename_pattern or "[datetime]_[[model_name]]_[seed]-[seed_last]") |
|
zip_filepath = os.path.join(path, f"{zip_filename}.zip") |
|
|
|
from zipfile import ZipFile |
|
with ZipFile(zip_filepath, "w") as zip_file: |
|
for i in range(len(fullfns)): |
|
with open(fullfns[i], mode="rb") as f: |
|
zip_file.writestr(filenames[i], f.read()) |
|
fullfns.insert(0, zip_filepath) |
|
|
|
return gr.File.update(value=fullfns, visible=True), plaintext_to_html(f"Saved: {filenames[0]}") |
|
|
|
|
|
def create_output_panel(tabname, outdir): |
|
|
|
def open_folder(f): |
|
if not os.path.exists(f): |
|
print(f'Folder "{f}" does not exist. After you create an image, the folder will be created.') |
|
return |
|
elif not os.path.isdir(f): |
|
print(f""" |
|
WARNING |
|
An open_folder request was made with an argument that is not a folder. |
|
This could be an error or a malicious attempt to run code on your computer. |
|
Requested path was: {f} |
|
""", file=sys.stderr) |
|
return |
|
|
|
if not shared.cmd_opts.hide_ui_dir_config: |
|
path = os.path.normpath(f) |
|
if platform.system() == "Windows": |
|
os.startfile(path) |
|
elif platform.system() == "Darwin": |
|
sp.Popen(["open", path]) |
|
elif "microsoft-standard-WSL2" in platform.uname().release: |
|
sp.Popen(["wsl-open", path]) |
|
else: |
|
sp.Popen(["xdg-open", path]) |
|
|
|
with gr.Column(variant='panel', elem_id=f"{tabname}_results"): |
|
with gr.Group(elem_id=f"{tabname}_gallery_container"): |
|
result_gallery = gr.Gallery(label='Output', show_label=False, elem_id=f"{tabname}_gallery", columns=4, preview=True, height=shared.opts.gallery_height or None) |
|
|
|
generation_info = None |
|
with gr.Column(): |
|
with gr.Row(elem_id=f"image_buttons_{tabname}", elem_classes="image-buttons"): |
|
open_folder_button = ToolButton(folder_symbol, elem_id=f'{tabname}_open_folder', visible=not shared.cmd_opts.hide_ui_dir_config, tooltip="Open images output directory.") |
|
|
|
if tabname != "extras": |
|
save = ToolButton('πΎ', elem_id=f'save_{tabname}', tooltip=f"Save the image to a dedicated directory ({shared.opts.outdir_save}).") |
|
save_zip = ToolButton('ποΈ', elem_id=f'save_zip_{tabname}', tooltip=f"Save zip archive with images to a dedicated directory ({shared.opts.outdir_save})") |
|
|
|
buttons = { |
|
'img2img': ToolButton('πΌοΈ', elem_id=f'{tabname}_send_to_img2img', tooltip="Send image and generation parameters to img2img tab."), |
|
'inpaint': ToolButton('π¨οΈ', elem_id=f'{tabname}_send_to_inpaint', tooltip="Send image and generation parameters to img2img inpaint tab."), |
|
'extras': ToolButton('π', elem_id=f'{tabname}_send_to_extras', tooltip="Send image and generation parameters to extras tab.") |
|
} |
|
|
|
open_folder_button.click( |
|
fn=lambda: open_folder(shared.opts.outdir_samples or outdir), |
|
inputs=[], |
|
outputs=[], |
|
) |
|
|
|
if tabname != "extras": |
|
download_files = gr.File(None, file_count="multiple", interactive=False, show_label=False, visible=False, elem_id=f'download_files_{tabname}') |
|
|
|
with gr.Group(): |
|
html_info = gr.HTML(elem_id=f'html_info_{tabname}', elem_classes="infotext") |
|
html_log = gr.HTML(elem_id=f'html_log_{tabname}', elem_classes="html-log") |
|
|
|
generation_info = gr.Textbox(visible=False, elem_id=f'generation_info_{tabname}') |
|
if tabname == 'txt2img' or tabname == 'img2img': |
|
generation_info_button = gr.Button(visible=False, elem_id=f"{tabname}_generation_info_button") |
|
generation_info_button.click( |
|
fn=update_generation_info, |
|
_js="function(x, y, z){ return [x, y, selected_gallery_index()] }", |
|
inputs=[generation_info, html_info, html_info], |
|
outputs=[html_info, html_info], |
|
show_progress=False, |
|
) |
|
|
|
save.click( |
|
fn=call_queue.wrap_gradio_call(save_files), |
|
_js="(x, y, z, w) => [x, y, false, selected_gallery_index()]", |
|
inputs=[ |
|
generation_info, |
|
result_gallery, |
|
html_info, |
|
html_info, |
|
], |
|
outputs=[ |
|
download_files, |
|
html_log, |
|
], |
|
show_progress=False, |
|
) |
|
|
|
save_zip.click( |
|
fn=call_queue.wrap_gradio_call(save_files), |
|
_js="(x, y, z, w) => [x, y, true, selected_gallery_index()]", |
|
inputs=[ |
|
generation_info, |
|
result_gallery, |
|
html_info, |
|
html_info, |
|
], |
|
outputs=[ |
|
download_files, |
|
html_log, |
|
] |
|
) |
|
|
|
else: |
|
html_info_x = gr.HTML(elem_id=f'html_info_x_{tabname}') |
|
html_info = gr.HTML(elem_id=f'html_info_{tabname}', elem_classes="infotext") |
|
html_log = gr.HTML(elem_id=f'html_log_{tabname}') |
|
|
|
paste_field_names = [] |
|
if tabname == "txt2img": |
|
paste_field_names = modules.scripts.scripts_txt2img.paste_field_names |
|
elif tabname == "img2img": |
|
paste_field_names = modules.scripts.scripts_img2img.paste_field_names |
|
|
|
for paste_tabname, paste_button in buttons.items(): |
|
parameters_copypaste.register_paste_params_button(parameters_copypaste.ParamBinding( |
|
paste_button=paste_button, tabname=paste_tabname, source_tabname="txt2img" if tabname == "txt2img" else None, source_image_component=result_gallery, |
|
paste_field_names=paste_field_names |
|
)) |
|
|
|
return result_gallery, generation_info if tabname != "extras" else html_info_x, html_info, html_log |
|
|
|
|
|
def create_refresh_button(refresh_component, refresh_method, refreshed_args, elem_id): |
|
refresh_components = refresh_component if isinstance(refresh_component, list) else [refresh_component] |
|
|
|
label = None |
|
for comp in refresh_components: |
|
label = getattr(comp, 'label', None) |
|
if label is not None: |
|
break |
|
|
|
def refresh(): |
|
refresh_method() |
|
args = refreshed_args() if callable(refreshed_args) else refreshed_args |
|
|
|
for k, v in args.items(): |
|
for comp in refresh_components: |
|
setattr(comp, k, v) |
|
|
|
return [gr.update(**(args or {})) for _ in refresh_components] if len(refresh_components) > 1 else gr.update(**(args or {})) |
|
|
|
refresh_button = ToolButton(value=refresh_symbol, elem_id=elem_id, tooltip=f"{label}: refresh" if label else "Refresh") |
|
refresh_button.click( |
|
fn=refresh, |
|
inputs=[], |
|
outputs=refresh_components |
|
) |
|
return refresh_button |
|
|
|
|
|
def setup_dialog(button_show, dialog, *, button_close=None): |
|
"""Sets up the UI so that the dialog (gr.Box) is invisible, and is only shown when buttons_show is clicked, in a fullscreen modal window.""" |
|
|
|
dialog.visible = False |
|
|
|
button_show.click( |
|
fn=lambda: gr.update(visible=True), |
|
inputs=[], |
|
outputs=[dialog], |
|
).then(fn=None, _js="function(){ popupId('" + dialog.elem_id + "'); }") |
|
|
|
if button_close: |
|
button_close.click(fn=None, _js="closePopup") |
|
|
|
|