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)