├── 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 | */ --------------------------------------------------------------------------------