diff --git a/CHANGELOG.md b/CHANGELOG.md index 06fed188a..d61c80414 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,12 @@ ## Update for 2023-09-20 -- Added **change log** to UI - see *System -> Changelog* +- **UI** + - added **change log** to UI + see *System -> Changelog* + - converted submenus from checkboxes to accordion elements + any ui state including state of open/closed menus can be saved as default! + see *System -> User interface -> Set menu states* - **Extra networks**: - faster search, ability to show/hide/sort networks - refactored subfolder handling @@ -16,8 +20,9 @@ - allow upscale-only as part of **txt2img** and **img2img** workflows simply set *denoising strength* to 0 so hires does not get triggered - **Samplers**: + - moved ui options to submenu - default list for new installs is now all samplers, list can be modified in settings - - simplified samplers configuration in settings + - simplified samplers configuration in settings - **Diffusers** - better pipeline auto-detect when loading from safetensors diff --git a/html/locale_en.json b/html/locale_en.json index 36d55d0e8..16ddb76f5 100644 --- a/html/locale_en.json +++ b/html/locale_en.json @@ -115,7 +115,7 @@ {"id":"","label":"Apply changes & restart server","localized":"","hint":"Apply all changes and restart server"}, {"id":"","label":"install","localized":"","hint":"install this extension"}, {"id":"","label":"uninstall","localized":"","hint":"uninstall this extension"}, - {"id":"","label":"UI Config","localized":"","hint":"Review and set current values as default values for the user interface"}, + {"id":"","label":"User interface","localized":"","hint":"Review and set current values as default values for the user interface"}, {"id":"","label":"View changes","localized":"","hint":"Review changes between default user interface values and current values"}, {"id":"","label":"Set new defaults","localized":"","hint":"Set current values as default values for the user interface"}, {"id":"","label":"Benchmark","localized":"","hint":"Run benchmarks"}, @@ -124,7 +124,7 @@ ], "txt2img tab": [ {"id":"","label":"Batch","localized":"","hint":"Additional batching options"}, - {"id":"","label":"Seed details","localized":"","hint":"Additional options regarding initial seed used to produce images"}, + {"id":"","label":"Sampler","localized":"","hint":"Additional sampling options"}, {"id":"","label":"Advanced","localized":"","hint":"Additional advanced options"}, {"id":"","label":"Sampling method","localized":"","hint":"Which algorithm to use to produce the image"}, {"id":"","label":"Sampling steps","localized":"","hint":"How many times to improve the generated image iteratively; higher values take longer; very low values can produce bad results"}, diff --git a/javascript/black-teal.css b/javascript/black-teal.css index 9bbcc4276..f4907f706 100644 --- a/javascript/black-teal.css +++ b/javascript/black-teal.css @@ -78,7 +78,7 @@ svg.feather.feather-image, .feather .feather-image { display: none } .tabs { background-color: var(--background-color); } .block.token-counter span { background-color: var(--input-background-fill) !important; box-shadow: 2px 2px 2px #111; border: none !important; font-size: 0.8rem; } .tab-nav { zoom: 120%; margin-top: 10px; margin-bottom: 10px; border-bottom: 2px solid var(--highlight-color) !important; padding-bottom: 2px; } -.label-wrap { margin: 16px 0px 8px 0px; } +.label-wrap { margin: 8px 0px 4px 0px; } .gradio-slider input[type="number"] { width: 4.5em; font-size: 0.8rem; height: 20px; } .gradio-button.tool { border: none; background: none; box-shadow: none; filter: hue-rotate(340deg) saturate(0.5); } #tab_extensions table td, #tab_extensions table th, #tab_config table td, #tab_config table th { border: none; padding: 0.5em; } @@ -107,8 +107,10 @@ svg.feather.feather-image, .feather .feather-image { display: none } #quicksettings .gr-button-tool { font-size: 1.6rem; box-shadow: none; margin-left: -20px; margin-top: -2px; height: 2.4em; } #open_folder_extras, #footer, #style_pos_col, #style_neg_col, #roll_col, #extras_upscaler_2, #extras_upscaler_2_visibility, #txt2img_seed_resize_from_w, #txt2img_seed_resize_from_h { display: none; } #save-animation { border-radius: var(--radius-sm) !important; margin-bottom: 16px; background-color: #111111; } -#script_list { padding: 4px; margin-top: 20px; margin-bottom: 20px; } +#script_list { padding: 4px; margin-top: 16px; margin-bottom: 16px; } #settings > div.flex-wrap { width: 15em; } +#settings_search { margin-top: 1em; margin-left: 1em; } +#settings_search textarea { padding: 0.5em; height: 2.2em !important; } #txt2img_cfg_scale { min-width: 200px; } #txt2img_checkboxes, #img2img_checkboxes { background-color: transparent; } #txt2img_checkboxes, #img2img_checkboxes { margin-bottom: 0.2em; } diff --git a/javascript/hires.js b/javascript/hires.js index 7601b1972..e0bd546dc 100644 --- a/javascript/hires.js +++ b/javascript/hires.js @@ -1,4 +1,4 @@ -function onCalcResolutionHires(enable_hr, width, height, hr_scale, hr_resize_x, hr_resize_y, hr_upscaler) { +function onCalcResolutionHires(width, height, hr_scale, hr_resize_x, hr_resize_y, hr_upscaler) { const setInactive = (elem, inactive) => elem.classList.toggle('inactive', !!inactive); const hrUpscaleBy = gradioApp().getElementById('txt2img_hr_scale'); const hrResizeX = gradioApp().getElementById('txt2img_hr_resize_x'); @@ -6,5 +6,5 @@ function onCalcResolutionHires(enable_hr, width, height, hr_scale, hr_resize_x, setInactive(hrUpscaleBy, hr_resize_x > 0 || hr_resize_y > 0); setInactive(hrResizeX, hr_resize_x === 0); setInactive(hrResizeY, hr_resize_y === 0); - return [enable_hr, width, height, hr_scale, hr_resize_x, hr_resize_y, hr_upscaler]; + return [width, height, hr_scale, hr_resize_x, hr_resize_y, hr_upscaler]; } diff --git a/javascript/style.css b/javascript/style.css index 4c4950fe7..e9b8b494e 100644 --- a/javascript/style.css +++ b/javascript/style.css @@ -9,7 +9,7 @@ div.compact{ gap: 1em; } div.gradio-html.min{ min-height: 0; } .block.gradio-checkbox { margin: 0.75em 1.5em 0 0; align-self: center; } .block.gradio-dropdown, .block.gradio-slider, .block.gradio-checkbox, .block.gradio-textbox, .block.gradio-radio, .block.gradio-checkboxgroup, .block.gradio-number, .block.gradio-colorpicker { border-width: 0 !important; box-shadow: none !important;} -.block.padded:not(.gradio-accordion) { padding: 0 !important; margin-right: 0; min-width: 100px !important; } +.block.padded:not(.gradio-accordion) { padding: 0 !important; margin-right: 0; min-width: 90px !important; } .compact{ background: transparent !important; padding: 0 !important; } .dark .gradio-dropdown ul.options li.item:not(:has(.hide)) { background-color: var(--neutral-900); } .gap.compact{ padding: 0; gap: 0.2em 0; } @@ -27,6 +27,12 @@ div.gradio-html.min{ min-height: 0; } .gradio-html div.wrap{ height: 100%; } .gradio-slider input[type="number"]{ width: 6em; margin-left: 0.5em; } .gradio-accordion { padding-top: var(--spacing-md) !important; padding-right: 0 !important; padding-bottom: 0 !important; color: var(--body-text-color); } +.gradio-accordion .label-wrap .icon { color: var(--button-primary-border-color); } +.settings-accordion >div { flex-flow: wrap; } +.settings-accordion .gap { padding-right: 1000px; } +.small-accordion { width: fit-content !important; padding-left: 0 !important; } +.small-accordion .form { min-width: var(--left-column) !important; } +.small-accordion .label-wrap .icon { margin-right: 1.6em; margin-left: 0.6em; color: var(--button-primary-border-color); } .hidden { display: none; } footer { display: none; } td { border-bottom: none !important; } @@ -217,12 +223,12 @@ table.settings-value-table td { padding: 0.4em; border: 1px solid #ccc; max-widt #tab_extensions button{ max-width: 16em; } #tab_extensions input[disabled="disabled"]{ opacity: 0.5; } .extension-tag{ font-weight: bold; font-size: 95%; } +.extension-button { font-size: 95% !important; width: 6em; } #extensions .name{ font-size: 1.1rem } #extensions .type{ opacity: 0.5; font-size: 90%; text-align: center; } #extensions .version{ opacity: 0.7; } #extensions .info{ margin: 0; } #extensions .date{ opacity: 0.85; font-size: 90%; } -.extension-button { font-size: 95% !important; width: 6em; } /* extra networks */ .extra-networks > div { margin: 0; gap: 0.2em; border-bottom: none !important; } @@ -272,13 +278,13 @@ div.controlnet_main_options { display: grid; grid-template-columns: 1fr 1fr; gri #scripts_alwayson_txt2img > .label-wrap, #scripts_alwayson_img2img > .label-wrap { background: var(--input-background-fill); padding: 0; margin: 0; border-radius: var(--radius-lg); } #scripts_alwayson_txt2img > .label-wrap > span, #scripts_alwayson_img2img > .label-wrap > span { padding: var(--spacing-xxl); } #script_txt2img_agent_scheduler { display: none; } - #extras_generate, #extras_interrupt, #extras_skip { display: block !important; position: relative; height: 36px; } #extras_upscale { margin-top: 10px } #refresh_tac_refreshTempFiles { display: none; } #train_tab { flex-flow: row-reverse; } #models_tab { flex-flow: row-reverse; } #swap_axes > button { min-width: 100px; font-size: 1em; } +#ui_defaults_review { margin: 1em; } .log-monitor { display: none; justify-content: unset !important; overflow: hidden; padding: 0; margin-top: auto; font-family: monospace; font-size: 0.85em; } .log-monitor td, .log-monitor th { padding-left: 1em; } @@ -303,7 +309,6 @@ div.controlnet_main_options { display: grid; grid-template-columns: 1fr 1fr; gri .loader::before { border-top-color: var(--primary-900); animation: 3s spin linear infinite; } .loader::after { border-top-color: var(--primary-300); animation: spin 1.5s linear infinite; } - @keyframes move { from { background-position-x: 0, -40px; } to { background-position-x: 0, 40px; } diff --git a/javascript/ui.js b/javascript/ui.js index 232bb5eb1..f03945179 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -143,7 +143,7 @@ function clearGallery(tabname) { footer.style.display = 'flex'; } -function submit(...args) { +function submit_txt2img(...args) { log('submitTxt'); clearGallery('txt2img'); const id = randomId(); @@ -170,6 +170,8 @@ function submit_postprocessing(...args) { return args; } +const submit = submit_txt2img; + function modelmerger(...args) { const id = randomId(); const res = create_submit_args(args); diff --git a/javascript/uiConfig.js b/javascript/uiConfig.js new file mode 100644 index 000000000..bb1017d4e --- /dev/null +++ b/javascript/uiConfig.js @@ -0,0 +1,22 @@ +function uiOpenSubmenus() { + const accordions = Array.from(gradioApp().querySelectorAll('.gradio-accordion')); + const states = {}; + accordions.forEach((el) => { + const name = el.querySelector('.label-wrap > span:not(.icon)').innerText.trim(); + const children = Array.from(el.childNodes); + const open = children.filter((c) => c.style?.display === 'block'); + if (states[name] === undefined) states[name] = open.length > 0; + }); + return states; +} + +onUiLoaded(() => { + const btn = gradioApp().getElementById('ui_defaults_view'); + console.log('HERE', btn); + if (!btn) return; + const intersectionObserver = new IntersectionObserver((entries) => { + if (entries[0].intersectionRatio <= 0) {} + if (entries[0].intersectionRatio > 0) btn.click(); + }); + intersectionObserver.observe(btn); // monitor visibility of tab +}); diff --git a/modules/scripts_postprocessing.py b/modules/scripts_postprocessing.py index 3c8720fc5..75b43887c 100644 --- a/modules/scripts_postprocessing.py +++ b/modules/scripts_postprocessing.py @@ -88,7 +88,7 @@ class ScriptPostprocessingRunner: def setup_ui(self): inputs = [] for script in self.scripts_in_preferred_order(): - with gr.Row() as group: + with gr.Accordion(label=script.name) as group: self.create_script_ui(script, inputs) script.group = group self.ui_created = True diff --git a/modules/sd_samplers_diffusers.py b/modules/sd_samplers_diffusers.py index ec9388588..59e942ffd 100644 --- a/modules/sd_samplers_diffusers.py +++ b/modules/sd_samplers_diffusers.py @@ -35,8 +35,8 @@ config = { 'Euler a': { }, 'Euler': { 'interpolation_type': "linear", 'use_karras_sigmas': False }, 'Heun': { 'use_karras_sigmas': False }, - 'KDPM2 a': { 'steps_offset': 0 }, 'KDPM2': { 'steps_offset': 0 }, + 'KDPM2 a': { 'steps_offset': 0 }, 'LMSD': { 'use_karras_sigmas': False, 'timestep_spacing': 'linspace', 'steps_offset': 0 }, 'PNDM': { 'skip_prk_steps': False, 'set_alpha_to_one': False, 'steps_offset': 0 }, 'UniPC': { 'solver_order': 2, 'thresholding': False, 'sample_max_value': 1.0, 'predict_x0': 'bh2', 'lower_order_final': True }, diff --git a/modules/ui.py b/modules/ui.py index 6cfecd97a..47c67feb5 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -11,9 +11,9 @@ from PIL import Image from modules.call_queue import wrap_gradio_gpu_call, wrap_queued_call, wrap_gradio_call from modules import sd_hijack, sd_models, script_callbacks, ui_extensions, deepbooru, extra_networks, ui_common, ui_postprocessing, ui_loadsave, ui_train, ui_models -from modules.ui_components import FormRow, FormColumn, FormGroup, ToolButton, FormHTML # pylint: disable=unused-import +from modules.ui_components import FormRow, FormGroup, ToolButton, FormHTML from modules.paths import script_path, data_path -from modules.shared import opts, cmd_opts, readfile +from modules.shared import opts, cmd_opts from modules.dml import directml_override_opts from modules import prompt_parser from modules import timer @@ -77,10 +77,8 @@ def add_style(name: str, prompt: str, negative_prompt: str): return [gr.Dropdown.update(visible=True, choices=list(modules.shared.prompt_styles.styles)) for _ in range(2)] -def calc_resolution_hires(enable, width, height, hr_scale, hr_resize_x, hr_resize_y, hr_upscaler): +def calc_resolution_hires(width, height, hr_scale, hr_resize_x, hr_resize_y, hr_upscaler): from modules import processing, devices - if not enable: - return "" if hr_upscaler == "None": return "Hires resize: None" p = processing.StableDiffusionProcessingTxt2Img(width=width, height=height, enable_hr=True, hr_scale=hr_scale, hr_resize_x=hr_resize_x, hr_resize_y=hr_resize_y) @@ -143,10 +141,10 @@ def interrogate_deepbooru(image): return gr.update() if prompt is None else prompt -def create_seed_inputs(tab, visible): - with FormGroup(visible=visible, elem_id=f"{tab}_seed_group") as seed_group: +def create_seed_inputs(tab): + with gr.Accordion(open=False, label="Seed", elem_id=f"{tab}_seed_group", elem_classes=["small-accordion"]): with FormRow(elem_id=f"{tab}_seed_row", variant="compact"): - seed = gr.Number(label='Seed', value=-1, elem_id=f"{tab}_seed") + seed = gr.Number(label='Initial seed', value=-1, elem_id=f"{tab}_seed") seed.style(container=False) random_seed = ToolButton(symbols.random, elem_id=f"{tab}_random_seed", label='Random seed') reuse_seed = ToolButton(symbols.reuse, elem_id=f"{tab}_reuse_seed", label='Reuse seed') @@ -161,7 +159,7 @@ def create_seed_inputs(tab, visible): seed_resize_from_h = gr.Slider(minimum=0, maximum=4096, step=8, label="Resize seed from height", value=0, elem_id=f"{tab}_seed_resize_from_h") random_seed.click(fn=lambda: [-1, -1], show_progress=False, inputs=[], outputs=[seed, subseed]) random_subseed.click(fn=lambda: -1, show_progress=False, inputs=[], outputs=[subseed]) - return seed_group, seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w + return seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w def connect_clear_prompt(button): @@ -268,7 +266,6 @@ def create_toprow(is_img2img): negative_token_button = gr.Button(visible=False, elem_id=f"{id_part}_negative_token_button") with gr.Row(elem_id=f"{id_part}_styles_row"): prompt_styles = gr.Dropdown(label="Styles", elem_id=f"{id_part}_styles", choices=[style.name for style in modules.shared.prompt_styles.styles.values()], value=[], multiselect=True) - # create_refresh_button(prompt_styles, modules.shared.prompt_styles.reload, lambda: {"choices": [k for k, v in modules.shared.prompt_styles.styles.items()]}, f"refresh_{id_part}_styles") prompt_styles_btn = gr.Button('Apply', elem_id=f"{id_part}_styles_select", visible=False) prompt_styles_btn.click(_js="applyStyles", fn=parse_style, inputs=[prompt_styles], outputs=[prompt_styles]) return prompt, prompt_styles, negative_prompt, submit, button_interrogate, button_deepbooru, prompt_style_apply, save_style, paste, extra_networks_button, token_counter, token_button, negative_token_counter, negative_token_button @@ -338,7 +335,6 @@ def create_ui(startup_timer = None): timer.startup = timer.Timer() reload_javascript() parameters_copypaste.reset() - ui_defaults = readfile(cmd_opts.ui_config) import modules.txt2img # pylint: disable=redefined-outer-name modules.scripts.scripts_current = modules.scripts.scripts_txt2img @@ -350,75 +346,74 @@ def create_ui(startup_timer = None): with FormRow(variant='compact', elem_id="txt2img_extra_networks", visible=False) as extra_networks_ui: from modules import ui_extra_networks extra_networks_ui = ui_extra_networks.create_ui(extra_networks_ui, extra_networks_button, 'txt2img', skip_indexing=opts.extra_network_skip_indexing) + with gr.Row().style(equal_height=False, elem_id="txt2img_interface"): with gr.Column(variant='compact', elem_id="txt2img_settings"): - modules.sd_samplers.set_samplers() - steps, sampler_index = create_sampler_and_steps_selection(modules.sd_samplers.samplers, "txt2img") with FormRow(): width = gr.Slider(minimum=64, maximum=4096, step=8, label="Width", value=512, elem_id="txt2img_width") height = gr.Slider(minimum=64, maximum=4096, step=8, label="Height", value=512, elem_id="txt2img_height") res_switch_btn = ToolButton(value=symbols.switch, elem_id="txt2img_res_switch_btn", label="Switch dims") - with FormRow(elem_classes="checkboxes-row", variant="compact"): - show_batch = gr.Checkbox(label='Batch', value=ui_defaults.get('txt2img/Batch/value', False), elem_id="txt2img_show_batch") - show_seed = gr.Checkbox(label='Seed details', value=ui_defaults.get('txt2img/Seed details/value', False), elem_id="txt2img_show_seed") - show_advanced = gr.Checkbox(label='Advanced', value=ui_defaults.get('txt2img/Advanced/value', False), elem_id="txt2img_show_advanced") - show_second_pass = gr.Checkbox(label='Second pass', value=ui_defaults.get('txt2img/Second pass/value', False), elem_id="txt2img_show_second_pass") + with FormGroup(elem_classes="settings-accordion"): + with gr.Accordion(open=False, label="Sampler", elem_id="txt2img_sampler", elem_classes=["small-accordion"]): + with FormRow(elem_id="txt2img_row_sampler"): + modules.sd_samplers.set_samplers() + steps, sampler_index = create_sampler_and_steps_selection(modules.sd_samplers.samplers, "txt2img") - with FormGroup(visible=show_batch.value, elem_id="txt2img_batch") as batch_group: - with FormRow(elem_id="txt2img_row_batch"): - batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id="txt2img_batch_count") - batch_size = gr.Slider(minimum=1, maximum=32, step=1, label='Batch size', value=1, elem_id="txt2img_batch_size") - batch_switch_btn = ToolButton(value=symbols.switch, elem_id="txt2img_batch_switch_btn", label="Switch dims") + with gr.Accordion(open=False, label="Batch", elem_id="txt2img_batch", elem_classes=["small-accordion"]): + with FormRow(elem_id="txt2img_row_batch"): + batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id="txt2img_batch_count") + batch_size = gr.Slider(minimum=1, maximum=32, step=1, label='Batch size', value=1, elem_id="txt2img_batch_size") + batch_switch_btn = ToolButton(value=symbols.switch, elem_id="txt2img_batch_switch_btn", label="Switch dims") - seed_group, seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = create_seed_inputs('txt2img', show_seed.value) + seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = create_seed_inputs('txt2img') - with FormGroup(visible=show_advanced.value, elem_id="txt2img_advanced") as advanced_group: - with FormRow(): - cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.1, label='CFG scale', value=6.0, elem_id="txt2img_cfg_scale") - clip_skip = gr.Slider(label='CLIP skip', value=1, minimum=1, maximum=14, step=1, elem_id='txt2img_clip_skip', interactive=True) - with FormRow(elem_id="guidence_scale_row", variant="compact"): - image_cfg_scale = gr.Slider(minimum=1.1, maximum=30.0, step=0.1, label='Secondary CFG scale', value=6.0, elem_id="txt2img_image_cfg_scale") - diffusers_guidance_rescale = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Guidance rescale', value=0.7, elem_id="txt2img_image_cfg_rescale") - with FormRow(elem_classes="checkboxes-row", variant="compact"): - full_quality = gr.Checkbox(label='Full quality', value=True, elem_id="txt2img_full_quality") - restore_faces = gr.Checkbox(label='Face restore', value=False, visible=len(modules.shared.face_restorers) > 1, elem_id="txt2img_restore_faces") - tiling = gr.Checkbox(label='Tiling', value=False, elem_id="txt2img_tiling") - - with FormGroup(visible=show_second_pass.value, elem_id="txt2img_second_pass") as second_pass_group: - with FormGroup(): - with FormRow(elem_id="sampler_selection_txt2img_alt_row1"): - latent_index = gr.Dropdown(label='Secondary sampler', elem_id="txt2img_sampling_alt", choices=[x.name for x in modules.sd_samplers.samplers], value='Default', type="index") - denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Denoising strength', value=0.5, elem_id="txt2img_denoising_strength") - with FormRow(elem_id="txt2img_hires_finalres", variant="compact"): - hr_final_resolution = FormHTML(value="", elem_id="txtimg_hr_finalres", label="Upscaled resolution", interactive=False) - with FormRow(elem_id="txt2img_hires_fix_row1", variant="compact"): - hr_upscaler = gr.Dropdown(label="Upscaler", elem_id="txt2img_hr_upscaler", choices=[*modules.shared.latent_upscale_modes, *[x.name for x in modules.shared.sd_upscalers]], value=modules.shared.latent_upscale_default_mode) - hr_force = gr.Checkbox(label='Force Hires', value=False, elem_id="txt2img_hr_force") - with FormRow(elem_id="txt2img_hires_fix_row2", variant="compact"): - hr_second_pass_steps = gr.Slider(minimum=0, maximum=99, step=1, label='Hires steps', elem_id="txt2img_steps_alt", value=20) - hr_scale = gr.Slider(minimum=1.0, maximum=4.0, step=0.05, label="Upscale by", value=2.0, elem_id="txt2img_hr_scale") - with FormRow(elem_id="txt2img_hires_fix_row3", variant="compact"): - hr_resize_x = gr.Slider(minimum=0, maximum=4096, step=8, label="Resize width to", value=0, elem_id="txt2img_hr_resize_x") - hr_resize_y = gr.Slider(minimum=0, maximum=4096, step=8, label="Resize height to", value=0, elem_id="txt2img_hr_resize_y") - with FormGroup(visible=modules.shared.backend == modules.shared.Backend.DIFFUSERS): + with gr.Accordion(open=False, label="Advanced", elem_id="txt2img_advanced", elem_classes=["small-accordion"]): with FormRow(): - hr_refiner = FormHTML(value="Refiner", elem_id="txtimg_hr_refiner", interactive=False) - with FormRow(elem_id="txt2img_refiner_row1", variant="compact"): - refiner_start = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Refiner start', value=0.8, elem_id="txt2img_refiner_start") - refiner_steps = gr.Slider(minimum=0, maximum=99, step=1, label="Refiner steps", elem_id="txt2img_refiner_steps", value=5) - with FormRow(elem_id="txt2img_refiner_row3", variant="compact"): - refiner_prompt = gr.Textbox(value='', label='Secondary Prompt') - with FormRow(elem_id="txt2img_refiner_row4", variant="compact"): - refiner_negative = gr.Textbox(value='', label='Secondary negative prompt') + cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.1, label='CFG scale', value=6.0, elem_id="txt2img_cfg_scale") + clip_skip = gr.Slider(label='CLIP skip', value=1, minimum=1, maximum=14, step=1, elem_id='txt2img_clip_skip', interactive=True) + with FormRow(elem_id="guidence_scale_row", variant="compact"): + image_cfg_scale = gr.Slider(minimum=1.1, maximum=30.0, step=0.1, label='Secondary CFG scale', value=6.0, elem_id="txt2img_image_cfg_scale") + diffusers_guidance_rescale = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Guidance rescale', value=0.7, elem_id="txt2img_image_cfg_rescale") + with FormRow(elem_classes="checkboxes-row", variant="compact"): + full_quality = gr.Checkbox(label='Full quality', value=True, elem_id="txt2img_full_quality") + restore_faces = gr.Checkbox(label='Face restore', value=False, visible=len(modules.shared.face_restorers) > 1, elem_id="txt2img_restore_faces") + tiling = gr.Checkbox(label='Tiling', value=False, elem_id="txt2img_tiling") + + with gr.Accordion(open=False, label="Second pass", elem_id="txt2img_second_pass", elem_classes=["small-accordion"]): + with FormGroup(): + with FormRow(elem_id="sampler_selection_txt2img_alt_row1"): + enable_hr = gr.Checkbox(label='Enable second pass', value=False, elem_id="txt2img_enable_hr") + with FormRow(elem_id="sampler_selection_txt2img_alt_row1"): + latent_index = gr.Dropdown(label='Secondary sampler', elem_id="txt2img_sampling_alt", choices=[x.name for x in modules.sd_samplers.samplers], value='Default', type="index") + denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Denoising strength', value=0.5, elem_id="txt2img_denoising_strength") + with FormRow(elem_id="txt2img_hires_finalres", variant="compact"): + hr_final_resolution = FormHTML(value="", elem_id="txtimg_hr_finalres", label="Upscaled resolution", interactive=False) + with FormRow(elem_id="txt2img_hires_fix_row1", variant="compact"): + hr_upscaler = gr.Dropdown(label="Upscaler", elem_id="txt2img_hr_upscaler", choices=[*modules.shared.latent_upscale_modes, *[x.name for x in modules.shared.sd_upscalers]], value=modules.shared.latent_upscale_default_mode) + hr_force = gr.Checkbox(label='Force Hires', value=False, elem_id="txt2img_hr_force") + with FormRow(elem_id="txt2img_hires_fix_row2", variant="compact"): + hr_second_pass_steps = gr.Slider(minimum=0, maximum=99, step=1, label='Hires steps', elem_id="txt2img_steps_alt", value=20) + hr_scale = gr.Slider(minimum=1.0, maximum=4.0, step=0.05, label="Upscale by", value=2.0, elem_id="txt2img_hr_scale") + with FormRow(elem_id="txt2img_hires_fix_row3", variant="compact"): + hr_resize_x = gr.Slider(minimum=0, maximum=4096, step=8, label="Resize width to", value=0, elem_id="txt2img_hr_resize_x") + hr_resize_y = gr.Slider(minimum=0, maximum=4096, step=8, label="Resize height to", value=0, elem_id="txt2img_hr_resize_y") + with FormGroup(visible=modules.shared.backend == modules.shared.Backend.DIFFUSERS): + with FormRow(elem_id="txt2img_refiner_row1", variant="compact"): + refiner_start = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Refiner start', value=0.8, elem_id="txt2img_refiner_start") + refiner_steps = gr.Slider(minimum=0, maximum=99, step=1, label="Refiner steps", elem_id="txt2img_refiner_steps", value=5) + with FormRow(elem_id="txt2img_refiner_row3", variant="compact"): + refiner_prompt = gr.Textbox(value='', label='Secondary Prompt') + with FormRow(elem_id="txt2img_refiner_row4", variant="compact"): + refiner_negative = gr.Textbox(value='', label='Secondary negative prompt') with FormRow(elem_id="txt2img_override_settings_row") as row: override_settings = create_override_settings_dropdown('txt2img', row) custom_inputs = modules.scripts.scripts_txt2img.setup_ui() - hr_resolution_preview_inputs = [show_second_pass, width, height, hr_scale, hr_resize_x, hr_resize_y, hr_upscaler] + hr_resolution_preview_inputs = [width, height, hr_scale, hr_resize_x, hr_resize_y, hr_upscaler] for preview_input in hr_resolution_preview_inputs: preview_input.change( fn=calc_resolution_hires, @@ -434,7 +429,7 @@ def create_ui(startup_timer = None): txt2img_args = dict( fn=wrap_gradio_gpu_call(modules.txt2img.txt2img, extra_outputs=[None, '', '']), - _js="submit", + _js="submit_txt2img", inputs=[ dummy_component, txt2img_prompt, txt2img_negative_prompt, @@ -448,7 +443,7 @@ def create_ui(startup_timer = None): clip_skip, seed, subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, height, width, - show_second_pass, denoising_strength, + enable_hr, denoising_strength, hr_scale, hr_upscaler, hr_force, hr_second_pass_steps, hr_resize_x, hr_resize_y, refiner_steps, refiner_start, refiner_prompt, refiner_negative, override_settings, @@ -464,17 +459,16 @@ def create_ui(startup_timer = None): txt2img_prompt.submit(**txt2img_args) submit.click(**txt2img_args) - def enable_hr_change(visible: bool, refiner_start): - enabled = modules.shared.opts.sd_model_refiner != "None" and refiner_start > 0 and refiner_start < 1 - return {"visible": visible, "__type__": "update"}, f'Refiner: {"enabled" if enabled else "disabled"}' - res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress=False) batch_switch_btn.click(lambda w, h: (h, w), inputs=[batch_count, batch_size], outputs=[batch_count, batch_size], show_progress=False) txt_prompt_img.change(fn=modules.images.image_data, inputs=[txt_prompt_img], outputs=[txt2img_prompt, txt_prompt_img]) - show_second_pass.change(enable_hr_change, inputs=[show_second_pass, refiner_start], outputs=[second_pass_group, hr_refiner], show_progress = False) - show_seed.change(gr_show, inputs=[show_seed], outputs=[seed_group], show_progress = False) + """ + show_sampler.change(gr_show, inputs=[show_sampler], outputs=[sampler_group], show_progress = False) show_batch.change(gr_show, inputs=[show_batch], outputs=[batch_group], show_progress = False) + show_seed.change(gr_show, inputs=[show_seed], outputs=[seed_group], show_progress = False) show_advanced.change(gr_show, inputs=[show_advanced], outputs=[advanced_group], show_progress = False) + show_second_pass.change(enable_hr_change, inputs=[show_second_pass, refiner_start], outputs=[second_pass_group, hr_refiner], show_progress = False) + """ txt2img_paste_fields = [ (txt2img_prompt, "Prompt"), @@ -609,108 +603,103 @@ def create_ui(startup_timer = None): button.click(fn=copy_image, inputs=[elem], outputs=[copy_image_destinations[name]]) button.click(fn=lambda: None, _js=f"switch_to_{name.replace(' ', '_')}", inputs=[], outputs=[]) - modules.sd_samplers.set_samplers() - steps, sampler_index = create_sampler_and_steps_selection(modules.sd_samplers.samplers_for_img2img, "img2img") + with FormGroup(elem_classes="settings-accordion"): + with gr.Accordion(open=False, label="Sampler", elem_classes=["small-accordion"], elem_id="img2img_sampling_group"): + modules.sd_samplers.set_samplers() + steps, sampler_index = create_sampler_and_steps_selection(modules.sd_samplers.samplers_for_img2img, "img2img") - with FormRow(elem_classes="checkboxes-row", variant="compact"): - show_seed = gr.Checkbox(label='Seed details', value=ui_defaults.get('img2img/Seed details/value', False), elem_id="img2img_show_seed") - show_resize = gr.Checkbox(label='Resize', value=ui_defaults.get('img2img/Resize/value', False), elem_id="img2img_show_resize") - show_batch = gr.Checkbox(label='Batch', value=ui_defaults.get('img2img/Batch/value', False), elem_id="img2img_show_batch") - show_denoise = gr.Checkbox(label='Denoise', value=ui_defaults.get('img2img/Denoise/value', True), elem_id="img2img_show_denoise") - show_advanced = gr.Checkbox(label='Advanced', value=ui_defaults.get('img2img/Advanced/value', False), elem_id="txt2img_show_advanced") + with gr.Accordion(open=False, label="Resize", elem_classes=["small-accordion"], elem_id="img2img_resize_group"): + with FormRow(): + resize_mode = gr.Radio(label="Resize mode", elem_id="resize_mode", choices=["None", "Resize fixed", "Crop and resize", "Resize and fill", "Resize using Latent upscale"], type="index", value="None") - with FormGroup(visible=show_resize.value, elem_id=f"{tab}_resize_group") as resize_group: - with FormRow(): - resize_mode = gr.Radio(label="Resize mode", elem_id="resize_mode", choices=["None", "Resize fixed", "Crop and resize", "Resize and fill", "Resize using Latent upscale"], type="index", value="None") + with FormRow(): + with gr.Column(elem_id="img2img_column_size", scale=4): + selected_scale_tab = gr.State(value=0) # pylint: disable=abstract-class-instantiated - with FormRow(): - with gr.Column(elem_id="img2img_column_size", scale=4): - selected_scale_tab = gr.State(value=0) # pylint: disable=abstract-class-instantiated + with gr.Tabs(): + with gr.Tab(label="Resize to") as tab_scale_to: + with FormRow(): + with gr.Column(elem_id="img2img_column_size", scale=4): + with FormRow(): + width = gr.Slider(minimum=64, maximum=4096, step=8, label="Width", value=512, elem_id="img2img_width") + height = gr.Slider(minimum=64, maximum=4096, step=8, label="Height", value=512, elem_id="img2img_height") + with gr.Column(elem_id="img2img_column_dim", scale=1, elem_classes="dimensions-tools"): + with FormRow(): + res_switch_btn = ToolButton(value=symbols.switch, elem_id="img2img_res_switch_btn") + detect_image_size_btn = ToolButton(value=symbols.detect, elem_id="img2img_detect_image_size_btn") - with gr.Tabs(): - with gr.Tab(label="Resize to") as tab_scale_to: - with FormRow(): - with gr.Column(elem_id="img2img_column_size", scale=4): - with FormRow(): - width = gr.Slider(minimum=64, maximum=4096, step=8, label="Width", value=512, elem_id="img2img_width") - height = gr.Slider(minimum=64, maximum=4096, step=8, label="Height", value=512, elem_id="img2img_height") - with gr.Column(elem_id="img2img_column_dim", scale=1, elem_classes="dimensions-tools"): - with FormRow(): - res_switch_btn = ToolButton(value=symbols.switch, elem_id="img2img_res_switch_btn") - detect_image_size_btn = ToolButton(value=symbols.detect, elem_id="img2img_detect_image_size_btn") + with gr.Tab(label="Resize by") as tab_scale_by: + scale_by = gr.Slider(minimum=0.05, maximum=4.0, step=0.05, label="Scale", value=1.0, elem_id="img2img_scale") - with gr.Tab(label="Resize by") as tab_scale_by: - scale_by = gr.Slider(minimum=0.05, maximum=4.0, step=0.05, label="Scale", value=1.0, elem_id="img2img_scale") + with FormRow(): + scale_by_html = FormHTML(resize_from_to_html(0, 0, 0.0), elem_id="img2img_scale_resolution_preview") + gr.Slider(label="Unused", elem_id="img2img_unused_scale_by_slider") + button_update_resize_to = gr.Button(visible=False, elem_id="img2img_update_resize_to") - with FormRow(): - scale_by_html = FormHTML(resize_from_to_html(0, 0, 0.0), elem_id="img2img_scale_resolution_preview") - gr.Slider(label="Unused", elem_id="img2img_unused_scale_by_slider") - button_update_resize_to = gr.Button(visible=False, elem_id="img2img_update_resize_to") + on_change_args = dict( + fn=resize_from_to_html, + _js="currentImg2imgSourceResolution", + inputs=[dummy_component, dummy_component, scale_by], + outputs=scale_by_html, + show_progress=False, + ) - on_change_args = dict( - fn=resize_from_to_html, - _js="currentImg2imgSourceResolution", - inputs=[dummy_component, dummy_component, scale_by], - outputs=scale_by_html, - show_progress=False, - ) + scale_by.release(**on_change_args) + button_update_resize_to.click(**on_change_args) - scale_by.release(**on_change_args) - button_update_resize_to.click(**on_change_args) + # the code below is meant to update the resolution label after the image in the image selection UI has changed. + # as it is now the event keeps firing continuously for inpaint edits, which ruins the page with constant requests. + # I assume this must be a gradio bug and for now we'll just do it for non-inpaint inputs. + for component in [init_img, sketch]: + component.change(fn=lambda: None, _js="updateImg2imgResizeToTextAfterChangingImage", inputs=[], outputs=[], show_progress=False) - # the code below is meant to update the resolution label after the image in the image selection UI has changed. - # as it is now the event keeps firing continuously for inpaint edits, which ruins the page with constant requests. - # I assume this must be a gradio bug and for now we'll just do it for non-inpaint inputs. - for component in [init_img, sketch]: - component.change(fn=lambda: None, _js="updateImg2imgResizeToTextAfterChangingImage", inputs=[], outputs=[], show_progress=False) + tab_scale_to.select(fn=lambda: 0, inputs=[], outputs=[selected_scale_tab]) + tab_scale_by.select(fn=lambda: 1, inputs=[], outputs=[selected_scale_tab]) - tab_scale_to.select(fn=lambda: 0, inputs=[], outputs=[selected_scale_tab]) - tab_scale_by.select(fn=lambda: 1, inputs=[], outputs=[selected_scale_tab]) + with gr.Accordion(open=False, label="Batch", elem_classes=["small-accordion"], elem_id="img2img_batch_group"): + with FormRow(elem_id="img2img_column_batch"): + batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id="img2img_batch_count") + batch_size = gr.Slider(minimum=1, maximum=8, step=1, label='Batch size', value=1, elem_id="img2img_batch_size") - with FormGroup(visible=show_batch.value, elem_id=f"{tab}_resize_group") as batch_group: - with FormRow(elem_id="img2img_column_batch"): - batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id="img2img_batch_count") - batch_size = gr.Slider(minimum=1, maximum=8, step=1, label='Batch size', value=1, elem_id="img2img_batch_size") + seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = create_seed_inputs('img2img') - seed_group, seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = create_seed_inputs('img2img', show_seed.value) + with gr.Accordion(open=False, label="Denoise", elem_classes=["small-accordion"], elem_id="img2img_denoise_group"): + with FormRow(): + denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Denoising strength', value=0.75, elem_id="img2img_denoising_strength") + refiner_start = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Denoise start', value=0.0, elem_id="img2img_refiner_start") - with FormGroup(visible=show_denoise.value, elem_id=f"{tab}_denoise_group") as denoise_group: - with FormRow(): - denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Denoising strength', value=0.75, elem_id="img2img_denoising_strength") - refiner_start = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Denoise start', value=0.0, elem_id="img2img_refiner_start") + with gr.Accordion(open=False, label="Advanced", elem_classes=["small-accordion"], elem_id="img2img_advanced_group"): + with FormRow(): + cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG scale', value=6.0, elem_id="img2img_cfg_scale") + image_cfg_scale = gr.Slider(minimum=0, maximum=30.0, step=0.05, label='Image CFG scale', value=1.5, elem_id="img2img_image_cfg_scale") + with FormRow(): + clip_skip = gr.Slider(label='CLIP skip', value=1, minimum=1, maximum=4, step=1, elem_id='img2img_clip_skip', interactive=True) + diffusers_guidance_rescale = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Guidance rescale', value=0.7, elem_id="txt2img_image_cfg_rescale") + with FormRow(elem_classes="img2img_checkboxes_row", variant="compact"): + full_quality = gr.Checkbox(label='Full quality', value=True, elem_id="img2img_full_quality") + restore_faces = gr.Checkbox(label='Restore faces', value=False, visible=len(modules.shared.face_restorers) > 1, elem_id="img2img_restore_faces") + tiling = gr.Checkbox(label='Tiling', value=False, elem_id="img2img_tiling") - with FormGroup(visible=show_advanced.value, elem_id=f"{tab}_advanced_group") as advanced_group: - with FormRow(): - cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG scale', value=6.0, elem_id="img2img_cfg_scale") - image_cfg_scale = gr.Slider(minimum=0, maximum=30.0, step=0.05, label='Image CFG scale', value=1.5, elem_id="img2img_image_cfg_scale") - with FormRow(): - clip_skip = gr.Slider(label='CLIP skip', value=1, minimum=1, maximum=4, step=1, elem_id='img2img_clip_skip', interactive=True) - diffusers_guidance_rescale = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Guidance rescale', value=0.7, elem_id="txt2img_image_cfg_rescale") - with FormRow(elem_classes="img2img_checkboxes_row", variant="compact"): - full_quality = gr.Checkbox(label='Full quality', value=True, elem_id="img2img_full_quality") - restore_faces = gr.Checkbox(label='Restore faces', value=False, visible=len(modules.shared.face_restorers) > 1, elem_id="img2img_restore_faces") - tiling = gr.Checkbox(label='Tiling', value=False, elem_id="img2img_tiling") + with FormGroup(elem_id="inpaint_controls", visible=False) as inpaint_controls: + with FormRow(): + mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=4, elem_id="img2img_mask_blur") + mask_alpha = gr.Slider(label="Mask transparency", visible=False, elem_id="img2img_mask_alpha") + with FormRow(): + with gr.Column(): + inpainting_mask_invert = gr.Radio(label='Mask mode', choices=['Inpaint masked', 'Inpaint not masked'], value='Inpaint masked', type="index", elem_id="img2img_mask_mode") + with gr.Column(): + inpainting_fill = gr.Radio(label='Masked content', choices=['fill', 'original', 'latent noise', 'latent nothing'], value='original', type="index", elem_id="img2img_inpainting_fill") + with FormRow(): + with gr.Column(): + inpaint_full_res = gr.Radio(label="Inpaint area", choices=["Whole picture", "Only masked"], type="index", value="Whole picture", elem_id="img2img_inpaint_full_res") + with gr.Column(): + inpaint_full_res_padding = gr.Slider(label='Only masked padding, pixels', minimum=0, maximum=256, step=4, value=32, elem_id="img2img_inpaint_full_res_padding") - with FormGroup(elem_id="inpaint_controls", visible=False) as inpaint_controls: - with FormRow(): - mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=4, elem_id="img2img_mask_blur") - mask_alpha = gr.Slider(label="Mask transparency", visible=False, elem_id="img2img_mask_alpha") - with FormRow(): - with gr.Column(): - inpainting_mask_invert = gr.Radio(label='Mask mode', choices=['Inpaint masked', 'Inpaint not masked'], value='Inpaint masked', type="index", elem_id="img2img_mask_mode") - with gr.Column(): - inpainting_fill = gr.Radio(label='Masked content', choices=['fill', 'original', 'latent noise', 'latent nothing'], value='original', type="index", elem_id="img2img_inpainting_fill") - with FormRow(): - with gr.Column(): - inpaint_full_res = gr.Radio(label="Inpaint area", choices=["Whole picture", "Only masked"], type="index", value="Whole picture", elem_id="img2img_inpaint_full_res") - with gr.Column(): - inpaint_full_res_padding = gr.Slider(label='Only masked padding, pixels', minimum=0, maximum=256, step=4, value=32, elem_id="img2img_inpaint_full_res_padding") + def select_img2img_tab(tab): + return gr.update(visible=tab in [2, 3, 4]), gr.update(visible=tab == 3) - def select_img2img_tab(tab): - return gr.update(visible=tab in [2, 3, 4]), gr.update(visible=tab == 3) - - for i, elem in enumerate(img2img_tabs): - elem.select(fn=lambda tab=i: select_img2img_tab(tab), inputs=[], outputs=[inpaint_controls, mask_alpha]) # pylint: disable=cell-var-from-loop + for i, elem in enumerate(img2img_tabs): + elem.select(fn=lambda tab=i: select_img2img_tab(tab), inputs=[], outputs=[inpaint_controls, mask_alpha]) # pylint: disable=cell-var-from-loop with FormRow(elem_id="img2img_override_settings_row") as row: override_settings = create_override_settings_dropdown('img2img', row) @@ -799,11 +788,6 @@ def create_ui(startup_timer = None): img2img_deepbooru.click(fn=lambda *args: process_interrogate(interrogate_deepbooru, *args), **interrogate_args) res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress=False) - show_seed.change(gr_show, inputs=[show_seed], outputs=[seed_group], show_progress = False) - show_resize.change(gr_show, inputs=[show_resize], outputs=[resize_group], show_progress = False) - show_batch.change(gr_show, inputs=[show_batch], outputs=[batch_group], show_progress = False) - show_denoise.change(gr_show, inputs=[show_denoise], outputs=[denoise_group], show_progress = False) - show_advanced.change(gr_show, inputs=[show_advanced], outputs=[advanced_group], show_progress = False) detect_image_size_btn.click( fn=lambda w, h, _: (w or gr.update(), h or gr.update()), @@ -882,7 +866,6 @@ def create_ui(startup_timer = None): parameters_copypaste.register_paste_params_button(parameters_copypaste.ParamBinding( paste_button=img2img_paste, tabname="img2img", source_text_component=img2img_prompt, source_image_component=None, )) - # log.debug(f'UI interface: tab=img2img seed={show_seed.value} resize={show_resize.value} batch={show_batch.value} denoise={show_denoise.value} advanced={show_advanced.value}') timer.startup.record("ui-img2img") @@ -1071,7 +1054,7 @@ def create_ui(startup_timer = None): with gr.TabItem("Show all pages", variant='primary', elem_id="settings_show_all_pages"): create_dirty_indicator("show_all_pages", [], interactive=False) - with gr.TabItem("UI Config", id="system_config", elem_id="tab_config"): + with gr.TabItem("User interface", id="system_config", elem_id="tab_config"): loadsave.create_ui() create_dirty_indicator("tab_defaults", [], interactive=False) diff --git a/modules/ui_loadsave.py b/modules/ui_loadsave.py index 0bd24ff74..294d040c6 100644 --- a/modules/ui_loadsave.py +++ b/modules/ui_loadsave.py @@ -15,6 +15,8 @@ class UiLoadsave: self.ui_defaults_apply = None # button self.ui_defaults_review = None # button self.ui_defaults_restore = None # button + self.ui_defaults_submenu = None # button + self.component_open = {} self.ui_defaults = {} self.ui_settings = self.read_from_file() @@ -41,9 +43,13 @@ class UiLoadsave: init_field(saved_value) if field == 'value' and key not in self.component_mapping: self.component_mapping[key] = x + if field == 'open' and key not in self.component_mapping: + self.component_open[key] = x if type(x) in [gr.Slider, gr.Radio, gr.Checkbox, gr.Textbox, gr.Number, gr.Dropdown, ToolButton, gr.Button] and x.visible: apply_field(x, 'visible') + if type(x) == gr.Accordion: + apply_field(x, 'open') if type(x) == gr.Slider: apply_field(x, 'value') apply_field(x, 'minimum') @@ -88,6 +94,8 @@ class UiLoadsave: def add_block(self, x, path=""): """adds all components inside a gradio block x to the registry of tracked components""" if hasattr(x, 'children'): + if isinstance(x, gr.Accordion): + self.add_component(f"{path}/{x.label}", x) if isinstance(x, gr.Tabs) and x.elem_id is not None: self.add_component(f"{path}/Tabs@{x.elem_id}", x) # Tabs element dont have a label, have to use elem_id instead for c in x.children: @@ -112,12 +120,6 @@ class UiLoadsave: self.write_to_file(self.ui_settings) def iter_changes(self, values): - """ - given a dictionary with defaults from a file and current values from gradio elements, returns - an iterator over tuples of values that are not the same between the file and the current; - tuple contents are: path, old value, new value - """ - # for (path, component), new_value in zip(self.component_mapping.items(), values): for i, name in enumerate(self.component_mapping): component = self.component_mapping[name] choices = getattr(component, 'choices', None) @@ -140,6 +142,18 @@ class UiLoadsave: yield name, old_value, new_value, default_value return [] + def iter_menus(self): + for _i, name in enumerate(self.component_open): + old_value = self.ui_settings.get(name, None) + new_value = self.component_open[name].open + default_value = self.ui_defaults.get(name, '') + if old_value == new_value: + continue + if (new_value == default_value) and (old_value is None): + continue + yield name, old_value, new_value, default_value + return [] + def ui_view(self, *values): text = """
| Menu | +State | +
|---|---|
| {k} | {'open' if opened else 'closed'} |