vorstcavry
commited on
Commit
•
31a9ae5
1
Parent(s):
c7ced54
Upload 3 files
Browse files- nr/extra_options_section.py +48 -0
- nr/scripts.py +679 -0
- nr/ui_settings.py +296 -0
nr/extra_options_section.py
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from modules import scripts, shared, ui_components, ui_settings
|
3 |
+
from modules.ui_components import FormColumn
|
4 |
+
|
5 |
+
|
6 |
+
class ExtraOptionsSection(scripts.Script):
|
7 |
+
section = "extra_options"
|
8 |
+
|
9 |
+
def __init__(self):
|
10 |
+
self.comps = None
|
11 |
+
self.setting_names = None
|
12 |
+
|
13 |
+
def title(self):
|
14 |
+
return "Extra options"
|
15 |
+
|
16 |
+
def show(self, is_img2img):
|
17 |
+
return scripts.AlwaysVisible
|
18 |
+
|
19 |
+
def ui(self, is_img2img):
|
20 |
+
self.comps = []
|
21 |
+
self.setting_names = []
|
22 |
+
|
23 |
+
with gr.Blocks() as interface:
|
24 |
+
with gr.Accordion("Options", open=False) if shared.opts.extra_options_accordion and shared.opts.extra_options else gr.Group(), gr.Row():
|
25 |
+
for setting_name in shared.opts.extra_options:
|
26 |
+
with FormColumn():
|
27 |
+
comp = ui_settings.create_setting_component(setting_name)
|
28 |
+
|
29 |
+
self.comps.append(comp)
|
30 |
+
self.setting_names.append(setting_name)
|
31 |
+
|
32 |
+
def get_settings_values():
|
33 |
+
return [ui_settings.get_value_for_setting(key) for key in self.setting_names]
|
34 |
+
|
35 |
+
interface.load(fn=get_settings_values, inputs=[], outputs=self.comps, queue=False, show_progress=False)
|
36 |
+
|
37 |
+
return self.comps
|
38 |
+
|
39 |
+
def before_process(self, p, *args):
|
40 |
+
for name, value in zip(self.setting_names, args):
|
41 |
+
if name not in p.override_settings:
|
42 |
+
p.override_settings[name] = value
|
43 |
+
|
44 |
+
|
45 |
+
shared.options_templates.update(shared.options_section(('ui', "User interface"), {
|
46 |
+
"extra_options": shared.OptionInfo([], "Options in main UI", ui_components.DropdownMulti, lambda: {"choices": list(shared.opts.data_labels.keys())}).js("info", "settingsHintsShowQuicksettings").info("setting entries that also appear in txt2img/img2img interfaces").needs_restart(),
|
47 |
+
"extra_options_accordion": shared.OptionInfo(False, "Place options in main UI into an accordion")
|
48 |
+
}))
|
nr/scripts.py
ADDED
@@ -0,0 +1,679 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import re
|
3 |
+
import sys
|
4 |
+
import inspect
|
5 |
+
from collections import namedtuple
|
6 |
+
|
7 |
+
import gradio as gr
|
8 |
+
|
9 |
+
from modules import shared, paths, script_callbacks, extensions, script_loading, scripts_postprocessing, errors, timer
|
10 |
+
|
11 |
+
AlwaysVisible = object()
|
12 |
+
|
13 |
+
|
14 |
+
class PostprocessImageArgs:
|
15 |
+
def __init__(self, image):
|
16 |
+
self.image = image
|
17 |
+
|
18 |
+
|
19 |
+
class PostprocessBatchListArgs:
|
20 |
+
def __init__(self, images):
|
21 |
+
self.images = images
|
22 |
+
|
23 |
+
|
24 |
+
class Script:
|
25 |
+
name = None
|
26 |
+
"""script's internal name derived from title"""
|
27 |
+
|
28 |
+
section = None
|
29 |
+
"""name of UI section that the script's controls will be placed into"""
|
30 |
+
|
31 |
+
filename = None
|
32 |
+
args_from = None
|
33 |
+
args_to = None
|
34 |
+
alwayson = False
|
35 |
+
|
36 |
+
is_txt2img = False
|
37 |
+
is_img2img = False
|
38 |
+
|
39 |
+
group = None
|
40 |
+
"""A gr.Group component that has all script's UI inside it"""
|
41 |
+
|
42 |
+
infotext_fields = None
|
43 |
+
"""if set in ui(), this is a list of pairs of gradio component + text; the text will be used when
|
44 |
+
parsing infotext to set the value for the component; see ui.py's txt2img_paste_fields for an example
|
45 |
+
"""
|
46 |
+
|
47 |
+
paste_field_names = None
|
48 |
+
"""if set in ui(), this is a list of names of infotext fields; the fields will be sent through the
|
49 |
+
various "Send to <X>" buttons when clicked
|
50 |
+
"""
|
51 |
+
|
52 |
+
api_info = None
|
53 |
+
"""Generated value of type modules.api.models.ScriptInfo with information about the script for API"""
|
54 |
+
|
55 |
+
def title(self):
|
56 |
+
"""this function should return the title of the script. This is what will be displayed in the dropdown menu."""
|
57 |
+
|
58 |
+
raise NotImplementedError()
|
59 |
+
|
60 |
+
def ui(self, is_img2img):
|
61 |
+
"""this function should create gradio UI elements. See https://gradio.app/docs/#components
|
62 |
+
The return value should be an array of all components that are used in processing.
|
63 |
+
Values of those returned components will be passed to run() and process() functions.
|
64 |
+
"""
|
65 |
+
|
66 |
+
pass
|
67 |
+
|
68 |
+
def show(self, is_img2img):
|
69 |
+
"""
|
70 |
+
is_img2img is True if this function is called for the img2img interface, and Fasle otherwise
|
71 |
+
|
72 |
+
This function should return:
|
73 |
+
- False if the script should not be shown in UI at all
|
74 |
+
- True if the script should be shown in UI if it's selected in the scripts dropdown
|
75 |
+
- script.AlwaysVisible if the script should be shown in UI at all times
|
76 |
+
"""
|
77 |
+
|
78 |
+
return True
|
79 |
+
|
80 |
+
def run(self, p, *args):
|
81 |
+
"""
|
82 |
+
This function is called if the script has been selected in the script dropdown.
|
83 |
+
It must do all processing and return the Processed object with results, same as
|
84 |
+
one returned by processing.process_images.
|
85 |
+
|
86 |
+
Usually the processing is done by calling the processing.process_images function.
|
87 |
+
|
88 |
+
args contains all values returned by components from ui()
|
89 |
+
"""
|
90 |
+
|
91 |
+
pass
|
92 |
+
|
93 |
+
def before_process(self, p, *args):
|
94 |
+
"""
|
95 |
+
This function is called very early before processing begins for AlwaysVisible scripts.
|
96 |
+
You can modify the processing object (p) here, inject hooks, etc.
|
97 |
+
args contains all values returned by components from ui()
|
98 |
+
"""
|
99 |
+
|
100 |
+
pass
|
101 |
+
|
102 |
+
def process(self, p, *args):
|
103 |
+
"""
|
104 |
+
This function is called before processing begins for AlwaysVisible scripts.
|
105 |
+
You can modify the processing object (p) here, inject hooks, etc.
|
106 |
+
args contains all values returned by components from ui()
|
107 |
+
"""
|
108 |
+
|
109 |
+
pass
|
110 |
+
|
111 |
+
def before_process_batch(self, p, *args, **kwargs):
|
112 |
+
"""
|
113 |
+
Called before extra networks are parsed from the prompt, so you can add
|
114 |
+
new extra network keywords to the prompt with this callback.
|
115 |
+
|
116 |
+
**kwargs will have those items:
|
117 |
+
- batch_number - index of current batch, from 0 to number of batches-1
|
118 |
+
- prompts - list of prompts for current batch; you can change contents of this list but changing the number of entries will likely break things
|
119 |
+
- seeds - list of seeds for current batch
|
120 |
+
- subseeds - list of subseeds for current batch
|
121 |
+
"""
|
122 |
+
|
123 |
+
pass
|
124 |
+
|
125 |
+
def after_extra_networks_activate(self, p, *args, **kwargs):
|
126 |
+
"""
|
127 |
+
Called after extra networks activation, before conds calculation
|
128 |
+
allow modification of the network after extra networks activation been applied
|
129 |
+
won't be call if p.disable_extra_networks
|
130 |
+
|
131 |
+
**kwargs will have those items:
|
132 |
+
- batch_number - index of current batch, from 0 to number of batches-1
|
133 |
+
- prompts - list of prompts for current batch; you can change contents of this list but changing the number of entries will likely break things
|
134 |
+
- seeds - list of seeds for current batch
|
135 |
+
- subseeds - list of subseeds for current batch
|
136 |
+
- extra_network_data - list of ExtraNetworkParams for current stage
|
137 |
+
"""
|
138 |
+
pass
|
139 |
+
|
140 |
+
def process_batch(self, p, *args, **kwargs):
|
141 |
+
"""
|
142 |
+
Same as process(), but called for every batch.
|
143 |
+
|
144 |
+
**kwargs will have those items:
|
145 |
+
- batch_number - index of current batch, from 0 to number of batches-1
|
146 |
+
- prompts - list of prompts for current batch; you can change contents of this list but changing the number of entries will likely break things
|
147 |
+
- seeds - list of seeds for current batch
|
148 |
+
- subseeds - list of subseeds for current batch
|
149 |
+
"""
|
150 |
+
|
151 |
+
pass
|
152 |
+
|
153 |
+
def postprocess_batch(self, p, *args, **kwargs):
|
154 |
+
"""
|
155 |
+
Same as process_batch(), but called for every batch after it has been generated.
|
156 |
+
|
157 |
+
**kwargs will have same items as process_batch, and also:
|
158 |
+
- batch_number - index of current batch, from 0 to number of batches-1
|
159 |
+
- images - torch tensor with all generated images, with values ranging from 0 to 1;
|
160 |
+
"""
|
161 |
+
|
162 |
+
pass
|
163 |
+
|
164 |
+
def postprocess_batch_list(self, p, pp: PostprocessBatchListArgs, *args, **kwargs):
|
165 |
+
"""
|
166 |
+
Same as postprocess_batch(), but receives batch images as a list of 3D tensors instead of a 4D tensor.
|
167 |
+
This is useful when you want to update the entire batch instead of individual images.
|
168 |
+
|
169 |
+
You can modify the postprocessing object (pp) to update the images in the batch, remove images, add images, etc.
|
170 |
+
If the number of images is different from the batch size when returning,
|
171 |
+
then the script has the responsibility to also update the following attributes in the processing object (p):
|
172 |
+
- p.prompts
|
173 |
+
- p.negative_prompts
|
174 |
+
- p.seeds
|
175 |
+
- p.subseeds
|
176 |
+
|
177 |
+
**kwargs will have same items as process_batch, and also:
|
178 |
+
- batch_number - index of current batch, from 0 to number of batches-1
|
179 |
+
"""
|
180 |
+
|
181 |
+
pass
|
182 |
+
|
183 |
+
def postprocess_image(self, p, pp: PostprocessImageArgs, *args):
|
184 |
+
"""
|
185 |
+
Called for every image after it has been generated.
|
186 |
+
"""
|
187 |
+
|
188 |
+
pass
|
189 |
+
|
190 |
+
def postprocess(self, p, processed, *args):
|
191 |
+
"""
|
192 |
+
This function is called after processing ends for AlwaysVisible scripts.
|
193 |
+
args contains all values returned by components from ui()
|
194 |
+
"""
|
195 |
+
|
196 |
+
pass
|
197 |
+
|
198 |
+
def before_component(self, component, **kwargs):
|
199 |
+
"""
|
200 |
+
Called before a component is created.
|
201 |
+
Use elem_id/label fields of kwargs to figure out which component it is.
|
202 |
+
This can be useful to inject your own components somewhere in the middle of vanilla UI.
|
203 |
+
You can return created components in the ui() function to add them to the list of arguments for your processing functions
|
204 |
+
"""
|
205 |
+
|
206 |
+
pass
|
207 |
+
|
208 |
+
def after_component(self, component, **kwargs):
|
209 |
+
"""
|
210 |
+
Called after a component is created. Same as above.
|
211 |
+
"""
|
212 |
+
|
213 |
+
pass
|
214 |
+
|
215 |
+
def describe(self):
|
216 |
+
"""unused"""
|
217 |
+
return ""
|
218 |
+
|
219 |
+
def elem_id(self, item_id):
|
220 |
+
"""helper function to generate id for a HTML element, constructs final id out of script name, tab and user-supplied item_id"""
|
221 |
+
|
222 |
+
need_tabname = self.show(True) == self.show(False)
|
223 |
+
tabkind = 'img2img' if self.is_img2img else 'txt2txt'
|
224 |
+
tabname = f"{tabkind}_" if need_tabname else ""
|
225 |
+
title = re.sub(r'[^a-z_0-9]', '', re.sub(r'\s', '_', self.title().lower()))
|
226 |
+
|
227 |
+
return f'script_{tabname}{title}_{item_id}'
|
228 |
+
|
229 |
+
def before_hr(self, p, *args):
|
230 |
+
"""
|
231 |
+
This function is called before hires fix start.
|
232 |
+
"""
|
233 |
+
pass
|
234 |
+
|
235 |
+
current_basedir = paths.script_path
|
236 |
+
|
237 |
+
|
238 |
+
def basedir():
|
239 |
+
"""returns the base directory for the current script. For scripts in the main scripts directory,
|
240 |
+
this is the main directory (where webui.py resides), and for scripts in extensions directory
|
241 |
+
(ie extensions/aesthetic/script/aesthetic.py), this is extension's directory (extensions/aesthetic)
|
242 |
+
"""
|
243 |
+
return current_basedir
|
244 |
+
|
245 |
+
|
246 |
+
ScriptFile = namedtuple("ScriptFile", ["basedir", "filename", "path"])
|
247 |
+
|
248 |
+
scripts_data = []
|
249 |
+
postprocessing_scripts_data = []
|
250 |
+
ScriptClassData = namedtuple("ScriptClassData", ["script_class", "path", "basedir", "module"])
|
251 |
+
|
252 |
+
|
253 |
+
def list_scripts(scriptdirname, extension):
|
254 |
+
scripts_list = []
|
255 |
+
|
256 |
+
basedir = os.path.join(paths.script_path, scriptdirname)
|
257 |
+
if os.path.exists(basedir):
|
258 |
+
for filename in sorted(os.listdir(basedir)):
|
259 |
+
scripts_list.append(ScriptFile(paths.script_path, filename, os.path.join(basedir, filename)))
|
260 |
+
|
261 |
+
for ext in extensions.active():
|
262 |
+
scripts_list += ext.list_files(scriptdirname, extension)
|
263 |
+
|
264 |
+
scripts_list = [x for x in scripts_list if os.path.splitext(x.path)[1].lower() == extension and os.path.isfile(x.path)]
|
265 |
+
|
266 |
+
return scripts_list
|
267 |
+
|
268 |
+
|
269 |
+
def list_files_with_name(filename):
|
270 |
+
res = []
|
271 |
+
|
272 |
+
dirs = [paths.script_path] + [ext.path for ext in extensions.active()]
|
273 |
+
|
274 |
+
for dirpath in dirs:
|
275 |
+
if not os.path.isdir(dirpath):
|
276 |
+
continue
|
277 |
+
|
278 |
+
path = os.path.join(dirpath, filename)
|
279 |
+
if os.path.isfile(path):
|
280 |
+
res.append(path)
|
281 |
+
|
282 |
+
return res
|
283 |
+
|
284 |
+
|
285 |
+
def load_scripts():
|
286 |
+
global current_basedir
|
287 |
+
scripts_data.clear()
|
288 |
+
postprocessing_scripts_data.clear()
|
289 |
+
script_callbacks.clear_callbacks()
|
290 |
+
|
291 |
+
scripts_list = list_scripts("scripts", ".py")
|
292 |
+
|
293 |
+
syspath = sys.path
|
294 |
+
|
295 |
+
def register_scripts_from_module(module):
|
296 |
+
for script_class in module.__dict__.values():
|
297 |
+
if not inspect.isclass(script_class):
|
298 |
+
continue
|
299 |
+
|
300 |
+
if issubclass(script_class, Script):
|
301 |
+
scripts_data.append(ScriptClassData(script_class, scriptfile.path, scriptfile.basedir, module))
|
302 |
+
elif issubclass(script_class, scripts_postprocessing.ScriptPostprocessing):
|
303 |
+
postprocessing_scripts_data.append(ScriptClassData(script_class, scriptfile.path, scriptfile.basedir, module))
|
304 |
+
|
305 |
+
def orderby(basedir):
|
306 |
+
# 1st webui, 2nd extensions-builtin, 3rd extensions
|
307 |
+
priority = {os.path.join(paths.script_path, "extensions-builtin"):1, paths.script_path:0}
|
308 |
+
for key in priority:
|
309 |
+
if basedir.startswith(key):
|
310 |
+
return priority[key]
|
311 |
+
return 9999
|
312 |
+
|
313 |
+
for scriptfile in sorted(scripts_list, key=lambda x: [orderby(x.basedir), x]):
|
314 |
+
try:
|
315 |
+
if scriptfile.basedir != paths.script_path:
|
316 |
+
sys.path = [scriptfile.basedir] + sys.path
|
317 |
+
current_basedir = scriptfile.basedir
|
318 |
+
|
319 |
+
script_module = script_loading.load_module(scriptfile.path)
|
320 |
+
register_scripts_from_module(script_module)
|
321 |
+
|
322 |
+
except Exception:
|
323 |
+
errors.report(f"Error loading script: {scriptfile.filename}", exc_info=True)
|
324 |
+
|
325 |
+
finally:
|
326 |
+
sys.path = syspath
|
327 |
+
current_basedir = paths.script_path
|
328 |
+
timer.startup_timer.record(scriptfile.filename)
|
329 |
+
|
330 |
+
global scripts_txt2img, scripts_img2img, scripts_postproc
|
331 |
+
|
332 |
+
scripts_txt2img = ScriptRunner()
|
333 |
+
scripts_img2img = ScriptRunner()
|
334 |
+
scripts_postproc = scripts_postprocessing.ScriptPostprocessingRunner()
|
335 |
+
|
336 |
+
|
337 |
+
def wrap_call(func, filename, funcname, *args, default=None, **kwargs):
|
338 |
+
try:
|
339 |
+
return func(*args, **kwargs)
|
340 |
+
except Exception:
|
341 |
+
errors.report(f"Error calling: {filename}/{funcname}", exc_info=True)
|
342 |
+
|
343 |
+
return default
|
344 |
+
|
345 |
+
|
346 |
+
class ScriptRunner:
|
347 |
+
def __init__(self):
|
348 |
+
self.scripts = []
|
349 |
+
self.selectable_scripts = []
|
350 |
+
self.alwayson_scripts = []
|
351 |
+
self.titles = []
|
352 |
+
self.infotext_fields = []
|
353 |
+
self.paste_field_names = []
|
354 |
+
self.inputs = [None]
|
355 |
+
|
356 |
+
def initialize_scripts(self, is_img2img):
|
357 |
+
from modules import scripts_auto_postprocessing
|
358 |
+
|
359 |
+
self.scripts.clear()
|
360 |
+
self.alwayson_scripts.clear()
|
361 |
+
self.selectable_scripts.clear()
|
362 |
+
|
363 |
+
auto_processing_scripts = scripts_auto_postprocessing.create_auto_preprocessing_script_data()
|
364 |
+
|
365 |
+
for script_data in auto_processing_scripts + scripts_data:
|
366 |
+
script = script_data.script_class()
|
367 |
+
script.filename = script_data.path
|
368 |
+
script.is_txt2img = not is_img2img
|
369 |
+
script.is_img2img = is_img2img
|
370 |
+
|
371 |
+
visibility = script.show(script.is_img2img)
|
372 |
+
|
373 |
+
if visibility == AlwaysVisible:
|
374 |
+
self.scripts.append(script)
|
375 |
+
self.alwayson_scripts.append(script)
|
376 |
+
script.alwayson = True
|
377 |
+
|
378 |
+
elif visibility:
|
379 |
+
self.scripts.append(script)
|
380 |
+
self.selectable_scripts.append(script)
|
381 |
+
|
382 |
+
def create_script_ui(self, script):
|
383 |
+
import modules.api.models as api_models
|
384 |
+
|
385 |
+
script.args_from = len(self.inputs)
|
386 |
+
script.args_to = len(self.inputs)
|
387 |
+
|
388 |
+
controls = wrap_call(script.ui, script.filename, "ui", script.is_img2img)
|
389 |
+
|
390 |
+
if controls is None:
|
391 |
+
return
|
392 |
+
|
393 |
+
script.name = wrap_call(script.title, script.filename, "title", default=script.filename).lower()
|
394 |
+
api_args = []
|
395 |
+
|
396 |
+
for control in controls:
|
397 |
+
control.custom_script_source = os.path.basename(script.filename)
|
398 |
+
|
399 |
+
arg_info = api_models.ScriptArg(label=control.label or "")
|
400 |
+
|
401 |
+
for field in ("value", "minimum", "maximum", "step", "choices"):
|
402 |
+
v = getattr(control, field, None)
|
403 |
+
if v is not None:
|
404 |
+
setattr(arg_info, field, v)
|
405 |
+
|
406 |
+
api_args.append(arg_info)
|
407 |
+
|
408 |
+
script.api_info = api_models.ScriptInfo(
|
409 |
+
name=script.name,
|
410 |
+
is_img2img=script.is_img2img,
|
411 |
+
is_alwayson=script.alwayson,
|
412 |
+
args=api_args,
|
413 |
+
)
|
414 |
+
|
415 |
+
if script.infotext_fields is not None:
|
416 |
+
self.infotext_fields += script.infotext_fields
|
417 |
+
|
418 |
+
if script.paste_field_names is not None:
|
419 |
+
self.paste_field_names += script.paste_field_names
|
420 |
+
|
421 |
+
self.inputs += controls
|
422 |
+
script.args_to = len(self.inputs)
|
423 |
+
|
424 |
+
def setup_ui_for_section(self, section, scriptlist=None):
|
425 |
+
if scriptlist is None:
|
426 |
+
scriptlist = self.alwayson_scripts
|
427 |
+
|
428 |
+
for script in scriptlist:
|
429 |
+
if script.alwayson and script.section != section:
|
430 |
+
continue
|
431 |
+
|
432 |
+
with gr.Group(visible=script.alwayson) as group:
|
433 |
+
self.create_script_ui(script)
|
434 |
+
|
435 |
+
script.group = group
|
436 |
+
|
437 |
+
def prepare_ui(self):
|
438 |
+
self.inputs = [None]
|
439 |
+
|
440 |
+
def setup_ui(self):
|
441 |
+
self.titles = [wrap_call(script.title, script.filename, "title") or f"{script.filename} [error]" for script in self.selectable_scripts]
|
442 |
+
|
443 |
+
self.setup_ui_for_section(None)
|
444 |
+
|
445 |
+
dropdown = gr.Dropdown(label="Script", elem_id="script_list", choices=["None"] + self.titles, value="None", type="index")
|
446 |
+
self.inputs[0] = dropdown
|
447 |
+
|
448 |
+
self.setup_ui_for_section(None, self.selectable_scripts)
|
449 |
+
|
450 |
+
def select_script(script_index):
|
451 |
+
selected_script = self.selectable_scripts[script_index - 1] if script_index>0 else None
|
452 |
+
|
453 |
+
return [gr.update(visible=selected_script == s) for s in self.selectable_scripts]
|
454 |
+
|
455 |
+
def init_field(title):
|
456 |
+
"""called when an initial value is set from ui-config.json to show script's UI components"""
|
457 |
+
|
458 |
+
if title == 'None':
|
459 |
+
return
|
460 |
+
|
461 |
+
script_index = self.titles.index(title)
|
462 |
+
self.selectable_scripts[script_index].group.visible = True
|
463 |
+
|
464 |
+
dropdown.init_field = init_field
|
465 |
+
|
466 |
+
dropdown.change(
|
467 |
+
fn=select_script,
|
468 |
+
inputs=[dropdown],
|
469 |
+
outputs=[script.group for script in self.selectable_scripts]
|
470 |
+
)
|
471 |
+
|
472 |
+
self.script_load_ctr = 0
|
473 |
+
|
474 |
+
def onload_script_visibility(params):
|
475 |
+
title = params.get('Script', None)
|
476 |
+
if title:
|
477 |
+
title_index = self.titles.index(title)
|
478 |
+
visibility = title_index == self.script_load_ctr
|
479 |
+
self.script_load_ctr = (self.script_load_ctr + 1) % len(self.titles)
|
480 |
+
return gr.update(visible=visibility)
|
481 |
+
else:
|
482 |
+
return gr.update(visible=False)
|
483 |
+
|
484 |
+
self.infotext_fields.append((dropdown, lambda x: gr.update(value=x.get('Script', 'None'))))
|
485 |
+
self.infotext_fields.extend([(script.group, onload_script_visibility) for script in self.selectable_scripts])
|
486 |
+
|
487 |
+
return self.inputs
|
488 |
+
|
489 |
+
def run(self, p, *args):
|
490 |
+
script_index = args[0]
|
491 |
+
|
492 |
+
if script_index == 0:
|
493 |
+
return None
|
494 |
+
|
495 |
+
script = self.selectable_scripts[script_index-1]
|
496 |
+
|
497 |
+
if script is None:
|
498 |
+
return None
|
499 |
+
|
500 |
+
script_args = args[script.args_from:script.args_to]
|
501 |
+
processed = script.run(p, *script_args)
|
502 |
+
|
503 |
+
shared.total_tqdm.clear()
|
504 |
+
|
505 |
+
return processed
|
506 |
+
|
507 |
+
def before_process(self, p):
|
508 |
+
for script in self.alwayson_scripts:
|
509 |
+
try:
|
510 |
+
script_args = p.script_args[script.args_from:script.args_to]
|
511 |
+
script.before_process(p, *script_args)
|
512 |
+
except Exception:
|
513 |
+
errors.report(f"Error running before_process: {script.filename}", exc_info=True)
|
514 |
+
|
515 |
+
def process(self, p):
|
516 |
+
for script in self.alwayson_scripts:
|
517 |
+
try:
|
518 |
+
script_args = p.script_args[script.args_from:script.args_to]
|
519 |
+
script.process(p, *script_args)
|
520 |
+
except Exception:
|
521 |
+
errors.report(f"Error running process: {script.filename}", exc_info=True)
|
522 |
+
|
523 |
+
def before_process_batch(self, p, **kwargs):
|
524 |
+
for script in self.alwayson_scripts:
|
525 |
+
try:
|
526 |
+
script_args = p.script_args[script.args_from:script.args_to]
|
527 |
+
script.before_process_batch(p, *script_args, **kwargs)
|
528 |
+
except Exception:
|
529 |
+
errors.report(f"Error running before_process_batch: {script.filename}", exc_info=True)
|
530 |
+
|
531 |
+
def after_extra_networks_activate(self, p, **kwargs):
|
532 |
+
for script in self.alwayson_scripts:
|
533 |
+
try:
|
534 |
+
script_args = p.script_args[script.args_from:script.args_to]
|
535 |
+
script.after_extra_networks_activate(p, *script_args, **kwargs)
|
536 |
+
except Exception:
|
537 |
+
errors.report(f"Error running after_extra_networks_activate: {script.filename}", exc_info=True)
|
538 |
+
|
539 |
+
def process_batch(self, p, **kwargs):
|
540 |
+
for script in self.alwayson_scripts:
|
541 |
+
try:
|
542 |
+
script_args = p.script_args[script.args_from:script.args_to]
|
543 |
+
script.process_batch(p, *script_args, **kwargs)
|
544 |
+
except Exception:
|
545 |
+
errors.report(f"Error running process_batch: {script.filename}", exc_info=True)
|
546 |
+
|
547 |
+
def postprocess(self, p, processed):
|
548 |
+
for script in self.alwayson_scripts:
|
549 |
+
try:
|
550 |
+
script_args = p.script_args[script.args_from:script.args_to]
|
551 |
+
script.postprocess(p, processed, *script_args)
|
552 |
+
except Exception:
|
553 |
+
errors.report(f"Error running postprocess: {script.filename}", exc_info=True)
|
554 |
+
|
555 |
+
def postprocess_batch(self, p, images, **kwargs):
|
556 |
+
for script in self.alwayson_scripts:
|
557 |
+
try:
|
558 |
+
script_args = p.script_args[script.args_from:script.args_to]
|
559 |
+
script.postprocess_batch(p, *script_args, images=images, **kwargs)
|
560 |
+
except Exception:
|
561 |
+
errors.report(f"Error running postprocess_batch: {script.filename}", exc_info=True)
|
562 |
+
|
563 |
+
def postprocess_batch_list(self, p, pp: PostprocessBatchListArgs, **kwargs):
|
564 |
+
for script in self.alwayson_scripts:
|
565 |
+
try:
|
566 |
+
script_args = p.script_args[script.args_from:script.args_to]
|
567 |
+
script.postprocess_batch_list(p, pp, *script_args, **kwargs)
|
568 |
+
except Exception:
|
569 |
+
errors.report(f"Error running postprocess_batch_list: {script.filename}", exc_info=True)
|
570 |
+
|
571 |
+
def postprocess_image(self, p, pp: PostprocessImageArgs):
|
572 |
+
for script in self.alwayson_scripts:
|
573 |
+
try:
|
574 |
+
script_args = p.script_args[script.args_from:script.args_to]
|
575 |
+
script.postprocess_image(p, pp, *script_args)
|
576 |
+
except Exception:
|
577 |
+
errors.report(f"Error running postprocess_image: {script.filename}", exc_info=True)
|
578 |
+
|
579 |
+
def before_component(self, component, **kwargs):
|
580 |
+
for script in self.scripts:
|
581 |
+
try:
|
582 |
+
script.before_component(component, **kwargs)
|
583 |
+
except Exception:
|
584 |
+
errors.report(f"Error running before_component: {script.filename}", exc_info=True)
|
585 |
+
|
586 |
+
def after_component(self, component, **kwargs):
|
587 |
+
for script in self.scripts:
|
588 |
+
try:
|
589 |
+
script.after_component(component, **kwargs)
|
590 |
+
except Exception:
|
591 |
+
errors.report(f"Error running after_component: {script.filename}", exc_info=True)
|
592 |
+
|
593 |
+
def reload_sources(self, cache):
|
594 |
+
for si, script in list(enumerate(self.scripts)):
|
595 |
+
args_from = script.args_from
|
596 |
+
args_to = script.args_to
|
597 |
+
filename = script.filename
|
598 |
+
|
599 |
+
module = cache.get(filename, None)
|
600 |
+
if module is None:
|
601 |
+
module = script_loading.load_module(script.filename)
|
602 |
+
cache[filename] = module
|
603 |
+
|
604 |
+
for script_class in module.__dict__.values():
|
605 |
+
if type(script_class) == type and issubclass(script_class, Script):
|
606 |
+
self.scripts[si] = script_class()
|
607 |
+
self.scripts[si].filename = filename
|
608 |
+
self.scripts[si].args_from = args_from
|
609 |
+
self.scripts[si].args_to = args_to
|
610 |
+
|
611 |
+
|
612 |
+
def before_hr(self, p):
|
613 |
+
for script in self.alwayson_scripts:
|
614 |
+
try:
|
615 |
+
script_args = p.script_args[script.args_from:script.args_to]
|
616 |
+
script.before_hr(p, *script_args)
|
617 |
+
except Exception:
|
618 |
+
errors.report(f"Error running before_hr: {script.filename}", exc_info=True)
|
619 |
+
|
620 |
+
|
621 |
+
scripts_txt2img: ScriptRunner = None
|
622 |
+
scripts_img2img: ScriptRunner = None
|
623 |
+
scripts_postproc: scripts_postprocessing.ScriptPostprocessingRunner = None
|
624 |
+
scripts_current: ScriptRunner = None
|
625 |
+
|
626 |
+
|
627 |
+
def reload_script_body_only():
|
628 |
+
cache = {}
|
629 |
+
scripts_txt2img.reload_sources(cache)
|
630 |
+
scripts_img2img.reload_sources(cache)
|
631 |
+
|
632 |
+
|
633 |
+
reload_scripts = load_scripts # compatibility alias
|
634 |
+
|
635 |
+
|
636 |
+
def add_classes_to_gradio_component(comp):
|
637 |
+
"""
|
638 |
+
this adds gradio-* to the component for css styling (ie gradio-button to gr.Button), as well as some others
|
639 |
+
"""
|
640 |
+
|
641 |
+
comp.elem_classes = [f"gradio-{comp.get_block_name()}", *(comp.elem_classes or [])]
|
642 |
+
|
643 |
+
if getattr(comp, 'multiselect', False):
|
644 |
+
comp.elem_classes.append('multiselect')
|
645 |
+
|
646 |
+
|
647 |
+
|
648 |
+
def IOComponent_init(self, *args, **kwargs):
|
649 |
+
if scripts_current is not None:
|
650 |
+
scripts_current.before_component(self, **kwargs)
|
651 |
+
|
652 |
+
script_callbacks.before_component_callback(self, **kwargs)
|
653 |
+
|
654 |
+
res = original_IOComponent_init(self, *args, **kwargs)
|
655 |
+
|
656 |
+
add_classes_to_gradio_component(self)
|
657 |
+
|
658 |
+
script_callbacks.after_component_callback(self, **kwargs)
|
659 |
+
|
660 |
+
if scripts_current is not None:
|
661 |
+
scripts_current.after_component(self, **kwargs)
|
662 |
+
|
663 |
+
return res
|
664 |
+
|
665 |
+
|
666 |
+
original_IOComponent_init = gr.components.IOComponent.__init__
|
667 |
+
gr.components.IOComponent.__init__ = IOComponent_init
|
668 |
+
|
669 |
+
|
670 |
+
def BlockContext_init(self, *args, **kwargs):
|
671 |
+
res = original_BlockContext_init(self, *args, **kwargs)
|
672 |
+
|
673 |
+
add_classes_to_gradio_component(self)
|
674 |
+
|
675 |
+
return res
|
676 |
+
|
677 |
+
|
678 |
+
original_BlockContext_init = gr.blocks.BlockContext.__init__
|
679 |
+
gr.blocks.BlockContext.__init__ = BlockContext_init
|
nr/ui_settings.py
ADDED
@@ -0,0 +1,296 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
|
3 |
+
from modules import ui_common, shared, script_callbacks, scripts, sd_models, sysinfo
|
4 |
+
from modules.call_queue import wrap_gradio_call
|
5 |
+
from modules.shared import opts
|
6 |
+
from modules.ui_components import FormRow
|
7 |
+
from modules.ui_gradio_extensions import reload_javascript
|
8 |
+
|
9 |
+
|
10 |
+
def get_value_for_setting(key):
|
11 |
+
value = getattr(opts, key)
|
12 |
+
|
13 |
+
info = opts.data_labels[key]
|
14 |
+
args = info.component_args() if callable(info.component_args) else info.component_args or {}
|
15 |
+
args = {k: v for k, v in args.items() if k not in {'precision'}}
|
16 |
+
|
17 |
+
return gr.update(value=value, **args)
|
18 |
+
|
19 |
+
|
20 |
+
def create_setting_component(key, is_quicksettings=False):
|
21 |
+
def fun():
|
22 |
+
return opts.data[key] if key in opts.data else opts.data_labels[key].default
|
23 |
+
|
24 |
+
info = opts.data_labels[key]
|
25 |
+
t = type(info.default)
|
26 |
+
|
27 |
+
args = info.component_args() if callable(info.component_args) else info.component_args
|
28 |
+
|
29 |
+
if info.component is not None:
|
30 |
+
comp = info.component
|
31 |
+
elif t == str:
|
32 |
+
comp = gr.Textbox
|
33 |
+
elif t == int:
|
34 |
+
comp = gr.Number
|
35 |
+
elif t == bool:
|
36 |
+
comp = gr.Checkbox
|
37 |
+
else:
|
38 |
+
raise Exception(f'bad options item type: {t} for key {key}')
|
39 |
+
|
40 |
+
elem_id = f"setting_{key}"
|
41 |
+
|
42 |
+
if info.refresh is not None:
|
43 |
+
if is_quicksettings:
|
44 |
+
res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {}))
|
45 |
+
ui_common.create_refresh_button(res, info.refresh, info.component_args, f"refresh_{key}")
|
46 |
+
else:
|
47 |
+
with FormRow():
|
48 |
+
res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {}))
|
49 |
+
ui_common.create_refresh_button(res, info.refresh, info.component_args, f"refresh_{key}")
|
50 |
+
else:
|
51 |
+
res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {}))
|
52 |
+
|
53 |
+
return res
|
54 |
+
|
55 |
+
|
56 |
+
class UiSettings:
|
57 |
+
submit = None
|
58 |
+
result = None
|
59 |
+
interface = None
|
60 |
+
components = None
|
61 |
+
component_dict = None
|
62 |
+
dummy_component = None
|
63 |
+
quicksettings_list = None
|
64 |
+
quicksettings_names = None
|
65 |
+
text_settings = None
|
66 |
+
|
67 |
+
def run_settings(self, *args):
|
68 |
+
changed = []
|
69 |
+
|
70 |
+
for key, value, comp in zip(opts.data_labels.keys(), args, self.components):
|
71 |
+
assert comp == self.dummy_component or opts.same_type(value, opts.data_labels[key].default), f"Bad value for setting {key}: {value}; expecting {type(opts.data_labels[key].default).__name__}"
|
72 |
+
|
73 |
+
for key, value, comp in zip(opts.data_labels.keys(), args, self.components):
|
74 |
+
if comp == self.dummy_component:
|
75 |
+
continue
|
76 |
+
|
77 |
+
if opts.set(key, value):
|
78 |
+
changed.append(key)
|
79 |
+
|
80 |
+
try:
|
81 |
+
opts.save(shared.config_filename)
|
82 |
+
except RuntimeError:
|
83 |
+
return opts.dumpjson(), f'{len(changed)} settings changed without save: {", ".join(changed)}.'
|
84 |
+
return opts.dumpjson(), f'{len(changed)} settings changed{": " if changed else ""}{", ".join(changed)}.'
|
85 |
+
|
86 |
+
def run_settings_single(self, value, key):
|
87 |
+
if not opts.same_type(value, opts.data_labels[key].default):
|
88 |
+
return gr.update(visible=True), opts.dumpjson()
|
89 |
+
|
90 |
+
if not opts.set(key, value):
|
91 |
+
return gr.update(value=getattr(opts, key)), opts.dumpjson()
|
92 |
+
|
93 |
+
opts.save(shared.config_filename)
|
94 |
+
|
95 |
+
return get_value_for_setting(key), opts.dumpjson()
|
96 |
+
|
97 |
+
def create_ui(self, loadsave, dummy_component):
|
98 |
+
self.components = []
|
99 |
+
self.component_dict = {}
|
100 |
+
self.dummy_component = dummy_component
|
101 |
+
|
102 |
+
shared.settings_components = self.component_dict
|
103 |
+
|
104 |
+
script_callbacks.ui_settings_callback()
|
105 |
+
opts.reorder()
|
106 |
+
|
107 |
+
with gr.Blocks(analytics_enabled=False) as settings_interface:
|
108 |
+
with gr.Row():
|
109 |
+
with gr.Column(scale=6):
|
110 |
+
self.submit = gr.Button(value="Apply settings", variant='primary', elem_id="settings_submit")
|
111 |
+
with gr.Column():
|
112 |
+
restart_gradio = gr.Button(value='Reload UI', variant='primary', elem_id="settings_restart_gradio")
|
113 |
+
|
114 |
+
self.result = gr.HTML(elem_id="settings_result")
|
115 |
+
|
116 |
+
self.quicksettings_names = opts.quicksettings_list
|
117 |
+
self.quicksettings_names = {x: i for i, x in enumerate(self.quicksettings_names) if x != 'quicksettings'}
|
118 |
+
|
119 |
+
self.quicksettings_list = []
|
120 |
+
|
121 |
+
previous_section = None
|
122 |
+
current_tab = None
|
123 |
+
current_row = None
|
124 |
+
with gr.Tabs(elem_id="settings"):
|
125 |
+
for i, (k, item) in enumerate(opts.data_labels.items()):
|
126 |
+
section_must_be_skipped = item.section[0] is None
|
127 |
+
|
128 |
+
if previous_section != item.section and not section_must_be_skipped:
|
129 |
+
elem_id, text = item.section
|
130 |
+
|
131 |
+
if current_tab is not None:
|
132 |
+
current_row.__exit__()
|
133 |
+
current_tab.__exit__()
|
134 |
+
|
135 |
+
gr.Group()
|
136 |
+
current_tab = gr.TabItem(elem_id=f"settings_{elem_id}", label=text)
|
137 |
+
current_tab.__enter__()
|
138 |
+
current_row = gr.Column(variant='compact')
|
139 |
+
current_row.__enter__()
|
140 |
+
|
141 |
+
previous_section = item.section
|
142 |
+
|
143 |
+
if k in self.quicksettings_names and not shared.cmd_opts.freeze_settings:
|
144 |
+
self.quicksettings_list.append((i, k, item))
|
145 |
+
self.components.append(dummy_component)
|
146 |
+
elif section_must_be_skipped:
|
147 |
+
self.components.append(dummy_component)
|
148 |
+
else:
|
149 |
+
component = create_setting_component(k)
|
150 |
+
self.component_dict[k] = component
|
151 |
+
self.components.append(component)
|
152 |
+
|
153 |
+
if current_tab is not None:
|
154 |
+
current_row.__exit__()
|
155 |
+
current_tab.__exit__()
|
156 |
+
|
157 |
+
with gr.TabItem("Defaults", id="defaults", elem_id="settings_tab_defaults"):
|
158 |
+
loadsave.create_ui()
|
159 |
+
|
160 |
+
with gr.TabItem("Sysinfo", id="sysinfo", elem_id="settings_tab_sysinfo"):
|
161 |
+
gr.HTML('<a href="./internal/sysinfo-download" class="sysinfo_big_link" download>Download system info</a><br /><a href="./internal/sysinfo">(or open as text in a new page)</a>', elem_id="sysinfo_download")
|
162 |
+
|
163 |
+
with gr.Row():
|
164 |
+
with gr.Column(scale=1):
|
165 |
+
sysinfo_check_file = gr.File(label="Check system info for validity", type='binary')
|
166 |
+
with gr.Column(scale=1):
|
167 |
+
sysinfo_check_output = gr.HTML("", elem_id="sysinfo_validity")
|
168 |
+
with gr.Column(scale=100):
|
169 |
+
pass
|
170 |
+
|
171 |
+
with gr.TabItem("Actions", id="actions", elem_id="settings_tab_actions"):
|
172 |
+
request_notifications = gr.Button(value='Request browser notifications', elem_id="request_notifications")
|
173 |
+
download_localization = gr.Button(value='Download localization template', elem_id="download_localization")
|
174 |
+
reload_script_bodies = gr.Button(value='Reload custom script bodies (No ui updates, No restart)', variant='secondary', elem_id="settings_reload_script_bodies")
|
175 |
+
with gr.Row():
|
176 |
+
unload_sd_model = gr.Button(value='Unload SD checkpoint to free VRAM', elem_id="sett_unload_sd_model")
|
177 |
+
reload_sd_model = gr.Button(value='Reload the last SD checkpoint back into VRAM', elem_id="sett_reload_sd_model")
|
178 |
+
|
179 |
+
with gr.TabItem("Licenses", id="licenses", elem_id="settings_tab_licenses"):
|
180 |
+
gr.HTML(shared.html("licenses.html"), elem_id="licenses")
|
181 |
+
|
182 |
+
gr.Button(value="Show all pages", elem_id="settings_show_all_pages")
|
183 |
+
|
184 |
+
self.text_settings = gr.Textbox(elem_id="settings_json", value=lambda: opts.dumpjson(), visible=False)
|
185 |
+
|
186 |
+
unload_sd_model.click(
|
187 |
+
fn=sd_models.unload_model_weights,
|
188 |
+
inputs=[],
|
189 |
+
outputs=[]
|
190 |
+
)
|
191 |
+
|
192 |
+
reload_sd_model.click(
|
193 |
+
fn=sd_models.reload_model_weights,
|
194 |
+
inputs=[],
|
195 |
+
outputs=[]
|
196 |
+
)
|
197 |
+
|
198 |
+
request_notifications.click(
|
199 |
+
fn=lambda: None,
|
200 |
+
inputs=[],
|
201 |
+
outputs=[],
|
202 |
+
_js='function(){}'
|
203 |
+
)
|
204 |
+
|
205 |
+
download_localization.click(
|
206 |
+
fn=lambda: None,
|
207 |
+
inputs=[],
|
208 |
+
outputs=[],
|
209 |
+
_js='download_localization'
|
210 |
+
)
|
211 |
+
|
212 |
+
def reload_scripts():
|
213 |
+
scripts.reload_script_body_only()
|
214 |
+
reload_javascript() # need to refresh the html page
|
215 |
+
|
216 |
+
reload_script_bodies.click(
|
217 |
+
fn=reload_scripts,
|
218 |
+
inputs=[],
|
219 |
+
outputs=[]
|
220 |
+
)
|
221 |
+
|
222 |
+
restart_gradio.click(
|
223 |
+
fn=shared.state.request_restart,
|
224 |
+
_js='restart_reload',
|
225 |
+
inputs=[],
|
226 |
+
outputs=[],
|
227 |
+
)
|
228 |
+
|
229 |
+
def check_file(x):
|
230 |
+
if x is None:
|
231 |
+
return ''
|
232 |
+
|
233 |
+
if sysinfo.check(x.decode('utf8', errors='ignore')):
|
234 |
+
return 'Valid'
|
235 |
+
|
236 |
+
return 'Invalid'
|
237 |
+
|
238 |
+
sysinfo_check_file.change(
|
239 |
+
fn=check_file,
|
240 |
+
inputs=[sysinfo_check_file],
|
241 |
+
outputs=[sysinfo_check_output],
|
242 |
+
)
|
243 |
+
|
244 |
+
self.interface = settings_interface
|
245 |
+
|
246 |
+
def add_quicksettings(self):
|
247 |
+
with gr.Row(elem_id="quicksettings", variant="compact"):
|
248 |
+
for _i, k, _item in sorted(self.quicksettings_list, key=lambda x: self.quicksettings_names.get(x[1], x[0])):
|
249 |
+
component = create_setting_component(k, is_quicksettings=True)
|
250 |
+
self.component_dict[k] = component
|
251 |
+
|
252 |
+
def add_functionality(self, demo):
|
253 |
+
self.submit.click(
|
254 |
+
fn=wrap_gradio_call(lambda *args: self.run_settings(*args), extra_outputs=[gr.update()]),
|
255 |
+
inputs=self.components,
|
256 |
+
outputs=[self.text_settings, self.result],
|
257 |
+
)
|
258 |
+
|
259 |
+
for _i, k, _item in self.quicksettings_list:
|
260 |
+
component = self.component_dict[k]
|
261 |
+
info = opts.data_labels[k]
|
262 |
+
|
263 |
+
if isinstance(component, gr.Textbox):
|
264 |
+
methods = [component.submit, component.blur]
|
265 |
+
elif hasattr(component, 'release'):
|
266 |
+
methods = [component.release]
|
267 |
+
else:
|
268 |
+
methods = [component.change]
|
269 |
+
|
270 |
+
for method in methods:
|
271 |
+
method(
|
272 |
+
fn=lambda value, k=k: self.run_settings_single(value, key=k),
|
273 |
+
inputs=[component],
|
274 |
+
outputs=[component, self.text_settings],
|
275 |
+
show_progress=info.refresh is not None,
|
276 |
+
)
|
277 |
+
|
278 |
+
button_set_checkpoint = gr.Button('Change checkpoint', elem_id='change_checkpoint', visible=False)
|
279 |
+
button_set_checkpoint.click(
|
280 |
+
fn=lambda value, _: self.run_settings_single(value, key='sd_model_checkpoint'),
|
281 |
+
_js="function(v){ var res = desiredCheckpointName; desiredCheckpointName = ''; return [res || v, null]; }",
|
282 |
+
inputs=[self.component_dict['sd_model_checkpoint'], self.dummy_component],
|
283 |
+
outputs=[self.component_dict['sd_model_checkpoint'], self.text_settings],
|
284 |
+
)
|
285 |
+
|
286 |
+
component_keys = [k for k in opts.data_labels.keys() if k in self.component_dict]
|
287 |
+
|
288 |
+
def get_settings_values():
|
289 |
+
return [get_value_for_setting(key) for key in component_keys]
|
290 |
+
|
291 |
+
demo.load(
|
292 |
+
fn=get_settings_values,
|
293 |
+
inputs=[],
|
294 |
+
outputs=[self.component_dict[k] for k in component_keys],
|
295 |
+
queue=False,
|
296 |
+
)
|