stable-diffusion-webui/modules/scripts.py

192 lines
6.1 KiB
Python
Raw Normal View History

import os
import sys
import traceback
import json
2022-09-03 14:21:15 +00:00
import modules.ui as ui
import gradio as gr
2022-09-03 14:21:15 +00:00
from modules.processing import StableDiffusionProcessing
2022-09-08 13:37:13 +00:00
from modules import shared
2022-09-03 14:21:15 +00:00
class Script:
filename = None
2022-09-03 14:21:15 +00:00
args_from = None
args_to = None
2022-09-17 19:43:57 +00:00
# The title of the script. This is what will be displayed in the dropdown menu.
def title(self):
raise NotImplementedError()
2022-09-17 19:43:57 +00:00
# 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.
2022-09-03 14:21:15 +00:00
def ui(self, is_img2img):
pass
# Put restraints on UI elements when this script is selected.
# Restricting the available sampling methods:
# {
# "methods": [ "Euler", "DDIM" ]
# }
def ui_restraints(self):
return {}
2022-09-17 19:43:57 +00:00
# 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.
2022-09-03 22:29:43 +00:00
def show(self, is_img2img):
return True
2022-09-17 19:43:57 +00:00
# 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.
2022-09-03 14:21:15 +00:00
def run(self, *args):
raise NotImplementedError()
2022-09-17 19:43:57 +00:00
# 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.
2022-09-03 14:21:15 +00:00
def describe(self):
return ""
2022-09-03 22:29:43 +00:00
scripts_data = []
2022-09-03 14:21:15 +00:00
def load_scripts(basedir):
if not os.path.exists(basedir):
return
for filename in os.listdir(basedir):
path = os.path.join(basedir, filename)
if not os.path.isfile(path):
continue
try:
with open(path, "r", encoding="utf8") as file:
text = file.read()
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):
2022-09-03 22:29:43 +00:00
scripts_data.append((script_class, path))
except Exception:
print(f"Error loading script: {filename}", file=sys.stderr)
print(traceback.format_exc(), file=sys.stderr)
def wrap_call(func, filename, funcname, *args, default=None, **kwargs):
try:
2022-09-03 14:21:15 +00:00
res = func(*args, **kwargs)
return res
except Exception:
2022-09-03 14:21:15 +00:00
print(f"Error calling: {filename}/{funcname}", file=sys.stderr)
print(traceback.format_exc(), file=sys.stderr)
return default
2022-09-03 22:29:43 +00:00
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]
id_prefix = "img2img_" if is_img2img else "txt2img_"
dropdown = gr.Dropdown(label="Script", choices=["None"] + titles, value="None", type="index", elem_id=id_prefix+"scripts")
2022-09-03 22:29:43 +00:00
inputs = [dropdown]
for script in self.scripts:
script.args_from = len(inputs)
script.args_to = len(inputs)
2022-09-03 22:29:43 +00:00
controls = wrap_call(script.ui, script.filename, "ui", is_img2img)
if controls is None:
continue
2022-09-03 14:21:15 +00:00
2022-09-03 22:29:43 +00:00
for control in controls:
control.custom_script_source = os.path.basename(script.filename)
2022-09-03 22:29:43 +00:00
control.visible = False
2022-09-27 17:52:36 +00:00
2022-09-03 22:29:43 +00:00
inputs += controls
script.args_to = len(inputs)
2022-09-03 14:21:15 +00:00
script_restraints_json = gr.Textbox(value="{}", elem_id=id_prefix+"script_restraints_json", show_label=False, visible=False)
inputs += [script_restraints_json];
2022-09-03 22:29:43 +00:00
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:
script = None
2022-09-03 22:29:43 +00:00
args_from = 0
args_to = 0
2022-09-03 14:21:15 +00:00
return (
[ui.gr_show(True if i == 0 else args_from <= i < args_to) for i in range(len(inputs)-1)]
+ [gr.Textbox.update(value=json.dumps(script.ui_restraints() if script is not None else {}), visible=False)]
)
2022-09-03 14:21:15 +00:00
2022-09-03 22:29:43 +00:00
dropdown.change(
fn=select_script,
inputs=[dropdown],
outputs=inputs
)
2022-09-03 14:21:15 +00:00
script_restraints_json.change(
_js="updateScriptRestraints",
fn=lambda: None,
inputs=[],
outputs=[]
)
2022-09-03 22:29:43 +00:00
return inputs
2022-09-03 14:21:15 +00:00
2022-09-03 22:29:43 +00:00
def run(self, p: StableDiffusionProcessing, *args):
script_index = args[0]
2022-09-03 14:21:15 +00:00
2022-09-03 22:29:43 +00:00
if script_index == 0:
return None
2022-09-03 14:21:15 +00:00
2022-09-03 22:29:43 +00:00
script = self.scripts[script_index-1]
2022-09-03 14:21:15 +00:00
2022-09-03 22:29:43 +00:00
if script is None:
return None
2022-09-03 14:21:15 +00:00
2022-09-27 17:52:36 +00:00
script_args = args[script.args_from:script.args_to]
2022-09-03 22:29:43 +00:00
processed = script.run(p, *script_args)
2022-09-03 14:21:15 +00:00
2022-09-08 13:37:13 +00:00
shared.total_tqdm.clear()
2022-09-03 22:29:43 +00:00
return processed
2022-09-03 14:21:15 +00:00
2022-09-03 22:29:43 +00:00
scripts_txt2img = ScriptRunner()
scripts_img2img = ScriptRunner()