diff --git a/javascript/ui.js b/javascript/ui.js index cfd0dcd..7e11646 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -45,14 +45,14 @@ function switch_to_txt2img(){ return args_to_array(arguments); } -function switch_to_img2img_img2img(){ +function switch_to_img2img(){ gradioApp().querySelector('#tabs').querySelectorAll('button')[1].click(); gradioApp().getElementById('mode_img2img').querySelectorAll('button')[0].click(); return args_to_array(arguments); } -function switch_to_img2img_inpaint(){ +function switch_to_inpaint(){ gradioApp().querySelector('#tabs').querySelectorAll('button')[1].click(); gradioApp().getElementById('mode_img2img').querySelectorAll('button')[1].click(); @@ -65,26 +65,6 @@ function switch_to_extras(){ return args_to_array(arguments); } -function extract_image_from_gallery_txt2img(gallery){ - switch_to_txt2img() - return extract_image_from_gallery(gallery); -} - -function extract_image_from_gallery_img2img(gallery){ - switch_to_img2img_img2img() - return extract_image_from_gallery(gallery); -} - -function extract_image_from_gallery_inpaint(gallery){ - switch_to_img2img_inpaint() - return extract_image_from_gallery(gallery); -} - -function extract_image_from_gallery_extras(gallery){ - switch_to_extras() - return extract_image_from_gallery(gallery); -} - function get_tab_index(tabId){ var res = 0 diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index f73647d..2b80737 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -3,13 +3,16 @@ import re import gradio as gr from modules.shared import script_path from modules import shared +import tempfile +from PIL import Image, PngImagePlugin re_param_code = r'\s*([\w ]+):\s*("(?:\\|\"|[^\"])+"|[^,]*)(?:,|$)' re_param = re.compile(re_param_code) re_params = re.compile(r"^(?:" + re_param_code + "){3,}$") re_imagesize = re.compile(r"^(\d+)x(\d+)$") type_of_gr_update = type(gr.update()) - +paste_fields = {} +bind_list = [] def quote(text): if ',' not in str(text): @@ -20,6 +23,81 @@ def quote(text): text = text.replace('"', '\\"') return f'"{text}"' +def image_from_url_text(filedata): + if type(filedata) == dict and filedata["is_file"]: + filename = filedata["name"] + tempdir = os.path.normpath(tempfile.gettempdir()) + normfn = os.path.normpath(filename) + assert normfn.startswith(tempdir), 'trying to open image file not in temporary directory' + + return Image.open(filename) + + if type(filedata) == list: + if len(filedata) == 0: + return None + + filedata = filedata[0] + + if filedata.startswith("data:image/png;base64,"): + filedata = filedata[len("data:image/png;base64,"):] + + filedata = base64.decodebytes(filedata.encode('utf-8')) + image = Image.open(io.BytesIO(filedata)) + return image + +def add_paste_fields(tabname, init_img, fields): + paste_fields[tabname] = {"init_img":init_img, "fields": fields} + +def create_buttons(tabs_list): + buttons = {} + for tab in tabs_list: + buttons[tab] = gr.Button(f"Send to {tab}") + return buttons + +#if send_generate_info is a tab name, mean generate_info comes from the params fields of the tab +def bind_buttons(buttons, send_image, send_generate_info): + bind_list.append([buttons, send_image, send_generate_info]) + +def run_bind(): + for buttons, send_image, send_generate_info in bind_list: + for tab in buttons: + button = buttons[tab] + if send_image and paste_fields[tab]["init_img"]: + if type(send_image) == gr.Gallery: + button.click( + fn=lambda x: image_from_url_text(x), + _js="extract_image_from_gallery", + inputs=[send_image], + outputs=[paste_fields[tab]["init_img"]], + ) + else: + button.click( + fn=lambda x:x, + inputs=[send_image], + outputs=[paste_fields[tab]["init_img"]], + ) + + if send_generate_info and paste_fields[tab]["fields"] is not None: + paste_field_names = ['Prompt', 'Negative prompt', 'Steps', 'Face restoration', 'Size-1', 'Size-2'] + if shared.opts.send_seed: + paste_field_names += ["Seed"] + if send_generate_info in paste_fields: + button.click( + fn=lambda *x:x, + inputs=[field for field,name in paste_fields[send_generate_info]["fields"] if name in paste_field_names], + outputs=[field for field,name in paste_fields[tab]["fields"] if name in paste_field_names], + ) + + else: + connect_paste(button, [(field, name) for field, name in paste_fields[tab]["fields"] if name in paste_field_names], send_generate_info) + + button.click( + fn=None, + _js=f"switch_to_{tab}", + inputs=None, + outputs=None, + ) + def parse_generation_parameters(x: str): """parses generation parameters string, the one you see in text field under the picture in UI: ``` @@ -67,8 +145,7 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model return res - -def connect_paste(button, paste_fields, input_comp, js=None): +def connect_paste(button, paste_fields, input_comp): def paste_func(prompt): if not prompt and not shared.cmd_opts.hide_ui_dir_config: filename = os.path.join(script_path, "params.txt") @@ -106,7 +183,8 @@ def connect_paste(button, paste_fields, input_comp, js=None): button.click( fn=paste_func, - _js=js, inputs=[input_comp], outputs=[x[0] for x in paste_fields], ) + + diff --git a/modules/shared.py b/modules/shared.py index f8b13b0..3ade2af 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -279,6 +279,7 @@ options_templates.update(options_section(('sd', "Stable Diffusion"), { "filter_nsfw": OptionInfo(False, "Filter NSFW content"), 'CLIP_stop_at_last_layers': OptionInfo(1, "Stop At last layers of CLIP model", gr.Slider, {"minimum": 1, "maximum": 12, "step": 1}), "random_artist_categories": OptionInfo([], "Allowed categories for random artists selection when using the Roll button", gr.CheckboxGroup, {"choices": artist_db.categories()}), + "send_seed": OptionInfo(False, "Send seed when sending prompt or image to other interface"), })) options_templates.update(options_section(('interrogate', "Interrogate Options"), { diff --git a/modules/ui.py b/modules/ui.py index 3e5b84d..ccba14b 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -35,7 +35,7 @@ if cmd_opts.deepdanbooru: from modules.deepbooru import get_deepbooru_tags import modules.codeformer_model -import modules.generation_parameters_copypaste +import modules.generation_parameters_copypaste as parameters_copypaste import modules.gfpgan_model import modules.hypernetworks.ui import modules.ldsr_model @@ -49,14 +49,11 @@ from modules.sd_hijack import model_hijack from modules.sd_samplers import samplers, samplers_for_img2img import modules.textual_inversion.ui import modules.hypernetworks.ui +from modules.generation_parameters_copypaste import image_from_url_text # this is a fix for Windows users. Without it, javascript files will be served with text/html content-type and the browser will not show any UI mimetypes.init() mimetypes.add_type('application/javascript', '.js') -txt2img_paste_fields = [] -img2img_paste_fields = [] -init_img_components = {} - if not cmd_opts.share and not cmd_opts.listen: # fix gradio phoning home @@ -99,37 +96,11 @@ def plaintext_to_html(text): text = "

" + "
\n".join([f"{html.escape(x)}" for x in text.split('\n')]) + "

" return text - -def image_from_url_text(filedata): - if type(filedata) == dict and filedata["is_file"]: - filename = filedata["name"] - tempdir = os.path.normpath(tempfile.gettempdir()) - normfn = os.path.normpath(filename) - assert normfn.startswith(tempdir), 'trying to open image file not in temporary directory' - - return Image.open(filename) - - if type(filedata) == list: - if len(filedata) == 0: - return None - - filedata = filedata[0] - - if filedata.startswith("data:image/png;base64,"): - filedata = filedata[len("data:image/png;base64,"):] - - filedata = base64.decodebytes(filedata.encode('utf-8')) - image = Image.open(io.BytesIO(filedata)) - return image - - def send_gradio_gallery_to_image(x): if len(x) == 0: return None - return image_from_url_text(x[0]) - def save_files(js_data, images, do_make_zip, index): import csv filenames = [] @@ -193,7 +164,6 @@ def save_files(js_data, images, do_make_zip, index): return gr.File.update(value=fullfns, visible=True), '', '', plaintext_to_html(f"Saved: {filenames[0]}") - def save_pil_to_file(pil_image, dir=None): use_metadata = False metadata = PngImagePlugin.PngInfo() @@ -626,6 +596,83 @@ def create_refresh_button(refresh_component, refresh_method, refreshed_args, ele ) return refresh_button +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]) + else: + sp.Popen(["xdg-open", path]) + + with gr.Column(variant='panel'): + with gr.Group(): + result_gallery = gr.Gallery(label='Output', show_label=False, elem_id=f"{tabname}_gallery").style(grid=4) + + generation_info = None + with gr.Column(): + with gr.Row(): + if tabname != "extras": + save = gr.Button('Save') + + buttons = parameters_copypaste.create_buttons(["img2img", "inpaint", "extras"]) + button_id = "hidden_element" if shared.cmd_opts.hide_ui_dir_config else 'open_folder' + open_folder = gr.Button(folder_symbol, elem_id=button_id) + + open_folder.click( + fn=lambda: open_folder(opts.outdir_samples or outdir), + inputs=[], + outputs=[], + ) + + if tabname != "extras": + with gr.Row(): + do_make_zip = gr.Checkbox(label="Make Zip when Save?", value=False) + + with gr.Row(): + download_files = gr.File(None, file_count="multiple", interactive=False, show_label=False, visible=False) + + with gr.Group(): + html_info = gr.HTML() + generation_info = gr.Textbox(visible=False) + + save.click( + fn=wrap_gradio_call(save_files), + _js="(x, y, z, w) => [x, y, z, selected_gallery_index()]", + inputs=[ + generation_info, + result_gallery, + do_make_zip, + html_info, + ], + outputs=[ + download_files, + html_info, + html_info, + html_info, + ] + ) + else: + html_info_x = gr.HTML() + html_info = gr.HTML() + parameters_copypaste.bind_buttons(buttons, result_gallery, "txt2img" if tabname == "txt2img" else None) + return result_gallery, generation_info if tabname != "extras" else html_info_x, html_info + + def create_ui(wrap_gradio_gpu_call): import modules.img2img @@ -676,31 +723,10 @@ def create_ui(wrap_gradio_gpu_call): with gr.Group(): custom_inputs = modules.scripts.scripts_txt2img.setup_ui(is_img2img=False) - with gr.Column(variant='panel'): - - with gr.Group(): - txt2img_preview = gr.Image(elem_id='txt2img_preview', visible=False) - txt2img_gallery = gr.Gallery(label='Output', show_label=False, elem_id='txt2img_gallery').style(grid=4) - - with gr.Column(): - with gr.Row(): - save = gr.Button('Save') - send_to_img2img = gr.Button('Send to img2img') - send_to_inpaint = gr.Button('Send to inpaint') - send_to_extras = gr.Button('Send to extras') - button_id = "hidden_element" if shared.cmd_opts.hide_ui_dir_config else 'open_folder' - open_txt2img_folder = gr.Button(folder_symbol, elem_id=button_id) - - with gr.Row(): - do_make_zip = gr.Checkbox(label="Make Zip when Save?", value=False) - - with gr.Row(): - download_files = gr.File(None, file_count="multiple", interactive=False, show_label=False, visible=False) - - with gr.Group(): - html_info = gr.HTML() - generation_info = gr.Textbox(visible=False) + + txt2img_gallery, generation_info, html_info = create_output_panel("txt2img", opts.outdir_txt2img_samples) + connect_reuse_seed(seed, reuse_seed, generation_info, dummy_component, is_subseed=False) connect_reuse_seed(subseed, reuse_subseed, generation_info, dummy_component, is_subseed=True) @@ -755,24 +781,7 @@ def create_ui(wrap_gradio_gpu_call): fn=lambda x: gr_show(x), inputs=[enable_hr], outputs=[hr_options], - ) - - save.click( - fn=wrap_gradio_call(save_files), - _js="(x, y, z, w) => [x, y, z, selected_gallery_index()]", - inputs=[ - generation_info, - txt2img_gallery, - do_make_zip, - html_info, - ], - outputs=[ - download_files, - html_info, - html_info, - html_info, - ] - ) + ) roll.click( fn=roll_artist, @@ -785,8 +794,7 @@ def create_ui(wrap_gradio_gpu_call): ] ) - global txt2img_paste_fields - txt2img_paste_fields = [ + parameters_copypaste.add_paste_fields("txt2img", None, [ (txt2img_prompt, "Prompt"), (txt2img_negative_prompt, "Negative prompt"), (steps, "Steps"), @@ -807,7 +815,7 @@ def create_ui(wrap_gradio_gpu_call): (firstphase_width, "First pass size-1"), (firstphase_height, "First pass size-2"), *modules.scripts.scripts_txt2img.infotext_fields - ] + ]) txt2img_preview_params = [ txt2img_prompt, @@ -894,30 +902,7 @@ def create_ui(wrap_gradio_gpu_call): with gr.Group(): custom_inputs = modules.scripts.scripts_img2img.setup_ui(is_img2img=True) - with gr.Column(variant='panel'): - - with gr.Group(): - img2img_preview = gr.Image(elem_id='img2img_preview', visible=False) - img2img_gallery = gr.Gallery(label='Output', show_label=False, elem_id='img2img_gallery').style(grid=4) - - with gr.Column(): - with gr.Row(): - save = gr.Button('Save') - img2img_send_to_img2img = gr.Button('Send to img2img') - img2img_send_to_inpaint = gr.Button('Send to inpaint') - img2img_send_to_extras = gr.Button('Send to extras') - button_id = "hidden_element" if shared.cmd_opts.hide_ui_dir_config else 'open_folder' - open_img2img_folder = gr.Button(folder_symbol, elem_id=button_id) - - with gr.Row(): - do_make_zip = gr.Checkbox(label="Make Zip when Save?", value=False) - - with gr.Row(): - download_files = gr.File(None, file_count="multiple", interactive=False, show_label=False, visible=False) - - with gr.Group(): - html_info = gr.HTML() - generation_info = gr.Textbox(visible=False) + img2img_gallery, generation_info, html_info = create_output_panel("img2img", opts.outdir_img2img_samples) connect_reuse_seed(seed, reuse_seed, generation_info, dummy_component, is_subseed=False) connect_reuse_seed(subseed, reuse_subseed, generation_info, dummy_component, is_subseed=True) @@ -1004,24 +989,8 @@ def create_ui(wrap_gradio_gpu_call): fn=interrogate_deepbooru, inputs=[init_img], outputs=[img2img_prompt], - ) - - save.click( - fn=wrap_gradio_call(save_files), - _js="(x, y, z, w) => [x, y, z, selected_gallery_index()]", - inputs=[ - generation_info, - img2img_gallery, - do_make_zip, - html_info, - ], - outputs=[ - download_files, - html_info, - html_info, - html_info, - ] ) + roll.click( fn=roll_artist, @@ -1056,7 +1025,8 @@ def create_ui(wrap_gradio_gpu_call): outputs=[prompt, negative_prompt, style1, style2], ) - global img2img_paste_fields + token_button.click(fn=update_token_counter, inputs=[img2img_prompt, steps], outputs=[token_counter]) + img2img_paste_fields = [ (img2img_prompt, "Prompt"), (img2img_negative_prompt, "Negative prompt"), @@ -1075,7 +1045,9 @@ def create_ui(wrap_gradio_gpu_call): (denoising_strength, "Denoising strength"), *modules.scripts.scripts_img2img.infotext_fields ] - token_button.click(fn=update_token_counter, inputs=[img2img_prompt, steps], outputs=[token_counter]) + parameters_copypaste.add_paste_fields("img2img", init_img, img2img_paste_fields) + parameters_copypaste.add_paste_fields("inpaint", init_img_with_mask, img2img_paste_fields) + with gr.Blocks(analytics_enabled=False) as extras_interface: with gr.Row().style(equal_height=False): @@ -1122,15 +1094,8 @@ def create_ui(wrap_gradio_gpu_call): submit = gr.Button('Generate', elem_id="extras_generate", variant='primary') - with gr.Column(variant='panel'): - result_images = gr.Gallery(label="Result", show_label=False) - html_info_x = gr.HTML() - html_info = gr.HTML() - extras_send_to_img2img = gr.Button('Send to img2img') - extras_send_to_inpaint = gr.Button('Send to inpaint') - button_id = "hidden_element" if shared.cmd_opts.hide_ui_dir_config else '' - open_extras_folder = gr.Button('Open output directory', elem_id=button_id) + result_images, html_info_x, html_info = create_output_panel("extras", opts.outdir_extras_samples) submit.click( fn=wrap_gradio_gpu_call(modules.extras.run_extras), @@ -1160,23 +1125,8 @@ def create_ui(wrap_gradio_gpu_call): html_info, ] ) + parameters_copypaste.add_paste_fields("extras", extras_image, None) - extras_send_to_img2img.click( - fn=lambda x: image_from_url_text(x), - _js="extract_image_from_gallery_img2img", - inputs=[result_images], - outputs=[init_img], - ) - - extras_send_to_inpaint.click( - fn=lambda x: image_from_url_text(x), - _js="extract_image_from_gallery_inpaint", - inputs=[result_images], - outputs=[init_img_with_mask], - ) - - global init_img_components - init_img_components = {"img2img":init_img, "inpaint":init_img_with_mask, "extras":extras_image} with gr.Blocks(analytics_enabled=False) as pnginfo_interface: with gr.Row().style(equal_height=False): @@ -1187,11 +1137,10 @@ def create_ui(wrap_gradio_gpu_call): html = gr.HTML() generation_info = gr.Textbox(visible=False) html2 = gr.HTML() - with gr.Row(): - pnginfo_send_to_txt2img = gr.Button('Send to txt2img') - pnginfo_send_to_img2img = gr.Button('Send to img2img') - + buttons = parameters_copypaste.create_buttons(["txt2img", "img2img", "inpaint", "extras"]) + parameters_copypaste.bind_buttons(buttons, image, generation_info) + image.change( fn=wrap_gradio_call(modules.extras.run_pnginfo), inputs=[image], @@ -1475,28 +1424,6 @@ def create_ui(wrap_gradio_gpu_call): script_callbacks.ui_settings_callback() opts.reorder() - 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]) - else: - sp.Popen(["xdg-open", path]) - def run_settings(*args): changed = 0 @@ -1641,6 +1568,8 @@ Requested path was: {f} if column is not None: column.__exit__() + parameters_copypaste.run_bind() + interfaces = [ (txt2img_interface, "txt2img", "txt2img"), (img2img_interface, "img2img", "img2img"), @@ -1731,85 +1660,14 @@ Requested path was: {f} component_dict['sd_model_checkpoint'], ] ) - paste_field_names = ['Prompt', 'Negative prompt', 'Steps', 'Face restoration', 'Seed', 'Size-1', 'Size-2'] - txt2img_fields = [field for field,name in txt2img_paste_fields if name in paste_field_names] - img2img_fields = [field for field,name in img2img_paste_fields if name in paste_field_names] - send_to_img2img.click( - fn=lambda img, *args: (image_from_url_text(img),*args), - _js="(gallery, ...args) => [extract_image_from_gallery_img2img(gallery), ...args]", - inputs=[txt2img_gallery] + txt2img_fields, - outputs=[init_img] + img2img_fields, - ) - - send_to_inpaint.click( - fn=lambda x, *args: (image_from_url_text(x), *args), - _js="(gallery, ...args) => [extract_image_from_gallery_inpaint(gallery), ...args]", - inputs=[txt2img_gallery] + txt2img_fields, - outputs=[init_img_with_mask] + img2img_fields, - ) - - img2img_send_to_img2img.click( - fn=lambda x: image_from_url_text(x), - _js="extract_image_from_gallery_img2img", - inputs=[img2img_gallery], - outputs=[init_img], - ) - - img2img_send_to_inpaint.click( - fn=lambda x: image_from_url_text(x), - _js="extract_image_from_gallery_inpaint", - inputs=[img2img_gallery], - outputs=[init_img_with_mask], - ) - - send_to_extras.click( - fn=lambda x: image_from_url_text(x), - _js="extract_image_from_gallery_extras", - inputs=[txt2img_gallery], - outputs=[extras_image], - ) - - open_txt2img_folder.click( - fn=lambda: open_folder(opts.outdir_samples or opts.outdir_txt2img_samples), - inputs=[], - outputs=[], - ) - - open_img2img_folder.click( - fn=lambda: open_folder(opts.outdir_samples or opts.outdir_img2img_samples), - inputs=[], - outputs=[], - ) - - open_extras_folder.click( - fn=lambda: open_folder(opts.outdir_samples or opts.outdir_extras_samples), - inputs=[], - outputs=[], - ) - - img2img_send_to_extras.click( - fn=lambda x: image_from_url_text(x), - _js="extract_image_from_gallery_extras", - inputs=[img2img_gallery], - outputs=[extras_image], - ) + settings_map = { 'sd_hypernetwork': 'Hypernet', 'CLIP_stop_at_last_layers': 'Clip skip', 'sd_model_checkpoint': 'Model hash', } - - settings_paste_fields = [ - (component_dict[k], lambda d, k=k, v=v: apply_setting(k, d.get(v, None))) - for k, v in settings_map.items() - ] - - modules.generation_parameters_copypaste.connect_paste(txt2img_paste, txt2img_paste_fields + settings_paste_fields, txt2img_prompt) - modules.generation_parameters_copypaste.connect_paste(img2img_paste, img2img_paste_fields + settings_paste_fields, img2img_prompt) - - modules.generation_parameters_copypaste.connect_paste(pnginfo_send_to_txt2img, txt2img_paste_fields + settings_paste_fields, generation_info, 'switch_to_txt2img') - modules.generation_parameters_copypaste.connect_paste(pnginfo_send_to_img2img, img2img_paste_fields + settings_paste_fields, generation_info, 'switch_to_img2img_img2img') + ui_config_file = cmd_opts.ui_config_file ui_settings = {} diff --git a/webui-user.bat b/webui-user.bat index e5a257b..fcd0f1f 100644 --- a/webui-user.bat +++ b/webui-user.bat @@ -3,6 +3,6 @@ set PYTHON= set GIT= set VENV_DIR= -set COMMANDLINE_ARGS= +set COMMANDLINE_ARGS=--ui-debug-mode call webui.bat