162 lines
4.9 KiB
Python
162 lines
4.9 KiB
Python
"""
|
|
Attempting to map Gradio UI elements present in scripts to allow
|
|
converting to pyQt elements on the plugin side.
|
|
"""
|
|
|
|
import logging
|
|
from typing import List, Sequence, Tuple
|
|
|
|
import gradio as gr
|
|
import modules
|
|
|
|
from .config import LOGGER_NAME
|
|
|
|
log = logging.getLogger(LOGGER_NAME)
|
|
|
|
|
|
def inspect_ui(script: modules.scripts.Script, is_img2img: bool):
|
|
"""Get metadata about accepted arguments by inspecting GUI. Needs Gradio Blocks context."""
|
|
elems = script.ui(is_img2img)
|
|
|
|
metadata = []
|
|
if not isinstance(elems, Sequence):
|
|
return metadata
|
|
|
|
for elem in elems:
|
|
data = {
|
|
"type": "None",
|
|
"label": elem.label,
|
|
"val": elem.value,
|
|
"is_index": False,
|
|
}
|
|
if isinstance(elem, gr.HTML):
|
|
data.update(val="")
|
|
elif isinstance(elem, gr.Markdown):
|
|
data.update(val="")
|
|
elif isinstance(elem, gr.Slider):
|
|
data.update(
|
|
type="range",
|
|
min=elem.minimum,
|
|
max=elem.maximum,
|
|
step=elem.step,
|
|
)
|
|
elif isinstance(elem, gr.Radio):
|
|
data.update(
|
|
type="combo",
|
|
is_index=elem.type == "index",
|
|
opts=elem.choices,
|
|
)
|
|
elif isinstance(elem, gr.Dropdown):
|
|
data.update(
|
|
type="combo",
|
|
is_index=elem.type == "index",
|
|
opts=elem.choices,
|
|
)
|
|
elif isinstance(elem, gr.Textbox):
|
|
data.update(
|
|
type="text",
|
|
)
|
|
elif isinstance(elem, gr.Checkbox):
|
|
data.update(
|
|
type="checkbox",
|
|
)
|
|
elif isinstance(elem, gr.CheckboxGroup):
|
|
data.update(
|
|
type="multiselect",
|
|
is_index=elem.type == "index",
|
|
opts=elem.choices,
|
|
)
|
|
elif isinstance(elem, gr.File):
|
|
data.update(val="") # unsupported
|
|
else:
|
|
data.update(val="") # unsupported
|
|
metadata.append(data)
|
|
|
|
return metadata
|
|
|
|
|
|
img2img_script_meta = None
|
|
txt2img_script_meta = None
|
|
|
|
|
|
def get_scripts_metadata(is_img2img: bool):
|
|
"""Get metadata about accepted arguments for scripts."""
|
|
# NOTE: inspect_ui is quite slow, so cache this
|
|
global txt2img_script_meta, img2img_script_meta
|
|
if is_img2img:
|
|
runner = modules.scripts.scripts_img2img
|
|
else:
|
|
runner = modules.scripts.scripts_txt2img
|
|
metadata = {"None": []}
|
|
if (
|
|
is_img2img
|
|
and img2img_script_meta
|
|
and len(img2img_script_meta) - 1 == len(runner.titles)
|
|
):
|
|
return img2img_script_meta
|
|
elif txt2img_script_meta and len(txt2img_script_meta) - 1 == len(runner.titles):
|
|
return txt2img_script_meta
|
|
|
|
with gr.Blocks(visible=False, analytics_enabled=False):
|
|
for name, script in zip(runner.titles, runner.selectable_scripts):
|
|
metadata[name] = inspect_ui(script, is_img2img)
|
|
if is_img2img:
|
|
img2img_script_meta = metadata
|
|
else:
|
|
txt2img_script_meta = metadata
|
|
return metadata
|
|
|
|
|
|
def get_script_info(
|
|
script_name: str, is_img2img: bool
|
|
) -> Tuple[int, modules.scripts.Script, List[dict]]:
|
|
"""Get index of script, script instance and argument metadata by name.
|
|
|
|
Args:
|
|
script_name (str): Exact name of script.
|
|
is_img2img (bool): Whether the script is for img2img or txt2img.
|
|
|
|
Raises:
|
|
KeyError: Script cannot be found.
|
|
|
|
Returns:
|
|
Tuple[int, Script, List[dict]]: Index of script, script itself and arguments metadata.
|
|
"""
|
|
if is_img2img:
|
|
runner = modules.scripts.scripts_img2img
|
|
else:
|
|
runner = modules.scripts.scripts_txt2img
|
|
# in API, index 0 means no script, scripts are indexed from 1 onwards
|
|
names = ["None"] + runner.titles
|
|
if script_name == "None":
|
|
return 0, None, []
|
|
for i, n in enumerate(names):
|
|
if n == script_name:
|
|
script = runner.selectable_scripts[i - 1]
|
|
return i, script, get_scripts_metadata(is_img2img)[n]
|
|
raise KeyError(f"script not found for type {type}: {script_name}")
|
|
|
|
|
|
def process_script_args(
|
|
script_ind: int, script: modules.scripts.Script, meta: List[dict], args: list
|
|
) -> list:
|
|
"""Get the position arguments required."""
|
|
if script is None:
|
|
return [0] # 0th element selects which script to use. 0 is None.
|
|
|
|
# convert strings back to indexes
|
|
for i, (o, arg) in enumerate(zip(meta, args)):
|
|
if o["is_index"]:
|
|
if isinstance(arg, list):
|
|
args[i] = [o["opts"].index(v) for v in arg]
|
|
else:
|
|
args[i] = o["opts"].index(arg)
|
|
|
|
log.info(
|
|
f"Script selected: {script.filename}, Args Range: [{script.args_from}:{script.args_to}]"
|
|
)
|
|
# pad the args like the internal API requires...
|
|
args = [script_ind] + [0] * (script.args_from - 1) + args
|
|
log.info(f"Script args:\n{args}")
|
|
return args
|