From 98a922ac5ccb59d653accdcc58997beb31bcd72c Mon Sep 17 00:00:00 2001
From: Zyin055 <5003647+Zyin055@users.noreply.github.com>
Date: Mon, 2 Jan 2023 17:08:40 -0600
Subject: [PATCH] massive backend updates
the txt2img and img2img dropdowns are now separated. each has its own config file and its own config presets.
adjusted to sampler index not using checkboxes, but a dropdown now.
adjusted for the removal of firstpass width/height, and added Upscale by.
components on in the UI are tracked with elem_id instead of labels.
when creating a new preset, need to restart the UI because of stupid Gradio desync bug.
moved our dropdown UI section to below the gallery, instead of below the gallery's buttons.
added error handling incase the A1111 UI changes the again to hopefully not brick people's UI.
Create button renamed to Save & Restart.
#16
---
.gitignore | 2 +
javascript/config_presets.js | 27 +-
scripts/config_presets.py | 482 +++++++++++++++++++++--------------
3 files changed, 306 insertions(+), 205 deletions(-)
diff --git a/.gitignore b/.gitignore
index af392e0..6b99ab5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
config.json
+config-img2img.json
+config-txt2img.json
diff --git a/javascript/config_presets.js b/javascript/config_presets.js
index 66c80c6..9a20ce5 100644
--- a/javascript/config_presets.js
+++ b/javascript/config_presets.js
@@ -1,28 +1,37 @@
-//add tooltip by piggybacking off of javascript/hints.js
+//add tooltip by piggybacking off of javascript/hints.js ...
//titles["Add/Remove..."] = "[Config Presets] Add or remove a preset"
-
-//or do it our more precise way
+//...or do it our more precise way:
onUiUpdate(function() {
//set tooltips
- gradioApp().querySelectorAll("#config_presets_open_config_file_button").forEach(el => el.setAttribute("title", "Open the config.json file for manual editing if you want to make changes that way, requires Gradio restart after editing"))
+ gradioApp().querySelectorAll("#config_presets_open_config_file_button").forEach(el => el.setAttribute("title", "Open the config file for manual editing if you want to make changes that way, requires Gradio restart after editing. The txt2img and img2img tabs have separate config files."))
gradioApp().querySelectorAll("#config_preset_save_textbox").forEach(el => el.setAttribute("title", "The label that will be displayed in the dropdown to the left"))
- gradioApp().querySelectorAll("#config_preset_save_button").forEach(el => el.setAttribute("title", "Saves current settings with the new preset name. Overwrites existing preset. This will save: Steps, Sampler, Width/Height, Highres fix, Firstpass width/height, Denoising strength, Batch size, CFG Scale."))
+ gradioApp().querySelectorAll("#config_preset_save_button").forEach(el => el.setAttribute("title", "Saves current settings with the new preset name and restarts the UI. Overwrites existing preset. This will save: Sampler, Steps, Width/Height, Hires. fix, Upscale by, Denoising strength, Batch size, CFG Scale."))
gradioApp().querySelectorAll("#config_preset_add_button").forEach(el => el.setAttribute("title", "[Config Presets] Create or delete a preset"))
gradioApp().querySelectorAll("#config_preset_cancel_save_button").forEach(el => el.setAttribute("title", "Go back"))
gradioApp().querySelectorAll("#config_preset_trash_button").forEach(el => el.setAttribute("title", "Permanently delete selected preset"))
})
-//change() event called by config_preset_dropdown in config_presets.py
+//this function called by config_preset_dropdown in config_presets.py
function config_preset_dropdown_change() {
//when Python changes the enable_hr checkbox in config_preset_dropdown_change() it doesn't fire off the change() JS event, so do this manually
//there is a race condition between the checkbox being checked in Python, and us firing its change event in JavaScript, so wait a bit before firing the event
setTimeout(function() {
- //the "Highres. fix" checkbox has no easy way to identify it, so use its tooltip attribute on the neighboring element
- let highresCheckbox = gradioApp().querySelector("label > span[title='Use a two step process to partially create an image at smaller resolution, upscale, and then improve details in it without changing composition'").parentElement.firstChild
+ let hiresFixCheckbox = gradioApp().querySelector("#txt2img_enable_hr > label").firstChild //gets the element next to the "Hires. fix"
let e = document.createEvent("HTMLEvents")
e.initEvent("change", true, false)
- highresCheckbox.dispatchEvent(e)
+ hiresFixCheckbox.dispatchEvent(e)
+
+ //console.log("hiresFixCheckbox="+hiresFixCheckbox)
+ //console.log("e="+e)
}, 200) //50ms is too fast
+}
+
+
+function config_preset_settings_restart_gradio() {
+ console.log('[Config-Presets] Restarting to apply new config preset...')
+ setTimeout(function() {
+ gradioApp().getElementById("settings_restart_gradio").click()
+ }, 1000)
}
\ No newline at end of file
diff --git a/scripts/config_presets.py b/scripts/config_presets.py
index 2555f66..222a18d 100644
--- a/scripts/config_presets.py
+++ b/scripts/config_presets.py
@@ -1,5 +1,5 @@
+import traceback
import modules.scripts as scripts
-import modules.sd_samplers
import gradio as gr
import json
import os
@@ -8,7 +8,8 @@ import subprocess as sp
BASEDIR = scripts.basedir() #C:\Stable Diffusion\extensions\Config-Presets needs to be set in global space to get the extra 'extensions\Config-Presets' path
-CONFIG_FILE_NAME = "config.json"
+CONFIG_TXT2IMG_FILE_NAME = "config-txt2img.json"
+CONFIG_IMG2IMG_FILE_NAME = "config-img2img.json"
class Script(scripts.Script):
@@ -16,92 +17,154 @@ class Script(scripts.Script):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
+ #self.txt2img_config_preset_dropdown = None
+
# These are the settings from the UI that are saved for each preset
- # First value is the component label, second value is the internal name which is kept for legacy version support
- self.component_labels = { # mirrors the config_preset_dropdown.change(output) events and config_preset_dropdown_change()
- "Sampling Steps": {"internal_name": "steps", "not_used_in_img2img": False},
- "Sampling method": {"internal_name": "sampler_index", "not_used_in_img2img": False},
- "Width": {"internal_name": "width", "not_used_in_img2img": False},
- "Height": {"internal_name": "height", "not_used_in_img2img": False},
- "Highres. fix": {"internal_name": "enable_hr", "not_used_in_img2img": True},
- "Firstpass width": {"internal_name": "firstphase_width", "not_used_in_img2img": True},
- "Firstpass height": {"internal_name": "firstphase_height", "not_used_in_img2img": True},
- "Denoising strength": {"internal_name": "denoising_strength", "not_used_in_img2img": False},
- "Batch count": {"internal_name": "batch_count", "not_used_in_img2img": False},
- "Batch size": {"internal_name": "batch_size", "not_used_in_img2img": False},
- "CFG Scale": {"internal_name": "cfg_scale", "not_used_in_img2img": False},
+ self.txt2img_component_ids = { # mirrors the config_preset_dropdown.change(output) events and config_preset_dropdown_change()
+ "txt2img_sampling",
+ "txt2img_steps",
+ "txt2img_width",
+ "txt2img_height",
+ "txt2img_enable_hr",
+ "txt2img_hr_scale",
+ "txt2img_denoising_strength",
+ "txt2img_batch_count",
+ "txt2img_batch_size",
+ "txt2img_cfg_scale",
+ }
+ self.img2img_component_ids = { # mirrors the config_preset_dropdown.change(output) events and config_preset_dropdown_change()
+ "img2img_sampling",
+ "img2img_steps",
+ "img2img_width",
+ "img2img_height",
+ "img2img_denoising_strength",
+ "img2img_batch_count",
+ "img2img_batch_size",
+ "img2img_cfg_scale",
}
# Mapping between component labels and the actual components in ui.py
- self.component_map = {k: None for k in self.component_labels} # gets filled up in the after_component() method
+ self.txt2img_component_map = {k: None for k in self.txt2img_component_ids} # gets filled up in the after_component() method
+ self.img2img_component_map = {k: None for k in self.img2img_component_ids} # gets filled up in the after_component() method
- # Load config file
+ # Load txt2img and img2img config files
try:
- with open(f"{BASEDIR}/{CONFIG_FILE_NAME}") as file:
- self.config_presets = json.load(file)
+ with open(f"{BASEDIR}/{CONFIG_TXT2IMG_FILE_NAME}") as file:
+ self.txt2img_config_presets = json.load(file)
+
+ # #print("self.config_presets loaded:")
+ # for preset_name, values_dict in self.txt2img_config_presets.items():
+ # #print(preset_name,values_dict)
+ # if "steps" in values_dict.keys():
+ # print("[ERROR][Config-Presets] Your config.json file is using an outdated format, so the Config Presets dropdown will not work. You need to delete /extensions/Config-Presets/config.json so it can be recreated with the new updated format.")
+ # break
+
+
except FileNotFoundError:
- # Config file not found
+ # txt2img config file not found
# First time running the extension or it was deleted, so fill it with default values
- self.config_presets = {
- "Default": {
- },
+ self.txt2img_config_presets = {
+ "Default": {},
"Low quality ------ 512x512, steps: 10, batch size: 8, DPM++ 2M Karras": {
- "steps": 10,
- "sampler_index": "DPM++ 2M Karras",
- "width": 512,
- "height": 512,
- "batch_count": 1,
- "batch_size": 8,
- "cfg_scale": 7
+ "txt2img_sampling": "DPM++ 2M Karras",
+ "txt2img_steps": 10,
+ "txt2img_width": 512,
+ "txt2img_height": 512,
+ "txt2img_batch_count": 1,
+ "txt2img_batch_size": 8,
+ "txt2img_cfg_scale": 7,
},
"Medium quality - 512x512, steps: 20, batch size: 8, DPM++ 2S a Karras": {
- "steps": 20,
- "sampler_index": "DPM++ 2S a Karras",
- "width": 512,
- "height": 512,
- "batch_count": 1,
- "batch_size": 8,
- "cfg_scale": 7
+ "txt2img_sampling": "DPM++ 2S a Karras",
+ "txt2img_steps": 20,
+ "txt2img_width": 512,
+ "txt2img_height": 512,
+ "txt2img_batch_count": 1,
+ "txt2img_batch_size": 8,
+ "txt2img_cfg_scale": 7,
},
"High quality ------ 512x512, steps: 40, batch size: 8, DPM++ 2S a Karras": {
- "steps": 40,
- "sampler_index": "DPM++ 2S a Karras",
- "width": 512,
- "height": 512,
- "batch_count": 1,
- "batch_size": 8,
- "cfg_scale": 7
+ "txt2img_sampling": "DPM++ 2S a Karras",
+ "txt2img_steps": 40,
+ "txt2img_width": 512,
+ "txt2img_height": 512,
+ "txt2img_batch_count": 1,
+ "txt2img_batch_size": 8,
+ "txt2img_cfg_scale": 7,
},
- "High res -------- 1024x1024, steps: 30, batch size: 1, DPM++ 2M Karras, [Highres fix: 512x512, Denoising: 0.4]": {
- "steps": 30,
- "sampler_index": "DPM++ 2M Karras",
- "width": 1024,
- "height": 1024,
- "enable_hr": "true",
- "firstphase_width": 512,
- "firstphase_height": 512,
- "denoising_strength": 0.4,
- "batch_count": 1,
- "batch_size": 1,
- "cfg_scale": 7
+ "High res -------- 1024x1024, steps: 30, batch size: 1, DPM++ 2M Karras, [Upscale by: 2, Denoising: 0.4]": {
+ "txt2img_steps": 30,
+ "txt2img_sampling": "DPM++ 2M Karras",
+ "txt2img_width": 512,
+ "txt2img_height": 512,
+ "txt2img_enable_hr": "true",
+ "txt2img_hr_scale": 2,
+ "txt2img_denoising_strength": 0.4,
+ "txt2img_batch_count": 1,
+ "txt2img_batch_size": 1,
+ "txt2img_cfg_scale": 7,
+ },
+ "Wallpaper ----- 1920x1080, steps: 30, batch size: 1, DPM++ 2M Karras, [Upscale by: 3, Denoising: 0.3]": {
+ "txt2img_steps": 30,
+ "txt2img_sampling": "DPM++ 2M Karras",
+ "txt2img_width": 640,
+ "txt2img_height": 360,
+ "txt2img_enable_hr": "true",
+ "txt2img_hr_scale": 3,
+ "txt2img_denoising_strength": 0.3,
+ "txt2img_batch_count": 1,
+ "txt2img_batch_size": 1,
+ "txt2img_cfg_scale": 7,
},
- "Wallpaper ----- 1920x1088, steps: 30, batch size: 1, DPM++ 2M Karras, [Highres fix: 768x448, Denoising: 0.3]": {
- "steps": 30,
- "sampler_index": "DPM++ 2M Karras",
- "width": 1920,
- "height": 1088,
- "enable_hr": "true",
- "firstphase_width": 768,
- "firstphase_height": 448,
- "denoising_strength": 0.3,
- "batch_count": 1,
- "batch_size": 1,
- "cfg_scale": 7
- }
}
- self.write_config_presets_to_file()
- print(f"Config Presets: Config file not found, created default config at {BASEDIR}/{CONFIG_FILE_NAME}")
+ write_config_presets_to_file(self.txt2img_config_presets, CONFIG_TXT2IMG_FILE_NAME)
+ print(f"[Config Presets] txt2img config file not found, created default config at {BASEDIR}/{CONFIG_TXT2IMG_FILE_NAME}")
+
+
+ try:
+ with open(f"{BASEDIR}/{CONFIG_IMG2IMG_FILE_NAME}") as file:
+ self.img2img_config_presets = json.load(file)
+
+ except FileNotFoundError:
+ # img2img config file not found
+ # First time running the extension or it was deleted, so fill it with default values
+ self.img2img_config_presets = {
+ "Default": {},
+ "Low denoising ------- 512x512, denoising: 0.25, steps: 10, DPM++ 2M Karras": {
+ "img2img_sampling": "DPM++ 2M Karras",
+ "img2img_steps": 10,
+ "img2img_width": 512,
+ "img2img_height": 512,
+ "img2img_batch_count": 1,
+ "img2img_batch_size": 1,
+ "img2img_cfg_scale": 7,
+ "img2img_denoising_strength": 0.25,
+ },
+ "Medium denoising -- 512x512, denoising: 0.50, steps: 10, DPM++ 2M Karras": {
+ "img2img_sampling": "DPM++ 2M Karras",
+ "img2img_steps": 10,
+ "img2img_width": 512,
+ "img2img_height": 512,
+ "img2img_batch_count": 1,
+ "img2img_batch_size": 1,
+ "img2img_cfg_scale": 7,
+ "img2img_denoising_strength": 0.50,
+ },
+ "High denoising ------- 512x512, denoising: 0.75, steps: 10, DPM++ 2M Karras": {
+ "img2img_sampling": "DPM++ 2M Karras",
+ "img2img_steps": 10,
+ "img2img_width": 512,
+ "img2img_height": 512,
+ "img2img_batch_count": 1,
+ "img2img_batch_size": 1,
+ "img2img_cfg_scale": 7,
+ "img2img_denoising_strength": 0.75,
+ },
+ }
+
+ write_config_presets_to_file(self.img2img_config_presets, CONFIG_IMG2IMG_FILE_NAME)
+ print(f"[Config Presets] img2img config file not found, created default config at {BASEDIR}/{CONFIG_IMG2IMG_FILE_NAME}")
def title(self):
@@ -112,56 +175,83 @@ class Script(scripts.Script):
return scripts.AlwaysVisible # hide this script in the Scripts dropdown
def after_component(self, component, **kwargs):
+ # to generalize the code, detect if we are in txt2img tab or img2img tab, and then use the corresponding self variables
+ # so we can use the same code for both tabs
+ component_map = None
+ component_ids = None
+ config_file_name = None
+ if self.is_txt2img:
+ component_map = self.txt2img_component_map
+ component_ids = self.txt2img_component_ids
+ config_file_name = CONFIG_TXT2IMG_FILE_NAME
+ else:
+ component_map = self.img2img_component_map
+ component_ids = self.img2img_component_ids
+ config_file_name = CONFIG_IMG2IMG_FILE_NAME
- if component.label in self.component_map:
- self.component_map[component.label] = component
- #print(f"DEBUG: found component: {component} {component.label}")
+ #if component.label in self.component_map:
+ if component.elem_id in component_map:
+ component_map[component.elem_id] = component
+ #print(f"[Config-Presets][DEBUG]: found component: {component.elem_id} {component}")
#if component.elem_id == "script_list": #bottom of the script dropdown
#if component.elem_id == "txt2img_style2_index": #doesn't work, need to be added after all the components we edit are loaded
- if component.elem_id == "open_folder": #bottom of the image gallery
+ #if component.elem_id == "open_folder": #bottom of the image gallery
+ if component.elem_id == "txt2img_generation_info_button" or component.elem_id == "img2img_generation_info_button": #very bottom of the txt2img/img2img image gallery
+
+ #print("Creating dropdown values...")
+ #print("key/value pairs in component_map:")
+ # before we create the dropdown, we need to check if each component was found successfully to prevent errors from bricking the Web UI
+ for k, v in component_map.items():
+ #print(k,v)
+ if v is None:
+ print(f"[ERROR][Config-Presets] The component '{k}' no longer exists in the Web UI. Try updating the Config-Presets extension. This extension will not work until this issue is resolved.")
+ return
+
preset_values = []
- for k in self.config_presets:
- preset_values.append(k)
- #print(f"Config Presets: added \"{k}\"")
+ config_presets = None
+ if self.is_txt2img:
+ config_presets = self.txt2img_config_presets
+ else:
+ config_presets = self.img2img_config_presets
+
+ for dropdownValue in config_presets:
+ preset_values.append(dropdownValue)
+ #print(f"Config Presets: added \"{dropdownValue}\"")
with gr.Column(min_width=600): # pushes our stuff onto a new row at 1080p screen resolution
with gr.Row():
with gr.Column(scale=8, min_width=100) as dropdown_column:
- def config_preset_dropdown_change(dropdown_value):
- config_preset = self.config_presets[dropdown_value]
+ def config_preset_txt2img_dropdown_change(dropdown_value):
+ config_preset = config_presets[dropdown_value]
print(f"Config Presets: changed to {dropdown_value}")
- if self.is_txt2img:
- # if we are txt2img highres fix has a component
- return (config_preset["steps"] if "steps" in config_preset else self.component_map["Sampling Steps"].value,
- config_preset["sampler_index"] if "sampler_index" in config_preset else self.component_map["Sampling method"].value,
- config_preset["width"] if "width" in config_preset else self.component_map["Width"].value,
- config_preset["height"] if "height" in config_preset else self.component_map["Height"].value,
- config_preset["enable_hr"] if "enable_hr" in config_preset else self.component_map["Highres. fix"].value,
- config_preset["firstphase_width"] if "firstphase_width" in config_preset else self.component_map["Firstpass width"].value,
- config_preset["firstphase_height"] if "firstphase_height" in config_preset else self.component_map["Firstpass height"].value,
- config_preset["denoising_strength"] if "denoising_strength" in config_preset else self.component_map["Denoising strength"].value,
- config_preset["batch_count"] if "batch_count" in config_preset else self.component_map["Batch count"].value,
- config_preset["batch_size"] if "batch_size" in config_preset else self.component_map["Batch size"].value,
- config_preset["cfg_scale"] if "cfg_scale" in config_preset else self.component_map["CFG Scale"].value,
- )
+ return (config_preset["txt2img_sampling"] if "txt2img_sampling" in config_preset else component_map["txt2img_sampling"].value,
+ config_preset["txt2img_steps"] if "txt2img_steps" in config_preset else component_map["txt2img_steps"].value,
+ config_preset["txt2img_width"] if "txt2img_width" in config_preset else component_map["txt2img_width"].value,
+ config_preset["txt2img_height"] if "txt2img_height" in config_preset else component_map["txt2img_height"].value,
+ config_preset["txt2img_enable_hr"] if "txt2img_enable_hr" in config_preset else component_map["txt2img_enable_hr"].value,
+ config_preset["txt2img_hr_scale"] if "txt2img_hr_scale" in config_preset else component_map["txt2img_hr_scale"].value,
+ config_preset["txt2img_denoising_strength"] if "txt2img_denoising_strength" in config_preset else component_map["txt2img_denoising_strength"].value,
+ config_preset["txt2img_batch_count"] if "txt2img_batch_count" in config_preset else component_map["txt2img_batch_count"].value,
+ config_preset["txt2img_batch_size"] if "txt2img_batch_size" in config_preset else component_map["txt2img_batch_size"].value,
+ config_preset["txt2img_cfg_scale"] if "txt2img_cfg_scale" in config_preset else component_map["txt2img_cfg_scale"].value,
+ )
- else:
- # if we are img2img highres fix component is empty
- return (config_preset["steps"] if "steps" in config_preset else self.component_map["Sampling Steps"].value,
- config_preset["sampler_index"] if "sampler_index" in config_preset else self.component_map["Sampling method"].value,
- config_preset["width"] if "width" in config_preset else self.component_map["Width"].value,
- config_preset["height"] if "height" in config_preset else self.component_map["Height"].value,
- #config_preset["enable_hr"] if "enable_hr" in config_preset else self.component_map["Highres. fix"].value,
- #config_preset["firstphase_width"] if "firstphase_width" in config_preset else self.component_map["Firstpass width"].value,
- #config_preset["firstphase_height"] if "firstphase_height" in config_preset else self.component_map["Firstpass height"].value,
- config_preset["denoising_strength"] if "denoising_strength" in config_preset else self.component_map["Denoising strength"].value,
- config_preset["batch_count"] if "batch_count" in config_preset else self.component_map["Batch count"].value,
- config_preset["batch_size"] if "batch_size" in config_preset else self.component_map["Batch size"].value,
- config_preset["cfg_scale"] if "cfg_scale" in config_preset else self.component_map["CFG Scale"].value,
- )
+ def config_preset_img2img_dropdown_change(dropdown_value):
+ config_preset = config_presets[dropdown_value]
+ print(f"Config Presets: changed to {dropdown_value}")
+
+ return (config_preset["img2img_sampling"] if "img2img_sampling" in config_preset else component_map["img2img_sampling"].value,
+ config_preset["img2img_steps"] if "img2img_steps" in config_preset else component_map["img2img_steps"].value,
+ config_preset["img2img_width"] if "img2img_width" in config_preset else component_map["img2img_width"].value,
+ config_preset["img2img_height"] if "img2img_height" in config_preset else component_map["img2img_height"].value,
+ config_preset["img2img_batch_count"] if "img2img_batch_count" in config_preset else component_map["img2img_batch_count"].value,
+ config_preset["img2img_batch_size"] if "img2img_batch_size" in config_preset else component_map["img2img_batch_size"].value,
+ config_preset["img2img_cfg_scale"] if "img2img_cfg_scale" in config_preset else component_map["img2img_cfg_scale"].value,
+ config_preset["img2img_denoising_strength"] if "img2img_denoising_strength" in config_preset else component_map["img2img_denoising_strength"].value,
+ )
config_preset_dropdown = gr.Dropdown(
@@ -171,60 +261,63 @@ class Script(scripts.Script):
)
config_preset_dropdown.style(container=False) #set to True to give it a white box to sit in
- if self.is_txt2img:
- config_preset_dropdown.change(
- fn=config_preset_dropdown_change,
- show_progress=False,
- inputs=[config_preset_dropdown],
- outputs=[self.component_map["Sampling Steps"],
- self.component_map["Sampling method"],
- self.component_map["Width"],
- self.component_map["Height"],
- self.component_map["Highres. fix"],
- self.component_map["Firstpass width"],
- self.component_map["Firstpass height"],
- self.component_map["Denoising strength"],
- self.component_map["Batch count"],
- self.component_map["Batch size"],
- self.component_map["CFG Scale"]]
- )
- else:
- config_preset_dropdown.change(
- fn=config_preset_dropdown_change,
- show_progress=False,
- inputs=[config_preset_dropdown],
- #outputs = list([self.component_map[e] for e in AVAILABLE_COMPONENTS if e != "Seeds" and e != "Highres. fix"]) # **** LIST COMPS FAIL W/ GRADIO'S IN/OUTPUTS
- outputs=[self.component_map["Sampling Steps"],
- self.component_map["Sampling method"],
- self.component_map["Width"],
- self.component_map["Height"],
- #self.component_map["Highres. fix"], no highres fix in img2img
- #self.component_map["Firstpass width"],
- #self.component_map["Firstpass height"],
- self.component_map["Denoising strength"],
- self.component_map["Batch count"],
- self.component_map["Batch size"],
- self.component_map["CFG Scale"]]
+ #self.txt2img_config_preset_dropdown = config_preset_dropdown
+
+ try:
+ if self.is_txt2img:
+ config_preset_dropdown.change(
+ fn=config_preset_txt2img_dropdown_change,
+ show_progress=False,
+ inputs=[config_preset_dropdown],
+ outputs=[component_map["txt2img_sampling"],
+ component_map["txt2img_steps"],
+ component_map["txt2img_width"],
+ component_map["txt2img_height"],
+ component_map["txt2img_enable_hr"],
+ component_map["txt2img_hr_scale"],
+ component_map["txt2img_denoising_strength"],
+ component_map["txt2img_batch_count"],
+ component_map["txt2img_batch_size"],
+ component_map["txt2img_cfg_scale"],
+ ]
+ )
+ else:
+ config_preset_dropdown.change(
+ fn=config_preset_img2img_dropdown_change,
+ show_progress=False,
+ inputs=[config_preset_dropdown],
+ outputs=[component_map["img2img_sampling"],
+ component_map["img2img_steps"],
+ component_map["img2img_width"],
+ component_map["img2img_height"],
+ component_map["img2img_batch_count"],
+ component_map["img2img_batch_size"],
+ component_map["img2img_cfg_scale"],
+ component_map["img2img_denoising_strength"],
+ ]
)
+ except AttributeError:
+ print(traceback.format_exc()) # prints the exception stacktrace
+ print("[ERROR][CRITICAL][Config-Presets] The Config-Presets extension encountered a fatal error. A component required by this extension no longer exists in the Web UI. This is most likely due to the A1111 Web UI being updated. Try updating the Config-Presets extension. If that doesn't work, please post a bug report at https://github.com/Zyin055/Config-Presets/issues and delete your extensions/Config-Presets folder until an update is published.")
config_preset_dropdown.change(
fn=None,
inputs=[],
outputs=[],
- _js="function() { config_preset_dropdown_change() }", # JS is used to update the Highres fix row to show/hide it
+ _js="function() { config_preset_dropdown_change() }", # JS is used to update the Hires fix row to show/hide it
)
with gr.Column(scale=15, min_width=100, visible=False) as collapsable_column:
with gr.Row():
with gr.Column(scale=1, min_width=10):
def delete_selected_preset(config_preset_name):
- if config_preset_name in self.config_presets.keys():
- del self.config_presets[config_preset_name]
+ if config_preset_name in config_presets.keys():
+ del config_presets[config_preset_name]
print(f'Config Presets: deleted "{config_preset_name}"')
- self.write_config_presets_to_file()
+ write_config_presets_to_file(config_presets, config_file_name)
- preset_keys = list(self.config_presets.keys())
+ preset_keys = list(config_presets.keys())
return gr.Dropdown.update(value=preset_keys[len(preset_keys)-1], choices=preset_keys)
return gr.Dropdown.update() # do nothing if no value is selected
@@ -245,19 +338,25 @@ class Script(scripts.Script):
max_lines=1,
elem_id="config_preset_save_textbox",
)
- with gr.Column(scale=2, min_width=50):
+ with gr.Column(scale=2, min_width=60):
save_button = gr.Button(
- value="Create",
+ #value="Create",
+ value="Save & Restart",
variant="primary",
elem_id="config_preset_save_button",
)
+
save_button.click(
- fn=self.save_config(),
- inputs=list([save_textbox] + [self.component_map[comp_name] for comp_name in self.component_labels if self.component_map[comp_name] is not None]),
- outputs=[config_preset_dropdown, save_textbox],
+ fn=save_config(config_presets, component_map, config_file_name),
+ inputs=list([save_textbox] + [component_map[comp_name] for comp_name in component_ids if component_map[comp_name] is not None]),
+ #outputs=[config_preset_dropdown, save_textbox],
+ )
+ save_button.click( # need this to runa after save_config()
+ fn=None,
+ _js="config_preset_settings_restart_gradio()", # restart Gradio
)
- with gr.Column(scale=2, min_width=50):
+ with gr.Column(scale=2, min_width=55):
def open_file(f):
path = os.path.normpath(f)
@@ -278,7 +377,7 @@ class Script(scripts.Script):
elem_id="config_presets_open_config_file_button",
)
open_config_file_button.click(
- fn=lambda: open_file(f"{BASEDIR}/{CONFIG_FILE_NAME}"),
+ fn=lambda: open_file(f"{BASEDIR}/{config_file_name}"),
inputs=[],
outputs=[],
)
@@ -330,56 +429,47 @@ class Script(scripts.Script):
def run(self, p, *args):
pass
- # Save the current values on the UI to a new entry in the config file
- # Gerschel came up with the idea for this code trick
- def save_config(self):
- # closure keeps path in memory, it's a hack to get around how click or change expects values to be formatted
- def func(new_setting_name, *new_setting):
- #print(f"save_config() func() new_setting_name={new_setting_name} *new_setting={new_setting}")
- #print(f"new_setting_name={new_setting_name}")
- if new_setting_name == "":
- return gr.Dropdown.update(), "" # do nothing if no label entered in textbox
+# Save the current values on the UI to a new entry in the config file
+def save_config(config_presets, component_map, config_file_name):
+ #print("save_config()")
+ # closure keeps path in memory, it's a hack to get around how click or change expects values to be formatted
+ def func(new_setting_name, *new_setting):
+ #print(f"save_config() func() new_setting_name={new_setting_name} *new_setting={new_setting}")
+ #print(f"config_presets()={config_presets}")
+ #print(f"component_map()={component_map}")
+ #print(f"config_file_name()={config_file_name}")
- new_setting_map = {}
+ if new_setting_name == "":
+ return gr.Dropdown.update(), "" # do nothing if no label entered in textbox
- j = 0
- for i, k in enumerate(self.component_labels):
- #print(f"i={i}, j={j} k={k}") # i=1,2,3... k="Sampling Steps", "Sampling methods", ...
+ new_setting_map = {} # dict[str, Any] {"txt2img_steps": 10, ...}
- if self.is_img2img and self.component_labels[k]["not_used_in_img2img"] == True:
- #if we're in the img2img tab, skip Highres. fix, Firstpass width, Firstpass height
- #print(f"{k} is not in the img2img tab, skipping")
- continue
+ for i, component_id in enumerate(component_map.keys()):
+ if component_map[component_id] is not None:
+ new_setting_map[component_id] = new_setting[i]
- if self.component_map[k] is not None:
- internal_name = self.component_labels[k]["internal_name"]
- new_value = new_setting[j]
- #print(f"internal_name={internal_name}, new_value={new_value}")
- if k != "Sampling method":
- new_setting_map[internal_name] = new_value
- else:
- if self.is_txt2img:
- new_setting_map[internal_name] = modules.sd_samplers.samplers[new_value].name
- else:
- new_setting_map[internal_name] = modules.sd_samplers.samplers_for_img2img[new_value].name
+ config_presets.update({new_setting_name: new_setting_map})
+ write_config_presets_to_file(config_presets, config_file_name)
- j += 1
+ # print(f"self.txt2img_config_preset_dropdown.choices before =\n{self.txt2img_config_preset_dropdown.choices}")
+ # self.txt2img_config_preset_dropdown.choices = list(config_presets.keys())
+ # print(f"self.txt2img_config_preset_dropdown.choices after =\n{self.txt2img_config_preset_dropdown.choices}")
- self.config_presets.update({new_setting_name: new_setting_map})
+ print(f"[Config-Presets] Added new preset: {new_setting_name}")
+ print(f"[Config-Presets] Restarting UI...") # done in _js
+ # update the dropdown with the new config preset, and clear the 'new preset name' textbox
+ return gr.Dropdown.update(value=new_setting_name, choices=list(config_presets.keys())), ""
- self.write_config_presets_to_file()
+ # this errors when adding a 2nd config preset
+ # the solution is supposed to be updating the backend Gradio object to reflect the frontend dropdown values, but it doesn't work. still throws: "ValueError: 0 is not in list"
+ # workaround is to restart the whole UI after creating a new config preset by clicking the "Restart Gradio and Refresh Components" button in javascript
+ # https://github.com/gradio-app/gradio/discussions/2848
- #print(f"new dropdown values: {list(self.config_presets.keys())}")
- # update the dropdown with the new config preset, clear the 'new preset name' textbox
- return gr.Dropdown.update(value=new_setting_name, choices=list(self.config_presets.keys())), ""
-
- return func
+ return func
-
- def write_config_presets_to_file(self):
- json_object = json.dumps(self.config_presets, indent=4)
- with open(f"{BASEDIR}/{CONFIG_FILE_NAME}", "w") as outfile:
- outfile.write(json_object)
-
\ No newline at end of file
+def write_config_presets_to_file(config_presets, config_file_name: str):
+ json_object = json.dumps(config_presets, indent=4)
+ with open(f"{BASEDIR}/{config_file_name}", "w") as outfile:
+ outfile.write(json_object)