From 6d6c4660718291cd21a95becc09dea94f98f9db9 Mon Sep 17 00:00:00 2001 From: Zyin055 <5003647+Zyin055@users.noreply.github.com> Date: Tue, 20 Dec 2022 21:16:12 -0600 Subject: [PATCH 1/3] Added save/delete functionality in the UI for config presets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New buttons: Add/Remove..., 🗑️, Create, Open..., Cancel New textbox: New preset name --- javascript/config_presets.js | 14 +- scripts/config_presets.py | 431 +++++++++++++++++++++++++---------- 2 files changed, 318 insertions(+), 127 deletions(-) diff --git a/javascript/config_presets.js b/javascript/config_presets.js index ab9e03b..e1b05e5 100644 --- a/javascript/config_presets.js +++ b/javascript/config_presets.js @@ -4,7 +4,12 @@ //or do it our more precise way onUiUpdate(function() { //set tooltips - gradioApp().querySelectorAll("#config_presets_open_config_file_button").forEach(el => el.setAttribute("title", "[Config Presets] open config.json, requires Gradio restart after editing")) + 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_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")) + gradioApp().querySelectorAll("#config_preset_add_button").forEach(el => el.setAttribute("title", "[Config Presets] Add or remove 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 @@ -20,4 +25,11 @@ function config_preset_dropdown_change() { e.initEvent("change", true, false) highresCheckbox.dispatchEvent(e) }, 200) //50ms is too fast +} + +function config_presets_add_new_preset() { + console.log("config_presets_add_new_preset()") + const temp = prompt("name?") + console.log("returning temp="+temp) + return temp } \ No newline at end of file diff --git a/scripts/config_presets.py b/scripts/config_presets.py index 3b05eef..9775bd0 100644 --- a/scripts/config_presets.py +++ b/scripts/config_presets.py @@ -1,35 +1,64 @@ +import time + import modules.scripts as scripts +import modules.sd_samplers import gradio as gr import json import os import platform import subprocess as sp +import logging +from pprint import pprint as pp + +#logging.basicConfig(level=logging.DEBUG) basedir = scripts.basedir() #C:\Stable Diffusion\extensions\Config-Presets needs to be set in global space to get the extra 'extensions\Config-Presets' path +def write_config_presets_to_file(config_presets): + json_object = json.dumps(config_presets, indent=4) + with open(f"{basedir}/config.json", "w") as outfile: + outfile.write(json_object) + class Script(scripts.Script): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + #self.basedir = scripts.basedir() #C:\Stable Diffusion #use at global instead to get the extra 'extensions\Config-Presets' path. - component_labels = [ - "Sampling Steps", - "Sampling method", - "Width", - "Height", - "Highres. fix", - "Firstpass width", - "Firstpass height", - "Denoising strength", - "Batch count", - "Batch size", - "CFG Scale", - ] - self.component_map = {k: None for k in component_labels} # gets filled up in after_component() + # self.component_labels = [ + # "Sampling Steps", + # "Sampling method", + # "Width", + # "Height", + # "Highres. fix", + # "Firstpass width", + # "Firstpass height", + # "Denoising strength", + # "Batch count", + # "Batch size", + # "CFG Scale", + # ] + self.component_labels = { + "Sampling Steps": "steps", + "Sampling method": "sampler_index", + "Width": "width", + "Height": "height", + "Highres. fix": "enable_hr", + "Firstpass width": "firstphase_width", + "Firstpass height": "firstphase_height", + "Denoising strength": "denoising_strength", + "Batch count": "batch_count", + "Batch size": "batch_size", + "CFG Scale": "cfg_scale", + } + self.component_map = {k: None for k in self.component_labels} # gets filled up in after_component() + #print(f"init self.component_map={self.component_map}") + self.settings_file = "config.json" + self.save_as = gr.Text(render=False) # Load config from file try: @@ -41,7 +70,7 @@ class Script(scripts.Script): self.config_presets = { "Default": { }, - "Low quality ------ 512x512, steps: 10, batch size: 8, DPM++ 2M Karras" : { + "Low quality ------ 512x512, steps: 10, batch size: 8, DPM++ 2M Karras": { "steps": 10, "sampler_index": "DPM++ 2M Karras", "width": 512, @@ -50,7 +79,7 @@ class Script(scripts.Script): "batch_size": 8, "cfg_scale": 7 }, - "Medium quality - 512x512, steps: 20, batch size: 8, DPM++ 2S a Karras" : { + "Medium quality - 512x512, steps: 20, batch size: 8, DPM++ 2S a Karras": { "steps": 20, "sampler_index": "DPM++ 2S a Karras", "width": 512, @@ -59,7 +88,7 @@ class Script(scripts.Script): "batch_size": 8, "cfg_scale": 7 }, - "High quality ------ 512x512, steps: 40, batch size: 8, DPM++ 2S a Karras" : { + "High quality ------ 512x512, steps: 40, batch size: 8, DPM++ 2S a Karras": { "steps": 40, "sampler_index": "DPM++ 2S a Karras", "width": 512, @@ -68,7 +97,7 @@ class Script(scripts.Script): "batch_size": 8, "cfg_scale": 7 }, - "High res -------- 1024x1024, steps: 30, batch size: 1, DPM++ 2M Karras, [Highres fix: 512x512, Denoising: 0.4]" : { + "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, @@ -81,7 +110,7 @@ class Script(scripts.Script): "batch_size": 1, "cfg_scale": 7 }, - "Wallpaper ----- 1920x1088, steps: 30, batch size: 1, DPM++ 2M Karras, [Highres fix: 768x448, Denoising: 0.3]" : { + "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, @@ -95,10 +124,9 @@ class Script(scripts.Script): "cfg_scale": 7 } } - json_object = json.dumps(self.config_presets, indent=4) - with open(f"{basedir}/config.json", "w") as outfile: - outfile.write(json_object) - print(f"Config Presets: Config file not found, created default config at {basedir}/config.json") + write_config_presets_to_file() + print(f"Config Presets: Config file not found, created default config at {basedir}/config.json") + def title(self): return "Config Presets" @@ -122,120 +150,271 @@ class Script(scripts.Script): preset_values.append(k) #print(f"Config Presets: added \"{k}\"") - with gr.Column(scale=9): - def config_preset_dropdown_change(dropdown_value): - config_preset = self.config_presets[dropdown_value] - print(f"Config Presets: changed to {dropdown_value}") - if self.component_map["Highres. fix"]: - # 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, - ) - - 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, - ) + 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): + #print(f"config_preset_dropdown_change(dropdown_value={dropdown_value})") + config_preset = self.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, + ) + + 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, + ) - config_preset_dropdown = gr.Dropdown( - label="Config Presets", - choices=preset_values, - elem_id="config_preset_dropdown", - ) - config_preset_dropdown.style(container=False) #set to True to give it a white box to sit in - if self.component_map["Highres. fix"]: - config_preset_dropdown.change( - fn=config_preset_dropdown_change, - show_progress=False, - inputs=[config_preset_dropdown], - #outputs=[ui_steps, ui_sampler_index, ui_width, ui_height, ui_enable_hr, ui_denoising_strength, ui_batch_count, ui_batch_size, ui_cfg_scale], - 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"]] + config_preset_dropdown = gr.Dropdown( + label="Config Presets", + choices=preset_values, + elem_id="config_preset_dropdown", ) - - 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 - ) + config_preset_dropdown.style(container=False) #set to True to give it a white box to sit in + if self.component_map["Highres. fix"]: + config_preset_dropdown.change( + fn=config_preset_dropdown_change, + show_progress=False, + inputs=[config_preset_dropdown], + #outputs=[ui_steps, ui_sampler_index, ui_width, ui_height, ui_enable_hr, ui_denoising_strength, ui_batch_count, ui_batch_size, ui_cfg_scale], + 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"]] + ) - with gr.Column(scale=1, min_width=30): - def open_file(f): - path = os.path.normpath(f) + 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 + ) + with gr.Column(scale=15, min_width=100, visible=False) as save_column: + with gr.Row(): + with gr.Column(scale=1, min_width=10): - if not os.path.exists(path): - print(f'Config Presets: The file at "{path}" does not exist.') - return + def delete_selected_preset(config_preset_name): + if config_preset_name in self.config_presets.keys(): - # copied from ui.py:538 - if platform.system() == "Windows": - os.startfile(path) - elif platform.system() == "Darwin": - sp.Popen(["open", path]) - else: - sp.Popen(["xdg-open", path]) + del self.config_presets[config_preset_name] + print(f'Config Presets: deleted "{config_preset_name}"') + #print(f"new self.config_presets={self.config_presets}") - open_config_file_button = gr.Button( # tooltip is set in javascript/config_presets.js - value="Edit...", - elem_id="config_presets_open_config_file_button" - ) - open_config_file_button.click( - fn=lambda: open_file(f"{basedir}/config.json"), - inputs=[], - outputs=[], - ) + write_config_presets_to_file(self.config_presets) + + preset_keys = list(self.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 + + trash_button = gr.Button( + value="\U0001F5D1", + elem_id="config_preset_trash_button", + ) + trash_button.click( + fn=delete_selected_preset, + inputs=[config_preset_dropdown], + outputs=[config_preset_dropdown], + ) + with gr.Column(scale=10, min_width=100): + save_textbox = gr.Textbox( + label="New preset name", + placeholder="Ex: Low quality", + #value="My Preset", + max_lines=1, + elem_id="config_preset_save_textbox", + ) + with gr.Column(scale=2, min_width=50): + save_button = gr.Button( + value="Create", + 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], + ) + + with gr.Column(scale=2, min_width=50): + def open_file(f): + path = os.path.normpath(f) + + if not os.path.exists(path): + print(f'Config Presets: The file at "{path}" does not exist.') + return + + # copied from ui.py:538 + if platform.system() == "Windows": + os.startfile(path) + elif platform.system() == "Darwin": + sp.Popen(["open", path]) + else: + sp.Popen(["xdg-open", path]) + + open_config_file_button = gr.Button( # tooltip is set in javascript/config_presets.js + value="Open...", + elem_id="config_presets_open_config_file_button", + # visible=False, + ) + open_config_file_button.click( + fn=lambda: open_file(f"{basedir}/config.json"), + inputs=[], + outputs=[], + ) + + with gr.Column(scale=2, min_width=50): + cancel_button = gr.Button( + value="Cancel", + elem_id="config_preset_cancel_save_button", + ) + + with gr.Column(scale=1, min_width=120, visible=True) as add_column: + add_remove_button = gr.Button( + value="Add/Remove...", + elem_id="config_preset_add_button", + ) + + + def add_remove_button_click(): + return gr.update(visible=True), gr.update(visible=False) + + def save_button_click(save_text): + if save_text == "": + return gr.update(), gr.update() + return gr.update(visible=True), gr.update(visible=False) + + def cancel_button_click(): + return gr.update(visible=True), gr.update(visible=False) + + add_remove_button.click( + fn=add_remove_button_click, + inputs=[], + outputs=[save_column, add_column], + ) + save_button.click( + fn=save_button_click, + inputs=[save_textbox], + outputs=[add_column, save_column], + ) + cancel_button.click( + fn=cancel_button_click, + inputs=[], + outputs=[add_column, save_column], + ) + + + + + #with gr.Column(scale=1, min_width=30) as edit_column: def ui(self, is_img2img): pass def run(self, p, *args): pass + + def save_config(self): + """ + Helper function to utilize closure + """ + + + # 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): + """ + Formats setting and overwrites file + input: setting_name is text autoformatted from clicks input + new_settings is a tuple (by using packing) of formatted text, outputs from + click method must be in same order of labels + """ + + print(f"save_config() in python") + #print(f"save_config() func() setting_name={setting_name} *new_setting={new_setting}") + + if new_setting_name == "": + return gr.Dropdown.update(), "" # do nothing + + # Format new_setting from tuple of values, and map them to their label + # Using presence of hires button to determine if we are img2img or txt2img + # if self.is_txt2img: + # new_setting = {k:new_setting[i] if k != "Sampling method" else modules.sd_samplers.samplers[new_setting[i]].name for i, k in enumerate(self.component_labels) if self.component_map[k] is not None} + # else: + # new_setting = {k:new_setting[i] if k != "Sampling method" else modules.sd_samplers.samplers_for_img2img[new_setting[i]].name for i, k in enumerate(self.component_labels) if self.component_map[k] is not None} + + new_setting_map = {} + + for i, k in enumerate(self.component_labels): + #print(f"i={i}, k={k}") # i=1,2,3... k="Sampling Steps", "Sampling methods", etc + if self.component_map[k] is not None: + internal_name = self.component_labels[k] + new_value = new_setting[i] + #print(f"internal_name={internal_name}, new_value={new_value}") + if k != "Sampling method": + #pass + 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 + + print(f"new_setting_name={new_setting_name}") + self.config_presets.update({new_setting_name: new_setting_map}) + + write_config_presets_to_file(self.config_presets) + + #print(f"new dropdown values: {list(self.config_presets.keys())}") + return gr.Dropdown.update(value=new_setting_name, choices=list(self.config_presets.keys())), "" + + + return func From c84d4b61a083f40f3e104021dbdc9c1b3d7d04e7 Mon Sep 17 00:00:00 2001 From: Zyin055 <5003647+Zyin055@users.noreply.github.com> Date: Tue, 20 Dec 2022 22:05:47 -0600 Subject: [PATCH 2/3] img2img tab support and code cleanup --- javascript/config_presets.js | 11 +-- scripts/config_presets.py | 135 +++++++++++++++-------------------- 2 files changed, 58 insertions(+), 88 deletions(-) diff --git a/javascript/config_presets.js b/javascript/config_presets.js index e1b05e5..49d3f76 100644 --- a/javascript/config_presets.js +++ b/javascript/config_presets.js @@ -1,12 +1,12 @@ //add tooltip by piggybacking off of javascript/hints.js -//titles["Edit..."] = "[Config Presets] open config.json" +//titles["Add/Remove..."] = "[Config Presets] Add or remove a preset" //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_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")) + gradioApp().querySelectorAll("#config_preset_save_button").forEach(el => el.setAttribute("title", "Saves current settings with the new preset name. This will save: Steps, Sampler, Width/Height, Highres fix, Firstpass width/height, Denoising strength, Batch size, CFG Scale.")) gradioApp().querySelectorAll("#config_preset_add_button").forEach(el => el.setAttribute("title", "[Config Presets] Add or remove 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")) @@ -25,11 +25,4 @@ function config_preset_dropdown_change() { e.initEvent("change", true, false) highresCheckbox.dispatchEvent(e) }, 200) //50ms is too fast -} - -function config_presets_add_new_preset() { - console.log("config_presets_add_new_preset()") - const temp = prompt("name?") - console.log("returning temp="+temp) - return temp } \ No newline at end of file diff --git a/scripts/config_presets.py b/scripts/config_presets.py index 9775bd0..512c9c9 100644 --- a/scripts/config_presets.py +++ b/scripts/config_presets.py @@ -1,5 +1,3 @@ -import time - import modules.scripts as scripts import modules.sd_samplers import gradio as gr @@ -7,41 +5,23 @@ import json import os import platform import subprocess as sp -import logging -from pprint import pprint as pp - -#logging.basicConfig(level=logging.DEBUG) basedir = scripts.basedir() #C:\Stable Diffusion\extensions\Config-Presets needs to be set in global space to get the extra 'extensions\Config-Presets' path -def write_config_presets_to_file(config_presets): - json_object = json.dumps(config_presets, indent=4) - with open(f"{basedir}/config.json", "w") as outfile: - outfile.write(json_object) + class Script(scripts.Script): + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.config_file_name = "config.json" - #self.basedir = scripts.basedir() #C:\Stable Diffusion #use at global instead to get the extra 'extensions\Config-Presets' path. - - # self.component_labels = [ - # "Sampling Steps", - # "Sampling method", - # "Width", - # "Height", - # "Highres. fix", - # "Firstpass width", - # "Firstpass height", - # "Denoising strength", - # "Batch count", - # "Batch size", - # "CFG Scale", - # ] + # 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 = { "Sampling Steps": "steps", "Sampling method": "sampler_index", @@ -55,14 +35,19 @@ class Script(scripts.Script): "Batch size": "batch_size", "CFG Scale": "cfg_scale", } - self.component_map = {k: None for k in self.component_labels} # gets filled up in after_component() - #print(f"init self.component_map={self.component_map}") - self.settings_file = "config.json" - self.save_as = gr.Text(render=False) + # These are the components that are NOT found in the img2img tab + self.components_not_in_img2img_tab = [ + "Highres. fix", + "Firstpass width", + "Firstpass height", + ] - # Load config from file + # 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 + + # Load config file try: - with open(f"{basedir}/config.json") as file: + with open(f"{basedir}/{self.config_file_name}") as file: self.config_presets = json.load(file) except FileNotFoundError: # Config file not found @@ -124,8 +109,17 @@ class Script(scripts.Script): "cfg_scale": 7 } } - write_config_presets_to_file() - print(f"Config Presets: Config file not found, created default config at {basedir}/config.json") + + self.write_config_presets_to_file() + print(f"Config Presets: Config file not found, created default config at {basedir}/{self.config_file_name}") + + + + def write_config_presets_to_file(self): + json_object = json.dumps(self.config_presets, indent=4) + with open(f"{basedir}/{self.config_file_name}", "w") as outfile: + outfile.write(json_object) + def title(self): @@ -137,7 +131,6 @@ class Script(scripts.Script): def after_component(self, component, **kwargs): - if component.label in self.component_map: self.component_map[component.label] = component #print(f"DEBUG: found component: {component} {component.label}") @@ -155,7 +148,6 @@ class Script(scripts.Script): with gr.Row(): with gr.Column(scale=8, min_width=100) as dropdown_column: def config_preset_dropdown_change(dropdown_value): - #print(f"config_preset_dropdown_change(dropdown_value={dropdown_value})") config_preset = self.config_presets[dropdown_value] print(f"Config Presets: changed to {dropdown_value}") @@ -196,7 +188,9 @@ class Script(scripts.Script): elem_id="config_preset_dropdown", ) config_preset_dropdown.style(container=False) #set to True to give it a white box to sit in - if self.component_map["Highres. fix"]: + + if self.is_txt2img: + #if self.component_map["Highres. fix"]: config_preset_dropdown.change( fn=config_preset_dropdown_change, show_progress=False, @@ -239,18 +233,16 @@ class Script(scripts.Script): outputs=[], _js="function() { config_preset_dropdown_change() }", # JS is used to update the Highres fix row to show/hide it ) - with gr.Column(scale=15, min_width=100, visible=False) as save_column: + 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] print(f'Config Presets: deleted "{config_preset_name}"') - #print(f"new self.config_presets={self.config_presets}") - write_config_presets_to_file(self.config_presets) + self.write_config_presets_to_file() preset_keys = list(self.config_presets.keys()) return gr.Dropdown.update(value=preset_keys[len(preset_keys)-1], choices=preset_keys) @@ -301,13 +293,12 @@ class Script(scripts.Script): else: sp.Popen(["xdg-open", path]) - open_config_file_button = gr.Button( # tooltip is set in javascript/config_presets.js + open_config_file_button = gr.Button( value="Open...", elem_id="config_presets_open_config_file_button", - # visible=False, ) open_config_file_button.click( - fn=lambda: open_file(f"{basedir}/config.json"), + fn=lambda: open_file(f"{basedir}/{self.config_file_name}"), inputs=[], outputs=[], ) @@ -318,7 +309,7 @@ class Script(scripts.Script): elem_id="config_preset_cancel_save_button", ) - with gr.Column(scale=1, min_width=120, visible=True) as add_column: + with gr.Column(scale=1, min_width=120, visible=True) as add_remove_button_column: add_remove_button = gr.Button( value="Add/Remove...", elem_id="config_preset_add_button", @@ -339,68 +330,53 @@ class Script(scripts.Script): add_remove_button.click( fn=add_remove_button_click, inputs=[], - outputs=[save_column, add_column], + outputs=[collapsable_column, add_remove_button_column], ) save_button.click( fn=save_button_click, inputs=[save_textbox], - outputs=[add_column, save_column], + outputs=[add_remove_button_column, collapsable_column], ) cancel_button.click( fn=cancel_button_click, inputs=[], - outputs=[add_column, save_column], + outputs=[add_remove_button_column, collapsable_column], ) - - - #with gr.Column(scale=1, min_width=30) as edit_column: - def ui(self, is_img2img): pass 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): - """ - Helper function to utilize closure - """ - - # 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): - """ - Formats setting and overwrites file - input: setting_name is text autoformatted from clicks input - new_settings is a tuple (by using packing) of formatted text, outputs from - click method must be in same order of labels - """ - - print(f"save_config() in python") - #print(f"save_config() func() setting_name={setting_name} *new_setting={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 - - # Format new_setting from tuple of values, and map them to their label - # Using presence of hires button to determine if we are img2img or txt2img - # if self.is_txt2img: - # new_setting = {k:new_setting[i] if k != "Sampling method" else modules.sd_samplers.samplers[new_setting[i]].name for i, k in enumerate(self.component_labels) if self.component_map[k] is not None} - # else: - # new_setting = {k:new_setting[i] if k != "Sampling method" else modules.sd_samplers.samplers_for_img2img[new_setting[i]].name for i, k in enumerate(self.component_labels) if self.component_map[k] is not None} + return gr.Dropdown.update(), "" # do nothing if no label entered in textbox new_setting_map = {} + j = 0 for i, k in enumerate(self.component_labels): - #print(f"i={i}, k={k}") # i=1,2,3... k="Sampling Steps", "Sampling methods", etc + #print(f"i={i}, j={j} k={k}") # i=1,2,3... k="Sampling Steps", "Sampling methods", ... + + if self.is_img2img and k in self.components_not_in_img2img_tab: + #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 + if self.component_map[k] is not None: internal_name = self.component_labels[k] - new_value = new_setting[i] + new_value = new_setting[j] #print(f"internal_name={internal_name}, new_value={new_value}") if k != "Sampling method": - #pass new_setting_map[internal_name] = new_value else: if self.is_txt2img: @@ -408,13 +384,14 @@ class Script(scripts.Script): else: new_setting_map[internal_name] = modules.sd_samplers.samplers_for_img2img[new_value].name - print(f"new_setting_name={new_setting_name}") + j += 1 + self.config_presets.update({new_setting_name: new_setting_map}) - write_config_presets_to_file(self.config_presets) + self.write_config_presets_to_file() #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 From aa58d6579a5886913300bd722739b660f31199e1 Mon Sep 17 00:00:00 2001 From: Zyin055 <5003647+Zyin055@users.noreply.github.com> Date: Wed, 21 Dec 2022 16:52:30 -0600 Subject: [PATCH 3/3] final touches component_labels data structure change code cleanup --- javascript/config_presets.js | 4 +-- scripts/config_presets.py | 66 +++++++++++++++--------------------- 2 files changed, 29 insertions(+), 41 deletions(-) diff --git a/javascript/config_presets.js b/javascript/config_presets.js index 49d3f76..66c80c6 100644 --- a/javascript/config_presets.js +++ b/javascript/config_presets.js @@ -6,8 +6,8 @@ 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_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. This will save: Steps, Sampler, Width/Height, Highres fix, Firstpass width/height, Denoising strength, Batch size, CFG Scale.")) - gradioApp().querySelectorAll("#config_preset_add_button").forEach(el => el.setAttribute("title", "[Config Presets] Add or remove a preset")) + 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_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")) }) diff --git a/scripts/config_presets.py b/scripts/config_presets.py index 512c9c9..2555f66 100644 --- a/scripts/config_presets.py +++ b/scripts/config_presets.py @@ -7,47 +7,37 @@ import platform 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 - - +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" class Script(scripts.Script): - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.config_file_name = "config.json" - # 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 = { - "Sampling Steps": "steps", - "Sampling method": "sampler_index", - "Width": "width", - "Height": "height", - "Highres. fix": "enable_hr", - "Firstpass width": "firstphase_width", - "Firstpass height": "firstphase_height", - "Denoising strength": "denoising_strength", - "Batch count": "batch_count", - "Batch size": "batch_size", - "CFG Scale": "cfg_scale", + 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}, } - # These are the components that are NOT found in the img2img tab - self.components_not_in_img2img_tab = [ - "Highres. fix", - "Firstpass width", - "Firstpass height", - ] # 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 # Load config file try: - with open(f"{basedir}/{self.config_file_name}") as file: + with open(f"{BASEDIR}/{CONFIG_FILE_NAME}") as file: self.config_presets = json.load(file) except FileNotFoundError: # Config file not found @@ -111,15 +101,7 @@ class Script(scripts.Script): } self.write_config_presets_to_file() - print(f"Config Presets: Config file not found, created default config at {basedir}/{self.config_file_name}") - - - - def write_config_presets_to_file(self): - json_object = json.dumps(self.config_presets, indent=4) - with open(f"{basedir}/{self.config_file_name}", "w") as outfile: - outfile.write(json_object) - + print(f"Config Presets: Config file not found, created default config at {BASEDIR}/{CONFIG_FILE_NAME}") def title(self): @@ -190,12 +172,10 @@ 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: - #if self.component_map["Highres. fix"]: config_preset_dropdown.change( fn=config_preset_dropdown_change, show_progress=False, inputs=[config_preset_dropdown], - #outputs=[ui_steps, ui_sampler_index, ui_width, ui_height, ui_enable_hr, ui_denoising_strength, ui_batch_count, ui_batch_size, ui_cfg_scale], outputs=[self.component_map["Sampling Steps"], self.component_map["Sampling method"], self.component_map["Width"], @@ -298,7 +278,7 @@ class Script(scripts.Script): elem_id="config_presets_open_config_file_button", ) open_config_file_button.click( - fn=lambda: open_file(f"{basedir}/{self.config_file_name}"), + fn=lambda: open_file(f"{BASEDIR}/{CONFIG_FILE_NAME}"), inputs=[], outputs=[], ) @@ -367,13 +347,13 @@ class Script(scripts.Script): 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", ... - if self.is_img2img and k in self.components_not_in_img2img_tab: + 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 if self.component_map[k] is not None: - internal_name = self.component_labels[k] + 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": @@ -395,3 +375,11 @@ class Script(scripts.Script): return gr.Dropdown.update(value=new_setting_name, choices=list(self.config_presets.keys())), "" 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