Commit f0b72b81 authored by AUTOMATIC1111's avatar AUTOMATIC1111
Browse files

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
Loading
Loading
Loading
Loading
+1 −7
Original line number Original line Diff line number Diff line
@@ -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
        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,
+95 −0
Original line number Original line Diff line number Diff line
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]
    )
+76 −1
Original line number Original line Diff line number Diff line
@@ -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:
        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:


    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:
    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:


        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:
        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:
        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:
            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:
        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:
        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:
                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:
                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:
                    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:
+1 −0
Original line number Original line Diff line number Diff line
@@ -73,6 +73,7 @@ ui_reorder_categories_builtin_items = [
    "checkboxes",
    "checkboxes",
    "dimensions",
    "dimensions",
    "cfg",
    "cfg",
    "denoising",
    "seed",
    "seed",
    "batch",
    "batch",
    "override_settings",
    "override_settings",
+1 −7
Original line number Original line Diff line number Diff line
@@ -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
        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,
Loading