From f40617d6c4e366773677baa8d7f4114ba2893282 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 3 Sep 2022 17:21:15 +0300 Subject: [PATCH] support for scripts --- modules/img2img.py | 11 ++++-- modules/processing.py | 55 ++++++++-------------------- modules/scripts.py | 82 ++++++++++++++++++++++++++++++++++++------ modules/sd_samplers.py | 32 +++++++++++------ modules/shared.py | 1 - modules/txt2img.py | 27 +++----------- modules/ui.py | 64 ++++++++++++++------------------- script.js | 27 ++++++++++++-- style.css | 12 ++++--- webui.py | 26 +++----------- 10 files changed, 183 insertions(+), 154 deletions(-) diff --git a/modules/img2img.py b/modules/img2img.py index f2817ba83..f14226f7b 100644 --- a/modules/img2img.py +++ b/modules/img2img.py @@ -7,8 +7,9 @@ import modules.shared as shared import modules.processing as processing from modules.ui import plaintext_to_html import modules.images as images +import modules.scripts -def img2img(prompt: str, init_img, init_img_with_mask, steps: int, sampler_index: int, mask_blur: int, inpainting_fill: int, use_GFPGAN: bool, prompt_matrix, mode: int, n_iter: int, batch_size: int, cfg_scale: float, denoising_strength: float, seed: int, height: int, width: int, resize_mode: int, upscaler_name: str, upscale_overlap: int, inpaint_full_res: bool): +def img2img(prompt: str, init_img, init_img_with_mask, steps: int, sampler_index: int, mask_blur: int, inpainting_fill: int, use_GFPGAN: bool, mode: int, n_iter: int, batch_size: int, cfg_scale: float, denoising_strength: float, seed: int, height: int, width: int, resize_mode: int, upscaler_name: str, upscale_overlap: int, inpaint_full_res: bool, *args): is_inpaint = mode == 1 is_loopback = mode == 2 is_upscale = mode == 3 @@ -35,7 +36,6 @@ def img2img(prompt: str, init_img, init_img_with_mask, steps: int, sampler_index cfg_scale=cfg_scale, width=width, height=height, - prompt_matrix=prompt_matrix, use_GFPGAN=use_GFPGAN, init_images=[image], mask=mask, @@ -128,6 +128,11 @@ def img2img(prompt: str, init_img, init_img_with_mask, steps: int, sampler_index processed = Processed(p, [combined_image], initial_seed, initial_info) else: - processed = process_images(p) + + processed = modules.scripts.run(p, *args) + + if processed is None: + processed = process_images(p) + return processed.images, processed.js(), plaintext_to_html(processed.info) diff --git a/modules/processing.py b/modules/processing.py index faf56c9c0..cab79b4c6 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -28,11 +28,12 @@ def torch_gc(): class StableDiffusionProcessing: - def __init__(self, sd_model=None, outpath_samples=None, outpath_grids=None, prompt="", seed=-1, sampler_index=0, batch_size=1, n_iter=1, steps=50, cfg_scale=7.0, width=512, height=512, prompt_matrix=False, use_GFPGAN=False, do_not_save_samples=False, do_not_save_grid=False, extra_generation_params=None, overlay_images=None, negative_prompt=None): + def __init__(self, sd_model=None, outpath_samples=None, outpath_grids=None, prompt="", seed=-1, sampler_index=0, batch_size=1, n_iter=1, steps=50, cfg_scale=7.0, width=512, height=512, use_GFPGAN=False, do_not_save_samples=False, do_not_save_grid=False, extra_generation_params=None, overlay_images=None, negative_prompt=None): self.sd_model = sd_model self.outpath_samples: str = outpath_samples self.outpath_grids: str = outpath_grids self.prompt: str = prompt + self.prompt_for_display: str = None self.negative_prompt: str = (negative_prompt or "") self.seed: int = seed self.sampler_index: int = sampler_index @@ -42,7 +43,6 @@ class StableDiffusionProcessing: self.cfg_scale: float = cfg_scale self.width: int = width self.height: int = height - self.prompt_matrix: bool = prompt_matrix self.use_GFPGAN: bool = use_GFPGAN self.do_not_save_samples: bool = do_not_save_samples self.do_not_save_grid: bool = do_not_save_grid @@ -71,8 +71,8 @@ class Processed: def js(self): obj = { - "prompt": self.prompt, - "seed": int(self.seed), + "prompt": self.prompt if type(self.prompt) != list else self.prompt[0], + "seed": int(self.seed if type(self.seed) != list else self.seed[0]), "width": self.width, "height": self.height, "sampler": self.sampler, @@ -105,35 +105,22 @@ def process_images(p: StableDiffusionProcessing) -> Processed: assert p.prompt is not None torch_gc() - seed = int(random.randrange(4294967294) if p.seed == -1 else p.seed) + seed = int(random.randrange(4294967294)) if p.seed == -1 else p.seed os.makedirs(p.outpath_samples, exist_ok=True) os.makedirs(p.outpath_grids, exist_ok=True) comments = [] - prompt_matrix_parts = [] - if p.prompt_matrix: - all_prompts = [] - prompt_matrix_parts = prompt.split("|") - combination_count = 2 ** (len(prompt_matrix_parts) - 1) - for combination_num in range(combination_count): - selected_prompts = [text.strip().strip(',') for n, text in enumerate(prompt_matrix_parts[1:]) if combination_num & (1 << n)] - - if opts.prompt_matrix_add_to_start: - selected_prompts = selected_prompts + [prompt_matrix_parts[0]] - else: - selected_prompts = [prompt_matrix_parts[0]] + selected_prompts - - all_prompts.append(", ".join(selected_prompts)) - - p.n_iter = math.ceil(len(all_prompts) / p.batch_size) - all_seeds = len(all_prompts) * [seed] - - print(f"Prompt matrix will create {len(all_prompts)} images using a total of {p.n_iter} batches.") + if type(prompt) == list: + all_prompts = prompt else: all_prompts = p.batch_size * p.n_iter * [prompt] - all_seeds = [seed + x for x in range(len(all_prompts))] + + if type(seed) == list: + all_seeds = seed + else: + all_seeds = [int(seed + x) for x in range(len(all_prompts))] def infotext(iteration=0, position_in_batch=0): generation_params = { @@ -149,7 +136,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed: generation_params_text = ", ".join([k if k == v else f'{k}: {v}' for k, v in generation_params.items() if v is not None]) - return f"{prompt}\n{generation_params_text}".strip() + "".join(["\n\n" + x for x in comments]) + return f"{p.prompt_for_display or prompt}\n{generation_params_text}".strip() + "".join(["\n\n" + x for x in comments]) if os.path.exists(cmd_opts.embeddings_dir): model_hijack.load_textual_inversion_embeddings(cmd_opts.embeddings_dir, p.sd_model) @@ -218,25 +205,13 @@ def process_images(p: StableDiffusionProcessing) -> Processed: if not p.do_not_save_grid and not unwanted_grid_because_of_img_count: return_grid = opts.return_grid - if p.prompt_matrix: - grid = images.image_grid(output_images, p.batch_size, rows=1 << ((len(prompt_matrix_parts)-1)//2)) - - try: - grid = images.draw_prompt_matrix(grid, p.width, p.height, prompt_matrix_parts) - except Exception: - import traceback - print("Error creating prompt_matrix text:", file=sys.stderr) - print(traceback.format_exc(), file=sys.stderr) - - return_grid = True - else: - grid = images.image_grid(output_images, p.batch_size) + grid = images.image_grid(output_images, p.batch_size) if return_grid: output_images.insert(0, grid) if opts.grid_save: - images.save_image(grid, p.outpath_grids, "grid", seed, prompt, opts.grid_format, info=infotext(), short_filename=not opts.grid_extended_filename) + images.save_image(grid, p.outpath_grids, "grid", seed, all_prompts[0], opts.grid_format, info=infotext(), short_filename=not opts.grid_extended_filename) torch_gc() return Processed(p, output_images, seed, infotext()) diff --git a/modules/scripts.py b/modules/scripts.py index 20f489ce9..be348a704 100644 --- a/modules/scripts.py +++ b/modules/scripts.py @@ -2,19 +2,33 @@ import os import sys import traceback +import modules.ui as ui import gradio as gr +from modules.processing import StableDiffusionProcessing + class Script: filename = None + args_from = None + args_to = None def title(self): raise NotImplementedError() + def ui(self, is_img2img): + pass + + def run(self, *args): + raise NotImplementedError() + + def describe(self): + return "" + scripts = [] -def load_scripts(basedir, globs): +def load_scripts(basedir): for filename in os.listdir(basedir): path = os.path.join(basedir, filename) @@ -27,27 +41,73 @@ def load_scripts(basedir, globs): from types import ModuleType compiled = compile(text, path, 'exec') module = ModuleType(filename) - module.__dict__.update(globs) exec(compiled, module.__dict__) - for key, item in module.__dict__.items(): - if type(item) == type and issubclass(item, Script): - item.filename = path + for key, script_class in module.__dict__.items(): + if type(script_class) == type and issubclass(script_class, Script): + obj = script_class() + obj.filename = path - scripts.append(item) + scripts.append(obj) def wrap_call(func, filename, funcname, *args, default=None, **kwargs): try: - res = func() + res = func(*args, **kwargs) return res except Exception: - print(f"Error calling: {filename/funcname}", file=sys.stderr) + print(f"Error calling: {filename}/{funcname}", file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) return default -def setup_ui(): - titles = [wrap_call(script.title, script.filename, "title") for script in scripts] - gr.Dropdown(options=[""] + titles, value="", type="index") +def setup_ui(is_img2img): + titles = [wrap_call(script.title, script.filename, "title") or f"{script.filename} [error]" for script in scripts] + + dropdown = gr.Dropdown(label="Script", choices=["None"] + titles, value="None", type="index") + + inputs = [dropdown] + + for script in scripts: + script.args_from = len(inputs) + controls = script.ui(is_img2img) + + for control in controls: + control.visible = False + + inputs += controls + script.args_to = len(inputs) + + def select_script(index): + if index > 0: + script = scripts[index-1] + args_from = script.args_from + args_to = script.args_to + else: + args_from = 0 + args_to = 0 + + return [ui.gr_show(True if i == 0 else args_from <= i < args_to) for i in range(len(inputs))] + + dropdown.change( + fn=select_script, + inputs=[dropdown], + outputs=inputs + ) + + return inputs + + +def run(p: StableDiffusionProcessing, *args): + script_index = args[0] - 1 + + if script_index < 0 or script_index >= len(scripts): + return None + + script = scripts[script_index] + + script_args = args[script.args_from:script.args_to] + processed = script.run(p, *script_args) + + return processed diff --git a/modules/sd_samplers.py b/modules/sd_samplers.py index 54c5fd7c0..6f028f5f6 100644 --- a/modules/sd_samplers.py +++ b/modules/sd_samplers.py @@ -9,18 +9,28 @@ from ldm.models.diffusion.plms import PLMSSampler from modules.shared import opts, cmd_opts, state import modules.shared as shared -SamplerData = namedtuple('SamplerData', ['name', 'constructor']) + +SamplerData = namedtuple('SamplerData', ['name', 'constructor', 'aliases']) + +samplers_k_diffusion = [ + ('Euler a', 'sample_euler_ancestral', ['k_euler_a']), + ('Euler', 'sample_euler', ['k_euler']), + ('LMS', 'sample_lms', ['k_lms']), + ('Heun', 'sample_heun', ['k_heun']), + ('DPM2', 'sample_dpm_2', ['k_dpm_2']), + ('DPM2 a', 'sample_dpm_2_ancestral', ['k_dpm_2_a']), +] + +samplers_data_k_diffusion = [ + SamplerData(label, lambda model, funcname=funcname: KDiffusionSampler(funcname, model), aliases) + for label, funcname, aliases in samplers_k_diffusion + if hasattr(k_diffusion.sampling, funcname) +] + samplers = [ - *[SamplerData(x[0], lambda model, funcname=x[1]: KDiffusionSampler(funcname, model)) for x in [ - ('Euler a', 'sample_euler_ancestral'), - ('Euler', 'sample_euler'), - ('LMS', 'sample_lms'), - ('Heun', 'sample_heun'), - ('DPM2', 'sample_dpm_2'), - ('DPM2 a', 'sample_dpm_2_ancestral'), - ] if hasattr(k_diffusion.sampling, x[1])], - SamplerData('DDIM', lambda model: VanillaStableDiffusionSampler(DDIMSample, model)), - SamplerData('PLMS', lambda model: VanillaStableDiffusionSampler(PLMSSampler, model)), + *samplers_data_k_diffusion, + SamplerData('DDIM', lambda model: VanillaStableDiffusionSampler(DDIMSampler, model), []), + SamplerData('PLMS', lambda model: VanillaStableDiffusionSampler(PLMSSampler, model), []), ] samplers_for_img2img = [x for x in samplers if x.name != 'PLMS'] diff --git a/modules/shared.py b/modules/shared.py index 33a085595..b02c9e36b 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -76,7 +76,6 @@ class Options: "export_for_4chan": OptionInfo(True, "If PNG image is larger than 4MB or any dimension is larger than 4000, downscale and save copy as JPG"), "enable_pnginfo": OptionInfo(True, "Save text information about generation parameters as chunks to png files"), "font": OptionInfo("arial.ttf", "Font for image grids that have text"), - "prompt_matrix_add_to_start": OptionInfo(True, "In prompt matrix, add the variable combination of text to the start of the prompt, rather than the end"), "enable_emphasis": OptionInfo(True, "Use (text) to make model pay more attention to text text and [text] to make it pay less attention"), "save_txt": OptionInfo(False, "Create a text file next to every image with generation parameters."), diff --git a/modules/txt2img.py b/modules/txt2img.py index d03a29f29..f5ac05407 100644 --- a/modules/txt2img.py +++ b/modules/txt2img.py @@ -1,4 +1,4 @@ - +import modules.scripts from modules.processing import StableDiffusionProcessing, Processed, StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img, process_images from modules.shared import opts, cmd_opts import modules.shared as shared @@ -6,7 +6,7 @@ import modules.processing as processing from modules.ui import plaintext_to_html -def txt2img(prompt: str, negative_prompt: str, steps: int, sampler_index: int, use_GFPGAN: bool, prompt_matrix: bool, n_iter: int, batch_size: int, cfg_scale: float, seed: int, height: int, width: int, code: str): +def txt2img(prompt: str, negative_prompt: str, steps: int, sampler_index: int, use_GFPGAN: bool, n_iter: int, batch_size: int, cfg_scale: float, seed: int, height: int, width: int, *args): p = StableDiffusionProcessingTxt2Img( sd_model=shared.sd_model, outpath_samples=opts.outdir_samples or opts.outdir_txt2img_samples, @@ -21,30 +21,13 @@ def txt2img(prompt: str, negative_prompt: str, steps: int, sampler_index: int, u cfg_scale=cfg_scale, width=width, height=height, - prompt_matrix=prompt_matrix, use_GFPGAN=use_GFPGAN ) - if code != '' and cmd_opts.allow_code: - p.do_not_save_grid = True - p.do_not_save_samples = True + processed = modules.scripts.run(p, *args) - display_result_data = [[], -1, ""] - - def display(imgs, s=display_result_data[1], i=display_result_data[2]): - display_result_data[0] = imgs - display_result_data[1] = s - display_result_data[2] = i - - from types import ModuleType - compiled = compile(code, '', 'exec') - module = ModuleType("testmodule") - module.__dict__.update(globals()) - module.p = p - module.display = display - exec(compiled, module.__dict__) - - processed = Processed(p, *display_result_data) + if processed is not None: + pass else: processed = process_images(p) diff --git a/modules/ui.py b/modules/ui.py index 5223179f4..c6ded6786 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -12,6 +12,7 @@ from PIL import Image import gradio as gr import gradio.utils +import gradio.routes from modules.paths import script_path from modules.shared import opts, cmd_opts @@ -19,6 +20,7 @@ import modules.shared as shared from modules.sd_samplers import samplers, samplers_for_img2img import modules.gfpgan_model as gfpgan import modules.realesrgan_model as realesrgan +import modules.scripts # this is a fix for Windows users. Without it, javascript files will be served with text/html content-type and the bowser will not show any UI mimetypes.init() @@ -131,7 +133,7 @@ def wrap_gradio_call(func): return f -def create_ui(opts, cmd_opts, txt2img, img2img, run_extras, run_pnginfo): +def create_ui(txt2img, img2img, run_extras, run_pnginfo): with gr.Blocks(analytics_enabled=False) as txt2img_interface: with gr.Row(): @@ -145,8 +147,7 @@ def create_ui(opts, cmd_opts, txt2img, img2img, run_extras, run_pnginfo): sampler_index = gr.Radio(label='Sampling method', elem_id="txt2img_sampling", choices=[x.name for x in samplers], value=samplers[0].name, type="index") with gr.Row(): - use_GFPGAN = gr.Checkbox(label='GFPGAN', value=False, visible=gfpgan.have_gfpgan) - prompt_matrix = gr.Checkbox(label='Prompt matrix', value=False) + use_gfpgan = gr.Checkbox(label='GFPGAN', value=False, visible=gfpgan.have_gfpgan) with gr.Row(): batch_count = gr.Slider(minimum=1, maximum=cmd_opts.max_batch_count, step=1, label='Batch count', value=1) @@ -160,7 +161,8 @@ def create_ui(opts, cmd_opts, txt2img, img2img, run_extras, run_pnginfo): seed = gr.Number(label='Seed', value=-1) - code = gr.Textbox(label="Python script", visible=cmd_opts.allow_code, lines=1) + with gr.Group(): + custom_inputs = modules.scripts.setup_ui(is_img2img=False) with gr.Column(variant='panel'): with gr.Group(): @@ -185,16 +187,14 @@ def create_ui(opts, cmd_opts, txt2img, img2img, run_extras, run_pnginfo): negative_prompt, steps, sampler_index, - use_GFPGAN, - prompt_matrix, + use_gfpgan, batch_count, batch_size, cfg_scale, seed, height, width, - code - ], + ] + custom_inputs, outputs=[ txt2img_gallery, generation_info, @@ -244,8 +244,7 @@ def create_ui(opts, cmd_opts, txt2img, img2img, run_extras, run_pnginfo): inpainting_fill = gr.Radio(label='Msked content', choices=['fill', 'original', 'latent noise', 'latent nothing'], value='fill', type="index", visible=False) with gr.Row(): - use_GFPGAN = gr.Checkbox(label='GFPGAN', value=False, visible=gfpgan.have_gfpgan) - prompt_matrix = gr.Checkbox(label='Prompt matrix', value=False) + use_gfpgan = gr.Checkbox(label='GFPGAN', value=False, visible=gfpgan.have_gfpgan) inpaint_full_res = gr.Checkbox(label='Inpaint at full resolution', value=True, visible=False) with gr.Row(): @@ -266,6 +265,10 @@ def create_ui(opts, cmd_opts, txt2img, img2img, run_extras, run_pnginfo): seed = gr.Number(label='Seed', value=-1) + with gr.Group(): + custom_inputs = modules.scripts.setup_ui(is_img2img=False) + + with gr.Column(variant='panel'): with gr.Group(): img2img_gallery = gr.Gallery(label='Output', elem_id='img2img_gallery') @@ -291,11 +294,10 @@ def create_ui(opts, cmd_opts, txt2img, img2img, run_extras, run_pnginfo): init_img_with_mask: gr_show(is_inpaint), mask_blur: gr_show(is_inpaint), inpainting_fill: gr_show(is_inpaint), - prompt_matrix: gr_show(is_classic), batch_count: gr_show(not is_upscale), batch_size: gr_show(not is_loopback), sd_upscale_upscaler_name: gr_show(is_upscale), - sd_upscale_overlap:gr_show(is_upscale), + sd_upscale_overlap: gr_show(is_upscale), inpaint_full_res: gr_show(is_inpaint), } @@ -307,7 +309,6 @@ def create_ui(opts, cmd_opts, txt2img, img2img, run_extras, run_pnginfo): init_img_with_mask, mask_blur, inpainting_fill, - prompt_matrix, batch_count, batch_size, sd_upscale_upscaler_name, @@ -326,8 +327,7 @@ def create_ui(opts, cmd_opts, txt2img, img2img, run_extras, run_pnginfo): sampler_index, mask_blur, inpainting_fill, - use_GFPGAN, - prompt_matrix, + use_gfpgan, switch_mode, batch_count, batch_size, @@ -340,7 +340,7 @@ def create_ui(opts, cmd_opts, txt2img, img2img, run_extras, run_pnginfo): sd_upscale_upscaler_name, sd_upscale_overlap, inpaint_full_res, - ], + ] + custom_inputs, outputs=[ img2img_gallery, generation_info, @@ -384,9 +384,6 @@ def create_ui(opts, cmd_opts, txt2img, img2img, run_extras, run_pnginfo): outputs=[init_img_with_mask], ) - - - with gr.Blocks(analytics_enabled=False) as extras_interface: with gr.Row().style(equal_height=False): with gr.Column(variant='panel'): @@ -420,7 +417,6 @@ def create_ui(opts, cmd_opts, txt2img, img2img, run_extras, run_pnginfo): submit.click(**extras_args) - send_to_extras.click( fn=lambda x: image_from_url_text(x), _js="extract_image_from_gallery", @@ -435,7 +431,6 @@ def create_ui(opts, cmd_opts, txt2img, img2img, run_extras, run_pnginfo): outputs=[image], ) - pnginfo_interface = gr.Interface( wrap_gradio_call(run_pnginfo), inputs=[ @@ -450,7 +445,6 @@ def create_ui(opts, cmd_opts, txt2img, img2img, run_extras, run_pnginfo): analytics_enabled=False, ) - def create_setting_component(key): def fun(): return opts.data[key] if key in opts.data else opts.data_labels[key].default @@ -520,20 +514,16 @@ def create_ui(opts, cmd_opts, txt2img, img2img, run_extras, run_pnginfo): return demo -with open(os.path.join(script_path, "script.js"), "r", encoding="utf8") as file: - javascript = file.read() - -def inject_gradio_html(javascript): - import gradio.routes - - def template_response(*args, **kwargs): - res = gradio_routes_templates_response(*args, **kwargs) - res.body = res.body.replace(b'', f''.encode("utf8")) - res.init_headers() - return res - - gradio_routes_templates_response = gradio.routes.templates.TemplateResponse - gradio.routes.templates.TemplateResponse = template_response +with open(os.path.join(script_path, "script.js"), "r", encoding="utf8") as jsfile: + javascript = jsfile.read() -inject_gradio_html(javascript) +def template_response(*args, **kwargs): + res = gradio_routes_templates_response(*args, **kwargs) + res.body = res.body.replace(b'', f''.encode("utf8")) + res.init_headers() + return res + + +gradio_routes_templates_response = gradio.routes.templates.TemplateResponse +gradio.routes.templates.TemplateResponse = template_response diff --git a/script.js b/script.js index 5d38b3342..7ead367e8 100644 --- a/script.js +++ b/script.js @@ -4,7 +4,7 @@ titles = { "GFPGAN": "Restore low quality faces using GFPGAN neural network", "Euler a": "Euler Ancestral - very creative, each can get acompletely different pictures depending on step count, setting seps tohigher than 30-40 does not help", "DDIM": "Denoising Diffusion Implicit Models - best at inpainting", - "Prompt matrix": "Separate prompts into part using vertical pipe character (|) and the script will create a picture for every combination of them (except for first part, which will be present in all combinations)", + "Batch count": "How many batches of images to create", "Batch size": "How many image to create in a single batch", "CFG Scale": "Classifier Free Guidance Scale - how strongly the image should conform to prompt - lower values produce more creative results", @@ -30,6 +30,16 @@ titles = { "Interrupt": "Stop processing images and return any results accumulated so far.", "Save": "Write image to a directory (default - log/images) and generation parameters into csv file.", + + "X values": "Separate values for X axis using commas.", + "Y values": "Separate values for Y axis using commas.", + + "None": "Do not do anything special", + "Prompt matrix": "Separate prompts into parts using vertical pipe character (|) and the script will create a picture for every combination of them (except for the first part, which will be present in all combinations)", + "X/Y Plot": "Create a grid where images will have different parameters. Use inputs below to specify which parameterswill be shared by columns and rows", + "Custom code": "Run python code. Advanced user only. Must run program with --allow-code for this to work", + + "Prompt S/R": "Separate a list of words with commas, and the first word will be used as a keyword: script will search for this word in the prompt, and replace it with others", } function gradioApp(){ @@ -37,12 +47,25 @@ function gradioApp(){ } function addTitles(root){ - root.querySelectorAll('span, button').forEach(function(span){ + root.querySelectorAll('span, button, select').forEach(function(span){ tooltip = titles[span.textContent]; + + if(!tooltip){ + tooltip = titles[span.value]; + } + if(tooltip){ span.title = tooltip; } }) + + root.querySelectorAll('select').forEach(function(select){ + if (select.onchange != null) return; + + select.onchange = function(){ + select.title = titles[select.value] || ""; + } + }) } document.addEventListener("DOMContentLoaded", function() { diff --git a/style.css b/style.css index f43d51622..81a0f492b 100644 --- a/style.css +++ b/style.css @@ -22,22 +22,24 @@ button{ overflow: visible !important; } -fieldset span.text-gray-500, .gr-panel div.flex-col div.justify-between label, label.block span{ +#x_type, #y_type{ + max-width: 10em; +} + +fieldset span.text-gray-500, .gr-block.gr-box span.text-gray-500, label.block span{ position: absolute; top: -0.6em; line-height: 1.2em; padding: 0 0.5em; margin: 0; -} -fieldset span.text-gray-500, .gr-panel div.flex-col div.justify-between label, label.block span{ background-color: white; border-top: 1px solid #eee; border-left: 1px solid #eee; border-right: 1px solid #eee; } -.dark fieldset span.text-gray-500, .dark .gr-panel div.flex-col div.justify-between label, .dark label.block span{ +.dark fieldset span.text-gray-500, .dark .gr-block.gr-box span.text-gray-500, .dark label.block span{ background-color: rgb(31, 41, 55); border-top: 1px solid rgb(55 65 81); border-left: 1px solid rgb(55 65 81); @@ -63,4 +65,4 @@ input[type="range"]{ #txt2img_sampling label{ padding-left: 0.6em; padding-right: 0.6em; -} \ No newline at end of file +} diff --git a/webui.py b/webui.py index c7a2cc78f..cb28d3595 100644 --- a/webui.py +++ b/webui.py @@ -55,23 +55,6 @@ def load_model_from_config(config, ckpt, verbose=False): return model -def draw_xy_grid(xs, ys, x_label, y_label, cell): - res = [] - - ver_texts = [[images.GridAnnotation(y_label(y))] for y in ys] - hor_texts = [[images.GridAnnotation(x_label(x))] for x in xs] - - for y in ys: - for x in xs: - state.job = f"{x + y * len(xs)} out of {len(xs) * len(ys)}" - res.append(cell(x, y)) - - grid = images.image_grid(res, rows=len(ys)) - grid = images.draw_grid_annotations(grid, res[0].width, res[0].height, hor_texts, ver_texts) - - return grid - - def run_extras(image, GFPGAN_strength, RealESRGAN_upscaling, RealESRGAN_model_index): processing.torch_gc() @@ -138,7 +121,6 @@ try: except Exception: pass - sd_config = OmegaConf.load(cmd_opts.config) shared.sd_model = load_model_from_config(sd_config, cmd_opts.ckpt) shared.sd_model = (shared.sd_model if cmd_opts.no_half else shared.sd_model.half()) @@ -150,18 +132,18 @@ else: modules.sd_hijack.model_hijack.hijack(shared.sd_model) +modules.scripts.load_scripts(os.path.join(script_path, "scripts")) + # make the program just exit at ctrl+c without waiting for anything -def sigint_handler(signal, frame): - print('Interrupted') +def sigint_handler(sig, frame): + print(f'Interrupted with singal {sig} in {frame}') os._exit(0) signal.signal(signal.SIGINT, sigint_handler) demo = modules.ui.create_ui( - opts=opts, - cmd_opts=cmd_opts, txt2img=wrap_gradio_gpu_call(modules.txt2img.txt2img), img2img=wrap_gradio_gpu_call(modules.img2img.img2img), run_extras=wrap_gradio_gpu_call(run_extras),