scripts.py 5.5 KB
Newer Older
1 2 3 4
import os
import sys
import traceback

A
AUTOMATIC 已提交
5
import modules.ui as ui
6 7
import gradio as gr

A
AUTOMATIC 已提交
8
from modules.processing import StableDiffusionProcessing
9
from modules import shared
A
AUTOMATIC 已提交
10

11 12
class Script:
    filename = None
A
AUTOMATIC 已提交
13 14
    args_from = None
    args_to = None
15

J
JashoBell 已提交
16
    # The title of the script. This is what will be displayed in the dropdown menu.
17 18 19
    def title(self):
        raise NotImplementedError()

J
JashoBell 已提交
20 21 22 23
    # How the script is displayed in the UI. See https://gradio.app/docs/#components
    # for the different UI components you can use and how to create them.
    # Most UI components can return a value, such as a boolean for a checkbox.
    # The returned values are passed to the run method as parameters.
A
AUTOMATIC 已提交
24 25 26
    def ui(self, is_img2img):
        pass

J
JashoBell 已提交
27 28 29 30
    # Determines when the script should be shown in the dropdown menu via the 
    # returned value. As an example:
    # is_img2img is True if the current tab is img2img, and False if it is txt2img.
    # Thus, return is_img2img to only show the script on the img2img tab.
A
AUTOMATIC 已提交
31 32 33
    def show(self, is_img2img):
        return True

J
JashoBell 已提交
34 35 36 37 38 39
    # This is where the additional processing is implemented. The parameters include
    # self, the model object "p" (a StableDiffusionProcessing class, see
    # processing.py), and the parameters returned by the ui method.
    # Custom functions can be defined here, and additional libraries can be imported 
    # to be used in processing. The return value should be a Processed object, which is
    # what is returned by the process_images method.
A
AUTOMATIC 已提交
40 41 42
    def run(self, *args):
        raise NotImplementedError()

J
JashoBell 已提交
43 44 45 46
    # The description method is currently unused.
    # To add a description that appears when hovering over the title, amend the "titles" 
    # dict in script.js to include the script title (returned by title) as a key, and 
    # your description as the value.
A
AUTOMATIC 已提交
47 48 49
    def describe(self):
        return ""

50

A
AUTOMATIC 已提交
51
scripts_data = []
52 53


A
AUTOMATIC 已提交
54
def load_scripts(basedir):
55 56 57
    if not os.path.exists(basedir):
        return

H
Hanusz Leszek 已提交
58
    for filename in sorted(os.listdir(basedir)):
59 60 61 62 63
        path = os.path.join(basedir, filename)

        if not os.path.isfile(path):
            continue

64
        try:
65 66 67
            with open(path, "r", encoding="utf8") as file:
                text = file.read()

68 69 70 71 72 73 74
            from types import ModuleType
            compiled = compile(text, path, 'exec')
            module = ModuleType(filename)
            exec(compiled, module.__dict__)

            for key, script_class in module.__dict__.items():
                if type(script_class) == type and issubclass(script_class, Script):
A
AUTOMATIC 已提交
75
                    scripts_data.append((script_class, path))
76 77 78 79

        except Exception:
            print(f"Error loading script: {filename}", file=sys.stderr)
            print(traceback.format_exc(), file=sys.stderr)
80 81 82 83


def wrap_call(func, filename, funcname, *args, default=None, **kwargs):
    try:
A
AUTOMATIC 已提交
84
        res = func(*args, **kwargs)
85 86
        return res
    except Exception:
A
AUTOMATIC 已提交
87
        print(f"Error calling: {filename}/{funcname}", file=sys.stderr)
88 89 90 91 92
        print(traceback.format_exc(), file=sys.stderr)

    return default


A
AUTOMATIC 已提交
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
class ScriptRunner:
    def __init__(self):
        self.scripts = []

    def setup_ui(self, is_img2img):
        for script_class, path in scripts_data:
            script = script_class()
            script.filename = path

            if not script.show(is_img2img):
                continue

            self.scripts.append(script)

        titles = [wrap_call(script.title, script.filename, "title") or f"{script.filename} [error]" for script in self.scripts]

        dropdown = gr.Dropdown(label="Script", choices=["None"] + titles, value="None", type="index")
        inputs = [dropdown]

        for script in self.scripts:
            script.args_from = len(inputs)
O
OWKenobi 已提交
114
            script.args_to = len(inputs)
A
AUTOMATIC 已提交
115 116 117 118 119

            controls = wrap_call(script.ui, script.filename, "ui", is_img2img)

            if controls is None:
                continue
A
AUTOMATIC 已提交
120

A
AUTOMATIC 已提交
121
            for control in controls:
D
DepFA 已提交
122
                control.custom_script_source = os.path.basename(script.filename)
A
AUTOMATIC 已提交
123
                control.visible = False
A
AUTOMATIC 已提交
124

A
AUTOMATIC 已提交
125 126
            inputs += controls
            script.args_to = len(inputs)
A
AUTOMATIC 已提交
127

A
AUTOMATIC 已提交
128 129 130 131 132 133 134 135
        def select_script(script_index):
            if 0 < script_index <= len(self.scripts):
                script = self.scripts[script_index-1]
                args_from = script.args_from
                args_to = script.args_to
            else:
                args_from = 0
                args_to = 0
A
AUTOMATIC 已提交
136

A
AUTOMATIC 已提交
137
            return [ui.gr_show(True if i == 0 else args_from <= i < args_to) for i in range(len(inputs))]
A
AUTOMATIC 已提交
138

A
AUTOMATIC 已提交
139 140 141 142 143
        dropdown.change(
            fn=select_script,
            inputs=[dropdown],
            outputs=inputs
        )
A
AUTOMATIC 已提交
144

A
AUTOMATIC 已提交
145
        return inputs
A
AUTOMATIC 已提交
146

A
AUTOMATIC 已提交
147 148
    def run(self, p: StableDiffusionProcessing, *args):
        script_index = args[0]
A
AUTOMATIC 已提交
149

A
AUTOMATIC 已提交
150 151
        if script_index == 0:
            return None
A
AUTOMATIC 已提交
152

A
AUTOMATIC 已提交
153
        script = self.scripts[script_index-1]
A
AUTOMATIC 已提交
154

A
AUTOMATIC 已提交
155 156
        if script is None:
            return None
A
AUTOMATIC 已提交
157

A
AUTOMATIC 已提交
158 159
        script_args = args[script.args_from:script.args_to]
        processed = script.run(p, *script_args)
A
AUTOMATIC 已提交
160

161 162
        shared.total_tqdm.clear()

A
AUTOMATIC 已提交
163
        return processed
A
AUTOMATIC 已提交
164 165


A
AUTOMATIC 已提交
166 167
scripts_txt2img = ScriptRunner()
scripts_img2img = ScriptRunner()
D
DepFA 已提交
168 169 170 171 172 173 174 175 176

def reload_scripts(basedir):
  global scripts_txt2img,scripts_img2img

  scripts_data.clear()
  load_scripts(basedir)

  scripts_txt2img = ScriptRunner()
  scripts_img2img = ScriptRunner()