├── README.md
├── additional_components.json
├── javascript
└── presetManager.js
├── preset_configuration.json
├── scripts
├── preset_manager_update_check
└── zpreset_utils.py
└── style.css
/README.md:
--------------------------------------------------------------------------------
1 | This is now being archived, and left available for others to review/modify as they see fit. I had not updated it for some time, and I think the core functionality having to deal with presets has had some work and will eventually reach an stable state (if it has not already).
2 |
3 | I might revisit this some day, to provide a presets enhanced version. But this is not likely, as there are other approaches to address dynamic configurations.
4 |
--------------------------------------------------------------------------------
/additional_components.json:
--------------------------------------------------------------------------------
1 | {
2 | "additionalComponents":
3 | {
4 | "X Type" : "xyz_plot_x_type",
5 | "Y Type" : "xyz_plot_y_type",
6 | "Z Type" : "xyz_plot_z_type",
7 | "X Values" : "xyz_plot_x_values",
8 | "Y Values" : "xyz_plot_y_values",
9 | "Z Values" : "xyz_plot_z_values",
10 | "X type" : "x_type",
11 | "Y type" : "y_type",
12 | "X values" : "x_values",
13 | "Y values" : "y_values",
14 | "Z type" : "y_type",
15 | "Z values" : "y_values",
16 | "Draw legend" : "xyz_plot_draw_legend",
17 | "Keep -1 for seeds" : "xyz_plot_no_fixed_seeds",
18 | "Include Sub Images" : "xyz_plot_include_lone_images",
19 | "Include Sub Grids" : "xyz_plot_include_sub_grids"
20 | }
21 | }
--------------------------------------------------------------------------------
/javascript/presetManager.js:
--------------------------------------------------------------------------------
1 | class PresetManagerDropdownComponent{
2 | constructor(component){
3 | this.component = gradioApp().getElementById(component)
4 | }
5 | updateDropdown(selection){
6 | //getElementById("preset-util_preset_ds_dd").querySelector("select").querySelectorAll("option")[1].selected = true;
7 | //Array.from(this.component.querySelector("select").querySelectorAll("option")).find( e => e.value.includes(selection)).selected = true;
8 | this.getDropDownOptions().find( e => e.value.includes(selection)).selected = true;
9 | this.component.querySelector("select").dispatchEvent(new Event("change"));
10 | }
11 | getDropDownOptions(asValues=false){
12 | if (asValues){
13 | let temp = new Array;
14 | Array.from(this.component.querySelector("select").querySelectorAll("option")).forEach( opt => temp.push(opt.value))
15 | return temp
16 | }
17 | else{
18 | return Array.from(this.component.querySelector("select").querySelectorAll("option"))
19 | }
20 | }
21 | getCurrentSelection(){
22 | return this.component.querySelector("select").value
23 | }
24 | }
25 |
26 | class PresetManagerCheckboxGroupComponent{
27 | constructor(component){
28 | this.component = gradioApp().getElementById(component);
29 | this.checkBoxes = new Object;
30 | this.setCheckBoxesContainer()
31 | }
32 | setCheckBoxesContainer(){
33 | Array.from(this.component.querySelectorAll("input")).forEach( _input => this.checkBoxes[_input.nextElementSibling.innerText] = _input)
34 | }
35 | getCheckBoxes(){
36 | let response = new Array;
37 | for (let _component in this.checkBoxes){
38 | response.push([_component, this.checkBoxes[_component]])
39 | }
40 | return response
41 | }
42 | setCheckBoxesValues(iterable){
43 | for (let _componentText in this.checkBoxes){
44 | this.conditionalToggle(false, this.checkBoxes[_componentText])
45 | }
46 | if (iterable instanceof Array){
47 | setTimeout( () =>
48 | iterable.forEach( _label => this.conditionalToggle(true, this.checkBoxes[_label])),
49 | 2)
50 | }
51 | }
52 | conditionalToggle(desiredVal, _component){
53 | //This method behaves like 'set this value to this'
54 | //Using element.checked = true/false, does not register the change, even if you called change afterwards,
55 | // it only sets what it looks like in our case, because there is no form submit, a person then has to click on it twice.
56 | //Options are to use .click() or dispatch an event
57 | if (desiredVal != _component.checked){
58 | _component.dispatchEvent(new Event("change"))//using change event instead of click, in case browser ad-blockers blocks the click method
59 | }
60 | }
61 | }
62 | class PresetManagerMover{
63 | constructor(self, target, onLoadHandler){
64 | this.presetManager = gradioApp().getElementById(self)
65 | this.target = gradioApp().getElementById(target)
66 | this.handler = gradioApp().getElementById(onLoadHandler)
67 | }
68 | move(adjacentAt='afterend'){
69 | /*
70 | 'beforebegin'
71 |
72 | 'afterbegin'
73 |
74 | 'beforeend'
75 |
76 | 'afterend'
77 | */
78 |
79 | this.target.insertAdjacentElement(adjacentAt, this.presetManager)
80 | }
81 | updateTarget(new_target){
82 | this.target = gradioApp().getElementById(new_target)
83 | }
84 | }
85 | //function move(){
86 | // let src = gradioApp().getElementById("txt2img_preset_manager_accordion");
87 | // //let target = Array.from(gradioApp().querySelectorAll("div")).filter( d => d.hasAttribute("class") ? d.getAttribute("class").split(" ")[0] == "gradio-container": false)[0]
88 | // //target.insertAdjacentElement("afterbegin", src)
89 | // let target = gradioApp().getElementById("tab_txt2img");
90 | //}
91 | //document.addEventListener("DOMContentLoaded",() => () => {setTimeout(move, 10000)})
92 |
93 | //onUiTabChange( function (...x) {console.log(x)})
--------------------------------------------------------------------------------
/preset_configuration.json:
--------------------------------------------------------------------------------
1 | {
2 | "Reset": {
3 | "Sampling Steps": 20,
4 | "Sampling method": "Euler a",
5 | "Width": 512,
6 | "Height": 512,
7 | "Restore faces": false,
8 | "Tiling": false,
9 | "Highres. fix": false,
10 | "Seed": -1.0,
11 | "Extra": false,
12 | "Variation seed": -1.0,
13 | "Variation strength": 0,
14 | "Resize seed from width": 0,
15 | "Resize seed from height": 0,
16 | "Firstpass width": 0,
17 | "Firstpass height": 0,
18 | "Denoising strength": 0.7,
19 | "Batch count": 1,
20 | "Batch size": 1,
21 | "CFG Scale": 7
22 | },
23 | "Normal with batch": {
24 | "Sampling Steps": 20,
25 | "Sampling method": "Euler a",
26 | "Width": 512,
27 | "Height": 512,
28 | "Restore faces": false,
29 | "Tiling": false,
30 | "Highres. fix": false,
31 | "Seed": -1.0,
32 | "Extra": false,
33 | "Variation seed": -1.0,
34 | "Variation strength": 0,
35 | "Resize seed from width": 0,
36 | "Resize seed from height": 0,
37 | "Firstpass width": 0,
38 | "Firstpass height": 0,
39 | "Denoising strength": 0.7,
40 | "Batch count": 1,
41 | "Batch size": 8,
42 | "CFG Scale": 7
43 | },
44 | "Quick batch": {
45 | "Sampling Steps": 10,
46 | "Sampling method": "Euler a",
47 | "Width": 512,
48 | "Height": 512,
49 | "Restore faces": false,
50 | "Tiling": false,
51 | "Highres. fix": false,
52 | "Seed": -1.0,
53 | "Extra": false,
54 | "Variation seed": -1.0,
55 | "Variation strength": 0,
56 | "Resize seed from width": 0,
57 | "Resize seed from height": 0,
58 | "Firstpass width": 0,
59 | "Firstpass height": 0,
60 | "Denoising strength": 0.7,
61 | "Batch count": 1,
62 | "Batch size": 8,
63 | "CFG Scale": 7
64 | },
65 | "Better": {
66 | "Sampling Steps": 40,
67 | "Sampling method": "Euler a",
68 | "Width": 512,
69 | "Height": 512,
70 | "Restore faces": false,
71 | "Tiling": false,
72 | "Highres. fix": false,
73 | "Seed": -1.0,
74 | "Extra": false,
75 | "Variation seed": -1.0,
76 | "Variation strength": 0,
77 | "Resize seed from width": 0,
78 | "Resize seed from height": 0,
79 | "Firstpass width": 0,
80 | "Firstpass height": 0,
81 | "Denoising strength": 0.7,
82 | "Batch count": 1,
83 | "Batch size": 8,
84 | "CFG Scale": 7
85 | },
86 | "Great": {
87 | "Sampling Steps": 30,
88 | "Sampling method": "Euler a",
89 | "Width": 1024,
90 | "Height": 1024,
91 | "Restore faces": false,
92 | "Tiling": false,
93 | "Highres. fix": true,
94 | "Seed": -1.0,
95 | "Extra": false,
96 | "Variation seed": -1.0,
97 | "Variation strength": 0,
98 | "Resize seed from width": 0,
99 | "Resize seed from height": 0,
100 | "Firstpass width": 512,
101 | "Firstpass height": 512,
102 | "Denoising strength": 0.4,
103 | "Batch count": 1,
104 | "Batch size": 1,
105 | "CFG Scale": 7
106 | },
107 | "Admirable": {
108 | "Sampling Steps": 30,
109 | "Sampling method": "Euler a",
110 | "Width": 1920,
111 | "Height": 1088,
112 | "Restore faces": false,
113 | "Tiling": false,
114 | "Highres. fix": true,
115 | "Seed": -1.0,
116 | "Extra": false,
117 | "Variation seed": -1.0,
118 | "Variation strength": 0,
119 | "Resize seed from width": 0,
120 | "Resize seed from height": 0,
121 | "Firstpass width": 768,
122 | "Firstpass height": 448,
123 | "Denoising strength": 0.3,
124 | "Batch count": 1,
125 | "Batch size": 1,
126 | "CFG Scale": 7
127 | },
128 | "TEST-MAX-ALL": {
129 | "Sampling Steps": 150,
130 | "Sampling method": "Euler a",
131 | "Width": 2048,
132 | "Height": 2048,
133 | "Restore faces": true,
134 | "Tiling": true,
135 | "Highres. fix": true,
136 | "Seed": 1234567890.0,
137 | "Extra": true,
138 | "Variation seed": 1234567890.0,
139 | "Variation strength": 1,
140 | "Resize seed from width": 2048,
141 | "Resize seed from height": 2048,
142 | "Firstpass width": 768,
143 | "Firstpass height": 448,
144 | "Denoising strength": 0.3,
145 | "Batch count": 100,
146 | "Batch size": 8,
147 | "CFG Scale": 30
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/scripts/preset_manager_update_check:
--------------------------------------------------------------------------------
1 | file used as a flag for install script to know if repository was updated
2 |
--------------------------------------------------------------------------------
/scripts/zpreset_utils.py:
--------------------------------------------------------------------------------
1 | import gradio as gr
2 | import modules.sd_samplers
3 | import modules.scripts as scripts
4 | from modules import shared
5 | import json
6 | import os
7 | import shutil
8 | from pprint import pprint
9 | from modules.ui import gr_show
10 | from collections import namedtuple
11 | from pathlib import Path
12 |
13 | # ********* versioning *****
14 | repo = None
15 | version_map = {
16 | 'https://github.com/vladmandic/automatic':"vlads",
17 | None: "a1111"
18 | }
19 | #Test for a1111 or vlads, vlad had the courtesy to set a variable
20 | if hasattr(shared, "url"):
21 | repo = version_map[getattr(shared, "url")]
22 | else:
23 | repo = "a1111"
24 |
25 | try:
26 | import launch
27 |
28 | if not launch.is_installed("colorama"):
29 | launch.run_pip("install colorama")
30 | except:
31 | pass
32 |
33 | try:
34 | from colorama import just_fix_windows_console, Fore, Style
35 | just_fix_windows_console()
36 | except:
37 | pass
38 |
39 | update_flag = "preset_manager_update_check"
40 |
41 | additional_config_source = "additional_components.json"
42 | additional_config_target = "additional_configs.json"
43 | presets_config_source = "preset_configuration.json"
44 | presets_config_target = "presets.json"
45 |
46 | file_path = scripts.basedir() # file_path is basedir
47 | scripts_path = os.path.join(file_path, "scripts")
48 | path_to_update_flag = os.path.join(scripts_path, update_flag)
49 | is_update_available = False
50 | if os.path.exists(path_to_update_flag):
51 | is_update_available = True
52 | try:
53 | print(Fore.CYAN + "Thank you for using:" + Fore.GREEN + "https://github.com/Gerschel/sd_web_ui_preset_utils/")
54 | print(Fore.RED +"""
55 | ______ _ ___ ___
56 | | ___ \ | | | \/ |
57 | | |_/ / __ ___ ___ ___| |_ | . . | __ _ _ __ __ _ __ _ ___ _ __
58 | | __/ '__/ _ \/ __|/ _ \ __| | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__|
59 | | | | | | __/\__ \ __/ |_ | | | | (_| | | | | (_| | (_| | __/ |
60 | \_| |_| \___||___/\___|\__| \_| |_/\__,_|_| |_|\__,_|\__, |\___|_|
61 | __/ |
62 | |___/
63 | """)
64 | print(Fore.YELLOW + "By: Gerschel Payne")
65 | print(Style.RESET_ALL + "Preset Manager: Checking for pre-existing configuration files.")
66 | except NameError:
67 | print( "Thank you for using: https://github.com/Gerschel/sd_web_ui_preset_utils/")
68 | print("""
69 | ______ _ ___ ___
70 | | ___ \ | | | \/ |
71 | | |_/ / __ ___ ___ ___| |_ | . . | __ _ _ __ __ _ __ _ ___ _ __
72 | | __/ '__/ _ \/ __|/ _ \ __| | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__|
73 | | | | | | __/\__ \ __/ |_ | | | | (_| | | | | (_| | (_| | __/ |
74 | \_| |_| \___||___/\___|\__| \_| |_/\__,_|_| |_|\__,_|\__, |\___|_|
75 | __/ |
76 | |___/
77 | """)
78 | print("By: Gerschel Payne")
79 | print("Preset Manager: Checking for pre-existing configuration files.")
80 |
81 |
82 | source_path = os.path.join(file_path, additional_config_source)
83 | target_path = os.path.join(file_path, additional_config_target)
84 | if not os.path.exists(target_path):
85 | shutil.move(source_path, target_path)
86 | print(f"Created: {additional_config_target}")
87 | else:
88 | print(f"Not writing {additional_config_target}: config exists already")
89 |
90 | source_path = os.path.join(file_path, presets_config_source)
91 | target_path = os.path.join(file_path, presets_config_target)
92 | if not os.path.exists(target_path):
93 | shutil.move(source_path, target_path)
94 | print(f"Created: {presets_config_target}")
95 | else:
96 | print(f"Not writing {presets_config_target}: config exists already")
97 |
98 |
99 | os.remove(path_to_update_flag)
100 |
101 |
102 |
103 | class PresetManager(scripts.Script):
104 |
105 | BASEDIR = scripts.basedir()
106 |
107 | def update_component_name(self, preset, oldval, newval):
108 | if preset.get(oldval) is not None:
109 | preset[newval] = preset.pop(oldval)
110 |
111 | def update_config(self):
112 | """This is a as per need method that will change per need"""
113 | component_remap = {
114 | "Highres. fix": "Hires. fix",
115 | "Firstpass width": "Upscaler",
116 | "Firstpass height": "Upscale by",
117 | "Sampling Steps": "Sampling steps",
118 | "Hires. steps": "Hires steps"
119 | }
120 |
121 | if repo == "vlads":
122 | component_remap.update({
123 | "Hires. fix" : "Hires fix"
124 | })
125 |
126 |
127 | config = self.get_config(self.settings_file)
128 | for preset in config.values():
129 | for old_val, new_val in component_remap.items():
130 | self.update_component_name(preset, old_val, new_val)
131 |
132 | #PresetManager.all_presets = config
133 | self.save_config(self.settings_file, config)
134 |
135 |
136 | def __init__(self, *args, **kwargs):
137 |
138 | self.compinfo = namedtuple("CompInfo", ["component", "label", "elem_id", "kwargs"])
139 |
140 | #self.settings_file = "preset_configuration.json"
141 | self.settings_file = "presets.json"
142 | #self.additional_settings_file = "additional_components.json"
143 | self.additional_settings_file = "additional_configs.json"
144 |
145 |
146 | self.additional_components_for_presets = self.get_config(self.additional_settings_file) #additionalComponents
147 | self.available_components = [
148 | "Prompt", #! must create filter for hr prompt and neg prompt using elem_id in after component 5/28/23
149 | "Negative prompt",
150 | "Sampling steps",
151 | "Sampling method",
152 | "Width",
153 | "Height",
154 | "Restore faces",
155 | "Tiling",
156 | "Hires. fix" if repo != "vlads" else "Hires fix",#NewNew Vlads #!update config needs a version check
157 | "Highres. fix",#old
158 | "Upscaler",#new
159 | "Upscale by",#new
160 | "Hires. steps",#NewOld,
161 | "Hires steps",#NewNew
162 | "Resize width to",#New
163 | "Resize height to",#New
164 | "Hires sampling method",#Newest 5/27/23
165 | "Seed",
166 | "Extra",
167 | "Variation seed",
168 | "Variation strength",
169 | "Resize seed from width",
170 | "Resize seed from height",
171 | "Firstpass width",#old now is upscaler
172 | "Firstpass height",#old now is upscale by
173 | "Denoising strength",
174 | "Batch count",
175 | "Batch size",
176 | "CFG Scale",
177 | "Image CFG Scale",
178 | "Script",
179 | "Input directory",
180 | "Output directory",
181 | "Inpaint batch mask directory (required for inpaint batch processing only)",
182 | "Resize mode",
183 | "Scale",
184 | "Mask blur",
185 | "Mask transparency",
186 | "Mask mode",
187 | "Masked content",
188 | "Inpaint area",
189 | "Only masked padding, pixels",
190 | ]
191 |
192 | if is_update_available:
193 | self.update_config()
194 |
195 | # components that pass through after_components
196 | self.all_components = []
197 |
198 | # Read saved settings
199 | PresetManager.all_presets = self.get_config(self.settings_file)
200 |
201 | # Initialize
202 | self.component_map = {k: None for k in self.available_components}
203 | self.additional_components_map = {k:None for k in self.additional_components_for_presets["additionalComponents"]}
204 | self.additional_components = [x for x in self.additional_components_map] # acts like available_components list for additional components
205 |
206 | # combine defaults and choices
207 | self.component_map = {**self.component_map, **self.additional_components_map}
208 | self.available_components = self.available_components + self.additional_components
209 |
210 |
211 |
212 | def fakeinit(self, *args, **kwargs):
213 | """
214 | __init__ workaround, since some data is not available during instantiation, such as is_img2img, filename, etc.
215 | This method is called from .show(), as that's the first method ScriptRunner calls after handing some state dat (is_txt2img, is_img2img2)
216 | """
217 | #self.elm_prfx = f"{'txt2img' if self.is_txt2img else 'img2img'}"
218 | self.elm_prfx = "preset-util"
219 |
220 |
221 | # UI elements
222 | # class level
223 | # NOTE: Would love to use one component rendered twice, but gradio does not allow rendering twice, so I need one per page
224 | if self.is_txt2img:
225 | # quick set tab
226 | PresetManager.txt2img_preset_dropdown = gr.Dropdown(
227 | label="Presets",
228 | choices=list(PresetManager.all_presets.keys()),
229 | render = False,
230 | elem_id=f"{self.elm_prfx}_preset_qs_dd"
231 | )
232 | # Detailed Save
233 | PresetManager.txt2img_save_detailed_name_dropdown = gr.Dropdown(render=False,
234 | choices=PresetManager.txt2img_preset_dropdown.choices,
235 | label="Presets",
236 | elem_id=f"{self.elm_prfx}_preset_ds_dd"
237 | )
238 | #else:
239 | # quick set tab
240 | PresetManager.img2img_preset_dropdown = gr.Dropdown(
241 | label="Presets",
242 | choices=list(PresetManager.all_presets.keys()),
243 | render = False,
244 | elem_id=f"{self.elm_prfx}_preset_qs_dd"
245 | )
246 | # Detailed Save
247 | PresetManager.img2img_save_detailed_name_dropdown = gr.Dropdown(render=False,
248 | choices=PresetManager.img2img_preset_dropdown.choices,
249 | label="Presets",
250 | elem_id=f"{self.elm_prfx}_preset_ds_dd"
251 | )
252 |
253 | # instance level
254 | # quick set tab
255 | self.stackable_check = gr.Checkbox(value=True, label="Stackable", elem_id=f"{self.elm_prfx}_stackable_check", render=False)
256 | self.save_as = gr.Text(render=False, label="Quick Save", elem_id=f"{self.elm_prfx}_save_qs_txt")
257 | self.save_button = gr.Button(value="Save", variant="secondary", render=False, visible=False, elem_id=f"{self.elm_prfx}_save_qs_bttn")
258 |
259 |
260 | # Detailed Save
261 | self.stackable_check_det = gr.Checkbox(value=True, label="Stackable", elem_id=f"{self.elm_prfx}_stackable_check_det", render=False)
262 | self.save_detail_md = gr.Markdown(render=False, value="
Options are all options hardcoded, and additional you added in additional_components.py\
263 | Make your choices, adjust your settings, set a name, save. To edit a prior choice, select from dropdown and overwrite.\
264 | To apply, go to quick set. Save now works immediately in other tab without restart, filters out non-common between tabs.\
265 | Settings stack. If it's not checked, it wont overwrite. Apply one, then another. Reset is old, update how you need.\
266 | Stackable checkbox is not used for saves, it's used when making a selection from the dropdown, whether to apply as stackable or not", elem_id=f"{self.elm_prfx}_mess_qs_md")
267 | self.save_detailed_as = gr.Text(render=False, label="Detailed Save As", elem_id=f"{self.elm_prfx}_save_ds_txt")
268 | self.save_detailed_button = gr.Button(value="Save", variant="primary", render=False, visible=False, elem_id=f"{self.elm_prfx}_save_ds_bttn")
269 | self.save_detailed_delete_button = gr.Button(value="❌Delete", render=False, elem_id=f"{self.elm_prfx}_del_ds_bttn")
270 | # ********************************** NOTE ********************************************
271 | # NOTE: This fix uglified the code ui is now _ui, row created in before_component, stored in var, used in after_component
272 | # ! TODO: Keep an eye out on this, could cause confusion, if it does, either go single checkboxes with others visible False, or ...
273 | # Potential place to put this, in after_components elem_id txt_generation_info_button or img2img_generation_info button
274 | #self.save_detailed_checkbox_group = gr.CheckboxGroup(render=False, choices=list(x for x in self.available_components if self.component_map[x] is not None), elem_id=f"{self.elm_prfx}_select_ds_chckgrp")
275 |
276 |
277 | # Restart tab
278 | self.gr_restart_bttn = gr.Button(value="Restart", variant="primary", render=False, elem_id=f"{self.elm_prfx}_restart_bttn")
279 |
280 |
281 | # Print tab
282 | self.gather_button = gr.Button(value="Gather", render = False, variant="primary", elem_id=f"{self.elm_prfx}_gather_bttn") # Helper button to print component map
283 | self.inspect_dd = gr.Dropdown(render = False, type="index", interactive=True, elem_id=f"{self.elm_prfx}_inspect_dd")
284 | self.inspect_ta = gr.TextArea(render=False, elem_id=f"{self.elm_prfx}_inspect_txt")
285 |
286 |
287 | self.info_markdown = gr.Markdown(value="!⚠! THIS IS IN ALPHA !⚠!\n\
288 | 🐉 I WILL INTRODUCE SOME BREAKING CHANGES (I will try to avoid it) 🐉\
289 | 🙏 Please recommend your favorite script composers to implement element id's 🙏\n\
290 |
\
291 | If they implement unique element id's, they can get support for presets without making their own\
292 | ❗ I have not added element id support yet, there are more labels than id's ❗\
293 |
\
294 | ❗❗But labels sometimes collide. I can't do 'Mask Blur' because it also matches 'Mask Blur' in scripts❗❗\
295 | Try adding a component label to additional_components.json with element id 'null' without quotes for None\
296 |
\
297 | I would like to support all custom scripts, but need script path/name/title, some distinguishing factor\
298 | through the kwargs in IOComponent_init 'after_compoenet' and 'before_component'\
299 | https://github.com/Gerschel/sd_web_ui_preset_utils", render=False)
300 |
301 |
302 | def title(self):
303 | return "Presets"
304 |
305 | def show(self, is_img2img):
306 | self.fakeinit()
307 | return True
308 | if self.ui_first == "sampler":
309 | if shared.opts.samplers_in_dropdown:
310 | self.before_component_label = "Sampling method"
311 | else:
312 | self.before_component_label = "Sampling Steps"
313 | return True
314 | else:
315 | self.before_component_label = self.positon_manager
316 | return True
317 |
318 | def before_component(self, component, **kwargs):
319 | pass
320 | def _before_component(self, component, **kwargs):
321 | # Define location of where to show up
322 | #if kwargs.get("elem_id") == "":#f"{'txt2img' if self.is_txt2img else 'img2img'}_progress_bar":
323 | #print(kwargs.get("label") == self.before_component_label, "TEST", kwargs.get("label"))
324 | #if kwargs.get("label") == self.before_component_label:
325 | with gr.Accordion(label="Preset Manager", open = False, elem_id=f"{'txt2img' if self.is_txt2img else 'img2img'}_preset_manager_accordion"):
326 | # Quick TAB
327 | with gr.Tab(label="Quick"):
328 | with gr.Row(equal_height = True):
329 | if self.is_txt2img:
330 | PresetManager.txt2img_preset_dropdown.render()
331 | else:
332 | PresetManager.img2img_preset_dropdown.render()
333 | with gr.Column(elem_id=f"{self.elm_prfx}_ref_del_col_qs"):
334 | self.stackable_check.render()
335 | with gr.Row():
336 | with gr.Column(scale=12):
337 | self.save_as.render()
338 | with gr.Column(scale=1):
339 | self.save_button.render()
340 |
341 | # Detailed Save TAB
342 | with gr.Tab(label="Detailed"):
343 | with gr.Accordion(label="Basic info", open=False):
344 | self.save_detail_md.render()
345 | with gr.Column(scale=1):
346 | with gr.Row(equal_height = True):
347 | if self.is_txt2img:
348 | PresetManager.txt2img_save_detailed_name_dropdown.render()
349 | else:
350 | PresetManager.img2img_save_detailed_name_dropdown.render()
351 | with gr.Column(elem_id=f"{self.elm_prfx}_ref_del_col_ds"):
352 | self.save_detailed_delete_button.render()
353 | self.stackable_check_det.render()
354 | with gr.Row():
355 | with gr.Column(scale=12):
356 | self.save_detailed_as.render()
357 | with gr.Column(scale=1):
358 | self.save_detailed_button.render()
359 | with gr.Column(scale=1) as detailed_check:
360 | self.detailed_check = detailed_check
361 | #self.save_detailed_checkbox_group.render()
362 |
363 | # Restart TAB
364 | with gr.Tab(label="Restart"):
365 | self.gr_restart_bttn.render()
366 |
367 | # Print TAB
368 | with gr.Tab(label = "Print"):
369 | self.gather_button.render()
370 | self.inspect_dd.render()
371 | self.inspect_ta.render()
372 |
373 | # Info TAB
374 | with gr.Tab(label="Info"):
375 | self.info_markdown.render()
376 |
377 |
378 | def after_component(self, component, **kwargs):
379 | if hasattr(component, "label") or hasattr(component, "elem_id"):
380 | self.all_components.append(self.compinfo(
381 | component=component,
382 | label=component.label if hasattr(component, "label") else None,
383 | elem_id=component.elem_id if hasattr(component, "elem_id") else None,
384 | kwargs=kwargs
385 | )
386 | )
387 | label = kwargs.get("label")
388 | ele = kwargs.get("elem_id")
389 | # TODO: element id
390 | #if label in self.component_map or label in self.additional_components_map:# and ele == self.additional_components["additionalComponents"]) or (ele == self.additional_components["additionalComponents"]):
391 | if label in self.component_map:# and ele == self.additional_components["additionalComponents"]) or (ele == self.additional_components["additionalComponents"]):
392 | #!Hack to remove conflict between main Prompt and hr Prompt
393 | if self.component_map[label] is None:
394 | self.component_map.update({component.label: component})
395 |
396 |
397 | if ele == "txt2img_generation_info_button" or ele == "img2img_generation_info_button":
398 | self._before_component("")
399 | self.save_detailed_checkbox_group = gr.CheckboxGroup(render=False, choices=list(x for x in self.available_components if self.component_map[x] is not None), elem_id=f"{self.elm_prfx}_select_ds_chckgrp", label="This preset affects?")
400 | with self.detailed_check:
401 | self.save_detailed_checkbox_group.render()
402 | self._ui()
403 |
404 | def ui(self, *args):
405 | pass
406 |
407 | def _ui(self):
408 |
409 |
410 | # Conditional for class members
411 | if self.is_txt2img:
412 | # Quick Set Tab
413 | PresetManager.txt2img_preset_dropdown.change(
414 | fn=self.fetch_valid_values_from_preset,
415 | inputs=[self.stackable_check, PresetManager.txt2img_preset_dropdown] + [self.component_map[comp_name] for comp_name in list(x for x in self.available_components if self.component_map[x] is not None)],
416 | outputs=[self.component_map[comp_name] for comp_name in list(x for x in self.available_components if self.component_map[x] is not None)],
417 | )
418 | # Detailed save tab
419 | PresetManager.txt2img_save_detailed_name_dropdown.change(
420 | fn = self.save_detailed_fetch_valid_values_from_preset,
421 | inputs = [self.stackable_check_det, PresetManager.txt2img_save_detailed_name_dropdown] + [self.component_map[comp_name] for comp_name in list(x for x in self.available_components if self.component_map[x] is not None)],
422 | outputs = [self.save_detailed_checkbox_group, self.save_detailed_as] + [self.component_map[comp_name] for comp_name in list(x for x in self.available_components if self.component_map[x] is not None)],
423 | )
424 | else:
425 | # Quick Set Tab
426 | PresetManager.img2img_preset_dropdown.change(
427 | fn=self.fetch_valid_values_from_preset,
428 | inputs=[self.stackable_check, PresetManager.img2img_preset_dropdown] + [self.component_map[comp_name] for comp_name in list(x for x in self.available_components if self.component_map[x] is not None)],
429 | outputs=[self.component_map[comp_name] for comp_name in list(x for x in self.available_components if self.component_map[x] is not None)],
430 | )
431 | # Detailed save tab
432 | PresetManager.img2img_save_detailed_name_dropdown.change(
433 | fn = self.save_detailed_fetch_valid_values_from_preset,
434 | inputs = [self.stackable_check_det, PresetManager.img2img_save_detailed_name_dropdown] + [self.component_map[comp_name] for comp_name in list(x for x in self.available_components if self.component_map[x] is not None)],
435 | outputs = [self.save_detailed_checkbox_group, self.save_detailed_as] + [self.component_map[comp_name] for comp_name in list(x for x in self.available_components if self.component_map[x] is not None)],
436 | )
437 |
438 | #Mixed Level use this section when needing to reference a class member for things that will affect both tabs
439 | #QuickSet Tab
440 | self.save_button.click(
441 | fn = self.wrapper_save_config(path=self.settings_file),
442 | inputs = [self.save_as] + [self.component_map[comp_name] for comp_name in list(x for x in self.available_components if self.component_map[x] is not None)],# if self.component_map[comp_name] is not None],
443 | outputs = [self.save_as, PresetManager.txt2img_preset_dropdown, PresetManager.img2img_preset_dropdown, PresetManager.txt2img_save_detailed_name_dropdown, PresetManager.img2img_save_detailed_name_dropdown]
444 | #outputs = [self.save_as, self.preset_dropdown, self.save_detailed_name_dropdown]
445 | )#Todo: class level components
446 | #Detailed Tab
447 | self.save_detailed_button.click(
448 | fn = self.save_detailed_config(path=self.settings_file),
449 | # Potential issue
450 | inputs = [self.save_detailed_as , self.save_detailed_checkbox_group] + [self.component_map[comp_name] for comp_name in list(x for x in self.available_components if self.component_map[x] is not None)],
451 | outputs = [self.save_detailed_as, PresetManager.txt2img_save_detailed_name_dropdown, PresetManager.img2img_save_detailed_name_dropdown, PresetManager.txt2img_preset_dropdown, PresetManager.img2img_preset_dropdown]
452 | #outputs = [self.save_detailed_as, self.save_detailed_name_dropdown, self.preset_dropdown]
453 | )
454 | self.save_detailed_delete_button.click(
455 | fn = lambda x: self.delete_preset(x, self.settings_file),
456 | inputs = PresetManager.txt2img_save_detailed_name_dropdown if self.is_txt2img else PresetManager.img2img_save_detailed_name_dropdown,
457 | outputs = [PresetManager.txt2img_preset_dropdown, PresetManager.img2img_preset_dropdown, PresetManager.txt2img_save_detailed_name_dropdown, PresetManager.img2img_save_detailed_name_dropdown]
458 | )
459 |
460 |
461 | #Instance level
462 | #QuickSet Tab
463 | self.save_as.change(
464 | fn = lambda x: gr.update(variant = "primary" if bool(x) else "secondary", visible = bool(x)),
465 | inputs = self.save_as,
466 | outputs = self.save_button
467 | )
468 |
469 | # Detailed save tab
470 | self.save_detailed_as.change(
471 | fn = lambda x: gr.update(visible = bool(x)),
472 | inputs = self.save_detailed_as,
473 | outputs = self.save_detailed_button
474 | )
475 | # Restart Tab
476 | self.gr_restart_bttn.click(fn=self.local_request_restart, _js='restart_reload', inputs=[], outputs=[])
477 |
478 | # Print/Inspect Tab
479 | self.gather_button.click(
480 | fn = self.f_b_syncer,
481 | outputs=[self.inspect_dd, self.gather_button]
482 | )
483 | self.inspect_dd.change(
484 | fn = lambda x: self.inspection_formatter(x),
485 | inputs = self.inspect_dd,
486 | outputs = self.inspect_ta,
487 | )
488 |
489 |
490 | def f_b_syncer(self):
491 | """
492 | ?Front/Backend synchronizer?
493 | Not knowing what else to call it, simple idea, rough to figure out. When updating choices on the front-end, back-end isn't updated, make them both match
494 | https://github.com/gradio-app/gradio/discussions/2848
495 | """
496 | self.inspect_dd.choices = [str(x) for x in self.all_components]
497 | return [gr.update(choices=[str(x) for x in self.all_components]), gr.Button.update(visible=False)]
498 |
499 |
500 | def inspection_formatter(self, x):
501 | comp = self.all_components[x]
502 | text = f"Component Label: {comp.label}\nElement ID: {comp.elem_id}\nComponent: {comp.component}\nAll Info Handed Down: {comp.kwargs}"
503 | return text
504 |
505 |
506 | def run(self, p, *args):
507 | pass
508 |
509 |
510 | def wrapper_save_config(self, path):
511 | """
512 | Helper function to utilize closure
513 | """
514 | # closure keeps path in memory, it's a hack to get around how click or change expects values to be formatted
515 | def func(setting_name, *new_setting):
516 | """
517 | Formats setting and overwrites file
518 | input: setting_name is text autoformatted from clicks input
519 | new_settings is a tuple (by using packing) of formatted text, outputs from
520 | click method must be in same order of labels
521 | """
522 | # Format new_setting from tuple of values, and map them to their label
523 | try:
524 | return_dict = {}
525 | #! TODO: This does not work with datasets or highlighted text that use the type of index
526 | for i,k in enumerate(x for x in self.component_map if self.component_map[x] is not None):
527 | if k != "Sampling method" and not hasattr(self.component_map[k], "type"):
528 | return_dict.update({k: new_setting[i]})
529 | elif k == "Sampling method":
530 | return_dict.update({k: modules.sd_samplers.samplers[new_setting[i]].name if self.is_txt2img else modules.sd_samplers.samplers_for_img2img[new_setting[i]].name})
531 | elif self.component_map[k].type == "index":
532 | return_dict.update({k: self.component_map[k].choices[new_setting[i]]})
533 | else:
534 | return_dict.update({k: new_setting[i]})
535 | new_setting = return_dict
536 |
537 | except IndexError as e:
538 | print(f"IndexError : {e}\n\
539 | Length: {len(new_setting)}\tvalue: {new_setting}\n\
540 | Length: {len(self.component_map)}\t keys: {list(self.component_map.keys())}\n\
541 | \tvalues: {list(self.component_map.values())}\n\
542 | Length: {len(self.available_components)}\t keys: {self.available_components}")
543 |
544 | file = os.path.join(PresetManager.BASEDIR, path)
545 | PresetManager.all_presets.update({setting_name : new_setting})
546 |
547 | with open(file, "w") as f:
548 | json.dump(PresetManager.all_presets, f, indent=4)
549 | #TODO: test [] + [] * 4
550 | return [gr.update(value=""), gr.update(choices = list(PresetManager.all_presets.keys())), gr.update(choices = list(PresetManager.all_presets.keys())), gr.update(choices = list(PresetManager.all_presets.keys())), gr.update(choices = list(PresetManager.all_presets.keys()))]
551 | return func
552 |
553 |
554 | def save_detailed_config(self, path):
555 | """
556 | Helper function to utilize closure
557 | """
558 | # closure keeps path in memory, it's a hack to get around how click or change expects values to be formatted
559 | #! ME ************************************************************
560 | def func(setting_name, *new_setting):
561 | """
562 | Formats setting and overwrites file
563 | input: setting_name is text autoformatted from clicks input
564 | new_settings is a tuple (by using packing) of formatted text, outputs from
565 | click method must be in same order of labels
566 | """
567 | # Format new_setting from tuple of values, and map them to their label
568 | checkbox_items = new_setting[0]
569 | checkbox_items = [x for x in checkbox_items if x in list(x for x in self.available_components if self.component_map[x] is not None)]
570 | items = [y for y in list(zip(new_setting[1:], list(x for x in self.component_map.items() if x[1] is not None))) if y[1][0] in checkbox_items]
571 | #items[0] is value to save, items[1] is tuple, items[1][0] is key items[1][1] is component
572 | try:
573 | return_dict = {}
574 | #! TODO: This does not work with datasets or highlighted text that use the type of index
575 | for e in items:
576 | if e[1][0] != "Sampling method" and not hasattr(e[1][1], "type"):
577 | return_dict.update({e[1][0]: e[0]})
578 | elif e[1][0] == "Sampling method":
579 | return_dict.update({e[1][0]: modules.sd_samplers.samplers[e[0]].name if self.is_txt2img else modules.sd_samplers.samplers_for_img2img[e[0]].name})
580 | elif e[1][1].type == "index":
581 | return_dict.update({e[1][0]: e[1][1].choices[e[0]]})
582 | else:
583 | return_dict.update({e[1][0]: e[0]})
584 | new_setting = return_dict
585 |
586 | except IndexError as e:
587 | print(f"IndexError : {e}\n\
588 | Length: {len(new_setting)}\tvalue: {new_setting}\n\
589 | Length: {len(self.component_map)}\t keys: {list(self.component_map.keys())}\n\
590 | \tvalues: {list(self.component_map.values())}\n\
591 | Length: {len(self.available_components)}\t keys: {self.available_components}")
592 |
593 | file = os.path.join(PresetManager.BASEDIR, path)
594 | PresetManager.all_presets.update({setting_name : new_setting})
595 | #TODO: replace with save method
596 | with open(file, "w") as f:
597 | json.dump(PresetManager.all_presets, f, indent=4)
598 | #return [gr.update(value=""), gr.update(choices = list(PresetManager.all_presets.keys())), gr.update(choices = list(PresetManager.all_presets.keys()))]
599 | return [gr.update(value=""), gr.update(choices = list(PresetManager.all_presets.keys())), gr.update(choices = list(PresetManager.all_presets.keys())), gr.update(choices = list(PresetManager.all_presets.keys())), gr.update(choices = list(PresetManager.all_presets.keys()))]
600 | return func
601 |
602 | def save_config(self, path, data=None, open_mode='w'):
603 | file = os.path.join(PresetManager.BASEDIR, path)
604 | try:
605 | with open(file, "w") as f:
606 | json.dump(data if data else PresetManager.all_presets, f, indent=4)
607 | except FileNotFoundError as e:
608 | print(f"{e}\n{file} not found, check if it exists or if you have moved it.")
609 |
610 |
611 | def get_config(self, path, open_mode='r'):
612 | file = os.path.join(PresetManager.BASEDIR, path)
613 | try:
614 | with open(file, open_mode) as f:
615 | as_dict = json.load(f)
616 | except FileNotFoundError as e:
617 | print(f"{e}\n{file} not found, check if it exists or if you have moved it.")
618 | return as_dict
619 |
620 |
621 | def fetch_valid_values_from_preset(self,stackable_flag, selection, *comps_vals):
622 | #print(stackable_flag)
623 | #print(selection)
624 | #print(comps_vals)
625 | """
626 | Fetches selected preset from dropdown choice and filters valid components from choosen preset
627 | non-valid components will still have None as the page didn't contain any
628 | """
629 | if stackable_flag:
630 | # saved value if in selection and (true if no choices type else true if value in choices else false (got to default)) else default value
631 | return [
632 | #saved value
633 | PresetManager.all_presets[selection][comp_name]
634 | if (comp_name in PresetManager.all_presets[selection]
635 | and (
636 | True if not hasattr(self.component_map[comp_name], "choices")
637 | else
638 | True if PresetManager.all_presets[selection][comp_name] in self.component_map[comp_name].choices
639 | else False
640 | )
641 | )
642 | else
643 | #default value
644 | comps_vals[i]
645 | # if it is not an option type
646 | if not hasattr(self.component_map[comp_name], "choices")
647 | # otherwise for option types choose based on index if
648 | else self.component_map[comp_name].choices[comps_vals[i]]
649 | # it is an index type
650 | if type(comps_vals[i]) == int
651 | # otherwise choose default option based on default entry
652 | #! So the error that I have is because the custom component that has text input but is of type
653 | #! dropdown, has no default, so it returns an empty list
654 | else self.component_map[comp_name].choices[self.component_map[comp_name].choices.index(comps_vals[i])]
655 | # ^ if it \/ has a proper default
656 | if comps_vals[i] in self.component_map[comp_name].choices
657 | # otherwise give the first option
658 | else None#self.component_map[comp_name].choices[0]
659 | for i, comp_name in enumerate(list(x for x in self.available_components if self.component_map[x] is not None and hasattr(self.component_map[x], "value")))]
660 | else:
661 | return [
662 | PresetManager.all_presets[selection][comp_name]
663 | if (comp_name in PresetManager.all_presets[selection]
664 | and (
665 | True if not hasattr(self.component_map[comp_name], "choices")
666 | else
667 | True if PresetManager.all_presets[selection][comp_name] in self.component_map[comp_name].choices
668 | else False
669 | )
670 | )
671 | else
672 | self.component_map[comp_name].value
673 | for i, comp_name in enumerate(list(x for x in self.available_components if self.component_map[x] is not None and hasattr(self.component_map[x], "value")))]
674 |
675 |
676 | def save_detailed_fetch_valid_values_from_preset(self, stackable_flag, selection, *comps_vals):
677 | """
678 | Fetches selected preset from dropdown choice and filters valid components from choosen preset
679 | non-valid components will still have None as the page didn't contain any
680 | """
681 | return [[ comp_name for comp_name in PresetManager.all_presets[selection] ], gr.update(value = selection)] + self.fetch_valid_values_from_preset(stackable_flag, selection, *comps_vals)
682 |
683 | def delete_preset(self, selection, filepath):
684 | """Delete preset from local memory and write file with it removed
685 | filepath is not hardcoded so it can be used with other preset profiles if support gets added for loading additional presets from shares
686 | """
687 | #For writing and keeping front-end in sync with back-end
688 | PresetManager.all_presets.pop(selection)
689 | #Keep front-end in sync with backend
690 | PresetManager.txt2img_preset_dropdown.choice = PresetManager.all_presets.keys()
691 | PresetManager.img2img_preset_dropdown.choice = PresetManager.all_presets.keys()
692 | PresetManager.txt2img_save_detailed_name_dropdown.choice = PresetManager.all_presets.keys()
693 | PresetManager.img2img_save_detailed_name_dropdown.choice = PresetManager.all_presets.keys()
694 | file = os.path.join(PresetManager.BASEDIR, filepath)
695 | with open(file, 'w') as f:
696 | json.dump(PresetManager.all_presets, f, indent=4)
697 | return [gr.Dropdown.update(choices= list(PresetManager.all_presets.keys()), value=list(PresetManager.all_presets.keys())[0])] * 4
698 |
699 | def local_request_restart(self):
700 | "Restart button"
701 | shared.state.interrupt()
702 | shared.state.need_restart = True
703 |
704 |
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | #preset-util_ref_del_col_qs, #preset-util_ref_del_col_ds{
2 | min-width: unset !important;
3 | min-height: unset !important;
4 | flex-grow: 0 !important;
5 | padding: 0;
6 | row-gap: 0;
7 | }
8 |
9 | #preset-util_ref_del_col_qs > button, #preset-util_ref_del_col_ds > button{
10 | /*
11 | min-width: 2em;
12 | min-height: 2em;
13 | */
14 | max-width: 1.4em;
15 | max-height: 1.4em;
16 | flex-grow: 0;
17 | padding-left: 0.25em;
18 | padding-right: 0.25em;
19 | margin: 0.1em 0;
20 | }
21 |
22 | #preset-util_ref_del_col_ds > button[id=preset-util_del_ds_bttn]{
23 | max-width: unset;
24 | max-height: 1.75em;
25 | }
26 | #preset-util_ref_del_col_qs > div[class*=gr-form], #preset-util_ref_del_col_ds > div[class*=gr-form]{
27 | min-width: unset !important;
28 | min-height: unset !important;
29 | padding-left: 0.25em;
30 | padding-right: 0.25em;
31 | margin: 0.1em 0;
32 | flex-grow: 0;
33 | }
34 |
35 | #preset-util_ref_del_col_qs div[id=preset_util_stackable_check_det], #preset-util_ref_del_col_ds div[id=preset_util_stackable_check_det]{
36 | padding: 0.125em;
37 | }
38 |
39 |
40 | #preset-util_save_qs_bttn, #preset-util_save_ds_bttn{
41 | min-height: 100% !important;
42 | }
43 | /*
44 | @media (min-width: 708px){
45 | #txt2img_preset_manager_accordion, #img2img_preset_manager_accordion{
46 | min-width:40vw;
47 | }
48 | }
49 | @media (max-width:707px){
50 | #txt2img_preset_manager_accordion, #img2img_preset_manager_accordion{
51 | min-width: 90vw;
52 | }
53 | }
54 | @media (max-width:679px){
55 | #txt2img_preset_manager_accordion, #img2img_preset_manager_accordion{
56 | min-width: 70vw;
57 | }
58 | }
59 | */
60 | /*
61 | #txt2img_ref_del_col_qs, #img2img_ref_del_col_qs, #txt2img_ref_del_col_ds, #img2img_ref_del_col_ds{
62 | min-width: unset !important;
63 | flex-grow: 0 !important;
64 | padding: 0;
65 | row-gap: 0;
66 | }
67 |
68 | #txt2img_ref_del_col_qs > button, #img2img_ref_del_col_qs > button, #txt2img_ref_del_col_ds > button, #img2img_ref_del_col_ds > button{
69 | min-width: 2em;
70 | min-height: 2em;
71 | max-width: 1.4em;
72 | max-height: 1.4em;
73 | flex-grow: 0;
74 | padding-left: 0.25em;
75 | padding-right: 0.25em;
76 | margin: 0.1em 0;
77 | }
78 |
79 | #txt2img_save_qs_bttn, #img2img_save_ds_bttn{
80 | min-height: 100%;
81 | }
82 | */
83 | /*
84 | #txt2img_preset_qs_dd, #img2img_preset_qs_dd{
85 | align-self: center;
86 | }
87 | */
--------------------------------------------------------------------------------