Commit f0b72b81 authored by AUTOMATIC1111's avatar AUTOMATIC1111

move seed, variation seed and variation seed strength to a single row, dump resize seed from UI

add a way for scripts to register a callback for before/after just a single component's creation
parent 6aa26a26
...@@ -116,7 +116,7 @@ def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args, to_scale=Fal ...@@ -116,7 +116,7 @@ def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args, to_scale=Fal
process_images(p) process_images(p)
def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_styles, init_img, sketch, init_img_with_mask, inpaint_color_sketch, inpaint_color_sketch_orig, init_img_inpaint, init_mask_inpaint, steps: int, sampler_name: str, mask_blur: int, mask_alpha: float, inpainting_fill: int, n_iter: int, batch_size: int, cfg_scale: float, image_cfg_scale: float, denoising_strength: float, seed: int, subseed: int, subseed_strength: float, seed_resize_from_h: int, seed_resize_from_w: int, seed_enable_extras: bool, selected_scale_tab: int, height: int, width: int, scale_by: float, resize_mode: int, inpaint_full_res: bool, inpaint_full_res_padding: int, inpainting_mask_invert: int, img2img_batch_input_dir: str, img2img_batch_output_dir: str, img2img_batch_inpaint_mask_dir: str, override_settings_texts, img2img_batch_use_png_info: bool, img2img_batch_png_info_props: list, img2img_batch_png_info_dir: str, request: gr.Request, *args): def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_styles, init_img, sketch, init_img_with_mask, inpaint_color_sketch, inpaint_color_sketch_orig, init_img_inpaint, init_mask_inpaint, steps: int, sampler_name: str, mask_blur: int, mask_alpha: float, inpainting_fill: int, n_iter: int, batch_size: int, cfg_scale: float, image_cfg_scale: float, denoising_strength: float, selected_scale_tab: int, height: int, width: int, scale_by: float, resize_mode: int, inpaint_full_res: bool, inpaint_full_res_padding: int, inpainting_mask_invert: int, img2img_batch_input_dir: str, img2img_batch_output_dir: str, img2img_batch_inpaint_mask_dir: str, override_settings_texts, img2img_batch_use_png_info: bool, img2img_batch_png_info_props: list, img2img_batch_png_info_dir: str, request: gr.Request, *args):
override_settings = create_override_settings_dict(override_settings_texts) override_settings = create_override_settings_dict(override_settings_texts)
is_batch = mode == 5 is_batch = mode == 5
...@@ -166,12 +166,6 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s ...@@ -166,12 +166,6 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s
prompt=prompt, prompt=prompt,
negative_prompt=negative_prompt, negative_prompt=negative_prompt,
styles=prompt_styles, styles=prompt_styles,
seed=seed,
subseed=subseed,
subseed_strength=subseed_strength,
seed_resize_from_h=seed_resize_from_h,
seed_resize_from_w=seed_resize_from_w,
seed_enable_extras=seed_enable_extras,
sampler_name=sampler_name, sampler_name=sampler_name,
batch_size=batch_size, batch_size=batch_size,
n_iter=n_iter, n_iter=n_iter,
......
import json
import gradio as gr
from modules import scripts, ui, errors
from modules.shared import cmd_opts
from modules.ui_components import ToolButton
class ScriptSeed(scripts.ScriptBuiltin):
section = "seed"
create_group = False
def __init__(self):
self.seed = None
self.reuse_seed = None
self.reuse_subseed = None
def title(self):
return "Seed"
def show(self, is_img2img):
return scripts.AlwaysVisible
def ui(self, is_img2img):
with gr.Row(elem_id=self.elem_id("seed_row")):
if cmd_opts.use_textbox_seed:
self.seed = gr.Textbox(label='Seed', value="", elem_id=self.elem_id("seed"))
else:
self.seed = gr.Number(label='Seed', value=-1, elem_id=self.elem_id("seed"), precision=0)
random_seed = ToolButton(ui.random_symbol, elem_id=self.elem_id("random_seed"), label='Random seed')
reuse_seed = ToolButton(ui.reuse_symbol, elem_id=self.elem_id("reuse_seed"), label='Reuse seed')
subseed = gr.Number(label='Variation seed', value=-1, elem_id=self.elem_id("subseed"), precision=0)
random_subseed = ToolButton(ui.random_symbol, elem_id=self.elem_id("random_subseed"))
reuse_subseed = ToolButton(ui.reuse_symbol, elem_id=self.elem_id("reuse_subseed"))
subseed_strength = gr.Slider(label='Variation strength', value=0.0, minimum=0, maximum=1, step=0.01, elem_id=self.elem_id("subseed_strength"))
random_seed.click(fn=None, _js="function(){setRandomSeed('" + self.elem_id("seed") + "')}", show_progress=False, inputs=[], outputs=[])
random_subseed.click(fn=None, _js="function(){setRandomSeed('" + self.elem_id("subseed") + "')}", show_progress=False, inputs=[], outputs=[])
self.infotext_fields = [
(self.seed, "Seed"),
(subseed, "Variation seed"),
(subseed_strength, "Variation seed strength"),
]
self.on_after_component(lambda x: connect_reuse_seed(self.seed, reuse_seed, x.component, False), elem_id=f'generation_info_{self.tabname}')
self.on_after_component(lambda x: connect_reuse_seed(self.seed, reuse_subseed, x.component, True), elem_id=f'generation_info_{self.tabname}')
return self.seed, subseed, subseed_strength
def before_process(self, p, seed, subseed, subseed_strength):
p.seed = seed
if subseed_strength > 0:
p.subseed = subseed
p.subseed_strength = subseed_strength
def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info: gr.Textbox, is_subseed):
""" Connects a 'reuse (sub)seed' button's click event so that it copies last used
(sub)seed value from generation info the to the seed field. If copying subseed and subseed strength
was 0, i.e. no variation seed was used, it copies the normal seed value instead."""
def copy_seed(gen_info_string: str, index):
res = -1
try:
gen_info = json.loads(gen_info_string)
index -= gen_info.get('index_of_first_image', 0)
if is_subseed and gen_info.get('subseed_strength', 0) > 0:
all_subseeds = gen_info.get('all_subseeds', [-1])
res = all_subseeds[index if 0 <= index < len(all_subseeds) else 0]
else:
all_seeds = gen_info.get('all_seeds', [-1])
res = all_seeds[index if 0 <= index < len(all_seeds) else 0]
except json.decoder.JSONDecodeError:
if gen_info_string:
errors.report(f"Error parsing JSON generation info: {gen_info_string}")
return [res, gr.update()]
reuse_seed.click(
fn=copy_seed,
_js="(x, y) => [x, selected_gallery_index()]",
show_progress=False,
inputs=[generation_info, seed],
outputs=[seed, seed]
)
...@@ -3,6 +3,7 @@ import re ...@@ -3,6 +3,7 @@ import re
import sys import sys
import inspect import inspect
from collections import namedtuple from collections import namedtuple
from dataclasses import dataclass
import gradio as gr import gradio as gr
...@@ -21,6 +22,11 @@ class PostprocessBatchListArgs: ...@@ -21,6 +22,11 @@ class PostprocessBatchListArgs:
self.images = images self.images = images
@dataclass
class OnComponent:
component: gr.blocks.Block
class Script: class Script:
name = None name = None
"""script's internal name derived from title""" """script's internal name derived from title"""
...@@ -35,6 +41,7 @@ class Script: ...@@ -35,6 +41,7 @@ class Script:
is_txt2img = False is_txt2img = False
is_img2img = False is_img2img = False
tabname = None
group = None group = None
"""A gr.Group component that has all script's UI inside it.""" """A gr.Group component that has all script's UI inside it."""
...@@ -55,6 +62,12 @@ class Script: ...@@ -55,6 +62,12 @@ class Script:
api_info = None api_info = None
"""Generated value of type modules.api.models.ScriptInfo with information about the script for API""" """Generated value of type modules.api.models.ScriptInfo with information about the script for API"""
on_before_component_elem_id = []
"""list of callbacks to be called before a component with an elem_id is created"""
on_after_component_elem_id = []
"""list of callbacks to be called after a component with an elem_id is created"""
def title(self): def title(self):
"""this function should return the title of the script. This is what will be displayed in the dropdown menu.""" """this function should return the title of the script. This is what will be displayed in the dropdown menu."""
...@@ -215,6 +228,24 @@ class Script: ...@@ -215,6 +228,24 @@ class Script:
pass pass
def on_before_component(self, callback, *, elem_id):
"""
Calls callback before a component is created. The callback function is called with a single argument of type OnComponent.
This function is an alternative to before_component in that it also cllows to run before a component is created, but
it doesn't require to be called for every created component - just for the one you need.
"""
self.on_before_component_elem_id.append((elem_id, callback))
def on_after_component(self, callback, *, elem_id):
"""
Calls callback after a component is created. The callback function is called with a single argument of type OnComponent.
"""
self.on_after_component_elem_id.append((elem_id, callback))
def describe(self): def describe(self):
"""unused""" """unused"""
return "" return ""
...@@ -236,6 +267,17 @@ class Script: ...@@ -236,6 +267,17 @@ class Script:
pass pass
class ScriptBuiltin(Script):
def elem_id(self, item_id):
"""helper function to generate id for a HTML element, constructs final id out of tab and user-supplied item_id"""
need_tabname = self.show(True) == self.show(False)
tabname = ('img2img' if self.is_img2img else 'txt2txt') + "_" if need_tabname else ""
return f'{tabname}{item_id}'
current_basedir = paths.script_path current_basedir = paths.script_path
...@@ -354,10 +396,17 @@ class ScriptRunner: ...@@ -354,10 +396,17 @@ class ScriptRunner:
self.selectable_scripts = [] self.selectable_scripts = []
self.alwayson_scripts = [] self.alwayson_scripts = []
self.titles = [] self.titles = []
self.title_map = {}
self.infotext_fields = [] self.infotext_fields = []
self.paste_field_names = [] self.paste_field_names = []
self.inputs = [None] self.inputs = [None]
self.on_before_component_elem_id = {}
"""dict of callbacks to be called before an element is created; key=elem_id, value=list of callbacks"""
self.on_after_component_elem_id = {}
"""dict of callbacks to be called after an element is created; key=elem_id, value=list of callbacks"""
def initialize_scripts(self, is_img2img): def initialize_scripts(self, is_img2img):
from modules import scripts_auto_postprocessing from modules import scripts_auto_postprocessing
...@@ -372,6 +421,7 @@ class ScriptRunner: ...@@ -372,6 +421,7 @@ class ScriptRunner:
script.filename = script_data.path script.filename = script_data.path
script.is_txt2img = not is_img2img script.is_txt2img = not is_img2img
script.is_img2img = is_img2img script.is_img2img = is_img2img
script.tabname = "img2img" if is_img2img else "txt2img"
visibility = script.show(script.is_img2img) visibility = script.show(script.is_img2img)
...@@ -446,6 +496,8 @@ class ScriptRunner: ...@@ -446,6 +496,8 @@ class ScriptRunner:
self.inputs = [None] self.inputs = [None]
def setup_ui(self): def setup_ui(self):
all_titles = [wrap_call(script.title, script.filename, "title") or script.filename for script in self.scripts]
self.title_map = {title.lower(): script for title, script in zip(all_titles, self.scripts)}
self.titles = [wrap_call(script.title, script.filename, "title") or f"{script.filename} [error]" for script in self.selectable_scripts] self.titles = [wrap_call(script.title, script.filename, "title") or f"{script.filename} [error]" for script in self.selectable_scripts]
self.setup_ui_for_section(None) self.setup_ui_for_section(None)
...@@ -492,6 +544,13 @@ class ScriptRunner: ...@@ -492,6 +544,13 @@ class ScriptRunner:
self.infotext_fields.append((dropdown, lambda x: gr.update(value=x.get('Script', 'None')))) self.infotext_fields.append((dropdown, lambda x: gr.update(value=x.get('Script', 'None'))))
self.infotext_fields.extend([(script.group, onload_script_visibility) for script in self.selectable_scripts]) self.infotext_fields.extend([(script.group, onload_script_visibility) for script in self.selectable_scripts])
for script in self.scripts:
for elem_id, callback in script.on_before_component_elem_id:
self.on_before_component_elem_id.get(elem_id, []).append((callback, script))
for elem_id, callback in script.on_after_component_elem_id:
self.on_after_component_elem_id.get(elem_id, []).append((callback, script))
return self.inputs return self.inputs
def run(self, p, *args): def run(self, p, *args):
...@@ -585,6 +644,13 @@ class ScriptRunner: ...@@ -585,6 +644,13 @@ class ScriptRunner:
errors.report(f"Error running postprocess_image: {script.filename}", exc_info=True) errors.report(f"Error running postprocess_image: {script.filename}", exc_info=True)
def before_component(self, component, **kwargs): def before_component(self, component, **kwargs):
for callbacks in self.on_before_component_elem_id.get(kwargs.get("elem_id"), []):
for callback, script in callbacks:
try:
callback(OnComponent(component=component))
except Exception:
errors.report(f"Error running on_before_component: {script.filename}", exc_info=True)
for script in self.scripts: for script in self.scripts:
try: try:
script.before_component(component, **kwargs) script.before_component(component, **kwargs)
...@@ -592,12 +658,22 @@ class ScriptRunner: ...@@ -592,12 +658,22 @@ class ScriptRunner:
errors.report(f"Error running before_component: {script.filename}", exc_info=True) errors.report(f"Error running before_component: {script.filename}", exc_info=True)
def after_component(self, component, **kwargs): def after_component(self, component, **kwargs):
for callbacks in self.on_after_component_elem_id.get(component.elem_id, []):
for callback, script in callbacks:
try:
callback(OnComponent(component=component))
except Exception:
errors.report(f"Error running on_after_component: {script.filename}", exc_info=True)
for script in self.scripts: for script in self.scripts:
try: try:
script.after_component(component, **kwargs) script.after_component(component, **kwargs)
except Exception: except Exception:
errors.report(f"Error running after_component: {script.filename}", exc_info=True) errors.report(f"Error running after_component: {script.filename}", exc_info=True)
def script(self, title):
return self.title_map.get(title.lower())
def reload_sources(self, cache): def reload_sources(self, cache):
for si, script in list(enumerate(self.scripts)): for si, script in list(enumerate(self.scripts)):
args_from = script.args_from args_from = script.args_from
...@@ -616,7 +692,6 @@ class ScriptRunner: ...@@ -616,7 +692,6 @@ class ScriptRunner:
self.scripts[si].args_from = args_from self.scripts[si].args_from = args_from
self.scripts[si].args_to = args_to self.scripts[si].args_to = args_to
def before_hr(self, p): def before_hr(self, p):
for script in self.alwayson_scripts: for script in self.alwayson_scripts:
try: try:
......
...@@ -73,6 +73,7 @@ ui_reorder_categories_builtin_items = [ ...@@ -73,6 +73,7 @@ ui_reorder_categories_builtin_items = [
"checkboxes", "checkboxes",
"dimensions", "dimensions",
"cfg", "cfg",
"denoising",
"seed", "seed",
"batch", "batch",
"override_settings", "override_settings",
......
...@@ -9,7 +9,7 @@ from modules.ui import plaintext_to_html ...@@ -9,7 +9,7 @@ from modules.ui import plaintext_to_html
import gradio as gr import gradio as gr
def txt2img(id_task: str, prompt: str, negative_prompt: str, prompt_styles, steps: int, sampler_name: str, n_iter: int, batch_size: int, cfg_scale: float, seed: int, subseed: int, subseed_strength: float, seed_resize_from_h: int, seed_resize_from_w: int, seed_enable_extras: bool, height: int, width: int, enable_hr: bool, denoising_strength: float, hr_scale: float, hr_upscaler: str, hr_second_pass_steps: int, hr_resize_x: int, hr_resize_y: int, hr_checkpoint_name: str, hr_sampler_name: str, hr_prompt: str, hr_negative_prompt, override_settings_texts, request: gr.Request, *args): def txt2img(id_task: str, prompt: str, negative_prompt: str, prompt_styles, steps: int, sampler_name: str, n_iter: int, batch_size: int, cfg_scale: float, height: int, width: int, enable_hr: bool, denoising_strength: float, hr_scale: float, hr_upscaler: str, hr_second_pass_steps: int, hr_resize_x: int, hr_resize_y: int, hr_checkpoint_name: str, hr_sampler_name: str, hr_prompt: str, hr_negative_prompt, override_settings_texts, request: gr.Request, *args):
override_settings = create_override_settings_dict(override_settings_texts) override_settings = create_override_settings_dict(override_settings_texts)
p = processing.StableDiffusionProcessingTxt2Img( p = processing.StableDiffusionProcessingTxt2Img(
...@@ -19,12 +19,6 @@ def txt2img(id_task: str, prompt: str, negative_prompt: str, prompt_styles, step ...@@ -19,12 +19,6 @@ def txt2img(id_task: str, prompt: str, negative_prompt: str, prompt_styles, step
prompt=prompt, prompt=prompt,
styles=prompt_styles, styles=prompt_styles,
negative_prompt=negative_prompt, negative_prompt=negative_prompt,
seed=seed,
subseed=subseed,
subseed_strength=subseed_strength,
seed_resize_from_h=seed_resize_from_h,
seed_resize_from_w=seed_resize_from_w,
seed_enable_extras=seed_enable_extras,
sampler_name=sampler_name, sampler_name=sampler_name,
batch_size=batch_size, batch_size=batch_size,
n_iter=n_iter, n_iter=n_iter,
......
This diff is collapsed.
...@@ -222,14 +222,18 @@ div.block.gradio-accordion { ...@@ -222,14 +222,18 @@ div.block.gradio-accordion {
padding: 0.1em 0.75em; padding: 0.1em 0.75em;
} }
[id$=_seed], [id$=_subseed]{
max-width: 10em;
}
[id$=_subseed_show]{ [id$=_subseed_show]{
min-width: auto !important; min-width: auto !important;
flex-grow: 0 !important; flex-grow: 0 !important;
display: flex; display: flex;
} }
[id$=_subseed_show] label{ [id$=_subseed_show] .label-wrap{
margin-bottom: 0.5em; margin: 0 0 0 0.5em;
align-self: end; align-self: end;
} }
...@@ -1028,5 +1032,6 @@ div.accordions{ ...@@ -1028,5 +1032,6 @@ div.accordions{
div.accordions > div.input-accordion.input-accordion-open{ div.accordions > div.input-accordion.input-accordion-open{
flex: 1 auto; flex: 1 auto;
flex-flow: column;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment