From ca7aa42cdbce0de5d341a0a6ea22eb6750bdb728 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Sat, 27 Apr 2024 18:44:11 -0400 Subject: [PATCH] redesign base interface --- CHANGELOG.md | 1 + javascript/black-teal.css | 3 +- javascript/sdnext.css | 4 +-- modules/ui_control.py | 7 +++-- modules/ui_img2img.py | 9 ++++-- modules/ui_sections.py | 64 ++++++++++++++++++++++++--------------- modules/ui_txt2img.py | 12 +++++--- 7 files changed, 64 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 785b05b32..e8f75c200 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,7 @@ it is time to give credit to original [author](https://github.com/auTOMATIC1111) **Standard* (built-in themes), **Modern** (experimental nextgen ui), **None** (used for Gradio and Huggingface 3rd party themes) Specifying a theme type updates list of available themes For example, *Gradio* themes will not appear as available if theme type is set to *Standard* + - Redesign of base txt2img interface - Minor tweaks to styles: refresh/apply/save - See details in [WiKi](https://github.com/vladmandic/automatic/wiki/Themes) - **API**: diff --git a/javascript/black-teal.css b/javascript/black-teal.css index 352058023..5632f0778 100644 --- a/javascript/black-teal.css +++ b/javascript/black-teal.css @@ -77,7 +77,7 @@ input[type=range]::-moz-range-track { width: 100% !important; heigh input[type=range]::-webkit-slider-thumb { border: 0px solid #000000 !important; height: var(--line-xs) !important; width: var(--line-md) !important; border-radius: var(--radius-lg) !important; background: var(--highlight-color) !important; cursor: pointer !important; appearance: none !important; margin-top: -4px !important; } input[type=range]::-moz-range-thumb { border: 0px solid #000000 !important; height: var(--line-xs) !important; width: var(--line-md) !important; border-radius: var(--radius-lg) !important; background: var(--highlight-color) !important; cursor: pointer !important; appearance: none !important; margin-top: -4px !important; } */ -input[type='range'] { display: block; margin: 0; padding: 0; height: 1em; background-color: transparent; overflow: hidden; cursor: pointer; box-shadow: 0 0 0 0 transparent; -webkit-appearance: none; } +input[type='range'] { display: block; margin: 0; padding: 0; height: 1em; background-color: transparent; overflow: hidden; cursor: pointer; box-shadow: 0 0 0 0 transparent; -webkit-appearance: none; appearance: none; } input[type='range']::-webkit-slider-thumb { height: .9em; width: .9em; background-color: hsl(180, 54%, 61%); box-shadow: var(--range-shadow); border-radius: var(--radius-sm); } input[type='range']::-webkit-slider-runnable-track, input[type='range']::-webkit-slider-thumb { -webkit-appearance: none; } input[type='range']::-moz-range-thumb { height: .9em; width: .9em; background-color: hsl(180, 54%, 61%); box-shadow: var(--range-shadow); } @@ -106,6 +106,7 @@ fieldset .gr-block.gr-box, label.block span { padding: 0; margin-top: -4px; } .gr-input-label { color: lightyellow; border-width: 0; background: transparent; padding: 2px !important; } .gr-panel { background-color: var(--background-color); } .eta-bar { display: none !important } +.gradio-slider { max-width: 200px; } .gradio-slider input[type="number"] { background: var(--neutral-950); margin-top: 2px; } svg.feather.feather-image, .feather .feather-image { display: none } .gap-2 { padding-top: 8px; } diff --git a/javascript/sdnext.css b/javascript/sdnext.css index faa445522..3cf212f7b 100644 --- a/javascript/sdnext.css +++ b/javascript/sdnext.css @@ -18,8 +18,6 @@ span { font-size: var(--text-md) !important; } button { font-size: var(--text-lg) !important; } input[type='color'] { width: 64px; height: 32px; } - - /* gradio elements */ .block .padded:not(.gradio-accordion) { padding: 0 !important; margin-right: 0; min-width: 90px !important; } .compact { gap: 1em 0.2em; background: transparent !important; padding: 0 !important; } @@ -125,6 +123,8 @@ div#extras_scale_to_tab div.form { flex-direction: row; } .tooltip-show { opacity: 0.9; } .toolbutton-selected { background: var(--background-fill-primary) !important; } #txt2img_hdr_color_row > div { min-width: unset !important; max-width: unset !important; } +#txt2img_advanced_options, #img2img_advanced_options, #control_advanced_options { min-width: 100%; } +#txt2img_advanced_options .gradio-checkbox, #img2img_advanced_options .gradio-checkbox, #control_advanced_options .gradio-checkbox { min-width: unset !important; max-width: fit-content; } /* settings */ #si-sparkline-memo, #si-sparkline-load { background-color: #111; } diff --git a/modules/ui_control.py b/modules/ui_control.py index bcc034282..951f1a803 100644 --- a/modules/ui_control.py +++ b/modules/ui_control.py @@ -84,6 +84,8 @@ def create_ui(_blocks: gr.Blocks=None): with gr.Row(elem_id='control_settings'): + full_quality, restore_faces, tiling, hidiffusion = ui_sections.create_options('control') + with gr.Accordion(open=False, label="Input", elem_id="control_input", elem_classes=["small-accordion"]): with gr.Row(): show_preview = gr.Checkbox(label="Show preview", value=True, elem_id="control_show_preview") @@ -104,14 +106,15 @@ def create_ui(_blocks: gr.Blocks=None): with gr.Accordion(open=False, label="Sampler", elem_id="control_sampler", elem_classes=["small-accordion"]): sd_samplers.set_samplers() steps, sampler_index = ui_sections.create_sampler_and_steps_selection(sd_samplers.samplers, "control") + ui_sections.create_sampler_options('control') - batch_count, batch_size = ui_sections.create_batch_inputs('control') + batch_count, batch_size = ui_sections.create_batch_inputs('control', accordion=True) seed, _reuse_seed, subseed, _reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = ui_sections.create_seed_inputs('control', reuse_visible=False) mask_controls = masking.create_segment_ui() - cfg_scale, clip_skip, image_cfg_scale, diffusers_guidance_rescale, sag_scale, cfg_end, full_quality, restore_faces, tiling, hidiffusion = ui_sections.create_advanced_inputs('control') + cfg_scale, clip_skip, image_cfg_scale, diffusers_guidance_rescale, sag_scale, cfg_end = ui_sections.create_advanced_inputs('control') hdr_mode, hdr_brightness, hdr_color, hdr_sharpen, hdr_clamp, hdr_boundary, hdr_threshold, hdr_maximize, hdr_max_center, hdr_max_boundry, hdr_color_picker, hdr_tint_ratio, = ui_sections.create_correction_inputs('control') with gr.Accordion(open=False, label="Video", elem_id="control_video", elem_classes=["small-accordion"]): diff --git a/modules/ui_img2img.py b/modules/ui_img2img.py index 47c42bcde..8d5db51bc 100644 --- a/modules/ui_img2img.py +++ b/modules/ui_img2img.py @@ -120,9 +120,11 @@ def create_ui(): with gr.Group(elem_classes="settings-accordion"): - steps, sampler_index = ui_sections.create_sampler_inputs('img2img') + with gr.Accordion(open=False, label="Sampler", elem_classes=["small-accordion"], elem_id="img2img_sampler_group"): + steps, sampler_index = ui_sections.create_sampler_and_steps_selection(None, "img2img") + ui_sections.create_sampler_options('img2img') resize_mode, resize_name, width, height, scale_by, selected_scale_tab = ui_sections.create_resize_inputs('img2img', [init_img, sketch], latent=True) - batch_count, batch_size = ui_sections.create_batch_inputs('img2img') + batch_count, batch_size = ui_sections.create_batch_inputs('img2img', accordion=True) seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = ui_sections.create_seed_inputs('img2img') with gr.Accordion(open=False, label="Denoise", elem_classes=["small-accordion"], elem_id="img2img_denoise_group"): @@ -130,7 +132,8 @@ def create_ui(): denoising_strength = gr.Slider(minimum=0.0, maximum=0.99, step=0.01, label='Denoising strength', value=0.50, 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") - cfg_scale, clip_skip, image_cfg_scale, diffusers_guidance_rescale, sag_scale, cfg_end, full_quality, restore_faces, tiling, hidiffusion = ui_sections.create_advanced_inputs('img2img') + cfg_scale, clip_skip, image_cfg_scale, diffusers_guidance_rescale, sag_scale, cfg_end = ui_sections.create_advanced_inputs('img2img') + full_quality, restore_faces, tiling, hidiffusion = ui_sections.create_options('img2img') hdr_mode, hdr_brightness, hdr_color, hdr_sharpen, hdr_clamp, hdr_boundary, hdr_threshold, hdr_maximize, hdr_max_center, hdr_max_boundry, hdr_color_picker, hdr_tint_ratio, = ui_sections.create_correction_inputs('img2img') # with gr.Group(elem_id="inpaint_controls", visible=False) as inpaint_controls: diff --git a/modules/ui_sections.py b/modules/ui_sections.py index 844cf468d..a2573b4fe 100644 --- a/modules/ui_sections.py +++ b/modules/ui_sections.py @@ -114,11 +114,11 @@ def create_sampler_inputs(tab, accordion=True): return steps, sampler_index -def create_batch_inputs(tab): - with gr.Accordion(open=False, label="Batch", elem_id=f"{tab}_batch", elem_classes=["small-accordion"]): +def create_batch_inputs(tab, accordion=True): + with gr.Accordion(open=False, label="Batch", elem_id=f"{tab}_batch", elem_classes=["small-accordion"]) if accordion else gr.Group(): with gr.Row(elem_id=f"{tab}_row_batch"): - batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id=f"{tab}_batch_count") - batch_size = gr.Slider(minimum=1, maximum=32, step=1, label='Batch size', value=1, elem_id=f"{tab}_batch_size") + batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id=f"{tab}_batch_count", scale=5) + batch_size = gr.Slider(minimum=1, maximum=32, step=1, label='Batch size', value=1, elem_id=f"{tab}_batch_size", scale=5) return batch_count, batch_size @@ -141,26 +141,37 @@ def create_seed_inputs(tab, reuse_visible=True): return seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w -def create_advanced_inputs(tab): +def create_options(tab): + with gr.Row(elem_id=f"{tab}_advanced_options"): + full_quality = gr.Checkbox(label='Full quality', value=True, elem_id=f"{tab}_full_quality") + restore_faces = gr.Checkbox(label='Face restore', value=False, elem_id=f"{tab}_restore_faces") + tiling = gr.Checkbox(label='Tiling', value=False, elem_id=f"{tab}_tiling") + hidiffusion = gr.Checkbox(label='HiDiffusion', value=False, elem_id=f"{tab}_hidiffusion") + return full_quality, restore_faces, tiling, hidiffusion + + +def create_cfg_inputs(tab): + with gr.Row(): + cfg_scale = gr.Slider(minimum=0.0, maximum=30.0, step=0.1, label='CFG scale', value=6.0, elem_id=f"{tab}_cfg_scale") + cfg_end = gr.Slider(minimum=0.0, maximum=1.0, step=0.1, label='CFG end', value=1.0, elem_id=f"{tab}_cfg_end") + return cfg_scale, cfg_end + + +def create_advanced_inputs(tab, base=True): with gr.Accordion(open=False, label="Advanced", elem_id=f"{tab}_advanced", elem_classes=["small-accordion"]): with gr.Group(): - with gr.Row(): - cfg_scale = gr.Slider(minimum=0.0, maximum=30.0, step=0.1, label='CFG scale', value=6.0, elem_id=f"{tab}_cfg_scale") - cfg_end = gr.Slider(minimum=0.0, maximum=1.0, step=0.1, label='CFG end', value=1.0, elem_id=f"{tab}_cfg_end") + if base: + cfg_scale, cfg_end = create_cfg_inputs(tab) + else: + cfg_scale, cfg_end = None, None with gr.Row(): image_cfg_scale = gr.Slider(minimum=0.0, maximum=30.0, step=0.1, label='Secondary guidance', value=6.0, elem_id=f"{tab}_image_cfg_scale") diffusers_guidance_rescale = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Rescale guidance', value=0.7, elem_id=f"{tab}_image_cfg_rescale", visible=shared.backend == shared.Backend.DIFFUSERS) diffusers_sag_scale = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Attention guidance', value=0.0, elem_id=f"{tab}_image_sag_scale", visible=shared.backend == shared.Backend.DIFFUSERS) with gr.Row(): clip_skip = gr.Slider(label='CLIP skip', value=1, minimum=0, maximum=12, step=0.1, elem_id=f"{tab}_clip_skip", interactive=True) - with gr.Group(): - gr.HTML('
') - with gr.Row(elem_id=f"{tab}_advanced_options"): - full_quality = gr.Checkbox(label='Full quality', value=True, elem_id=f"{tab}_full_quality") - restore_faces = gr.Checkbox(label='Face restore', value=False, elem_id=f"{tab}_restore_faces") - tiling = gr.Checkbox(label='Tiling', value=False, elem_id=f"{tab}_tiling", visible=True) - hidiffusion = gr.Checkbox(label='HiDiffusion', value=False, elem_id=f"{tab}_hidiffusion", visible=True) - return cfg_scale, clip_skip, image_cfg_scale, diffusers_guidance_rescale, diffusers_sag_scale, cfg_end, full_quality, restore_faces, tiling, hidiffusion + return cfg_scale, clip_skip, image_cfg_scale, diffusers_guidance_rescale, diffusers_sag_scale, cfg_end + def create_correction_inputs(tab): with gr.Accordion(open=False, label="Corrections", elem_id=f"{tab}_corrections", elem_classes=["small-accordion"], visible=shared.backend == shared.Backend.DIFFUSERS): @@ -187,6 +198,15 @@ def create_correction_inputs(tab): def create_sampler_and_steps_selection(choices, tabname): + if choices is None: + choices = sd_samplers.samplers + with gr.Row(elem_classes=['flex-break']): + steps = gr.Slider(minimum=1, maximum=99, step=1, label="Sampling steps", elem_id=f"{tabname}_steps", value=20) + sampler_index = gr.Dropdown(label='Sampling method', elem_id=f"{tabname}_sampling", choices=[x.name for x in choices], value='Default', type="index") + return steps, sampler_index + + +def create_sampler_options(tabname): def set_sampler_original_options(sampler_options, sampler_algo): shared.opts.data['schedulers_brownian_noise'] = 'brownian noise' in sampler_options shared.opts.data['schedulers_discard_penultimate'] = 'discard penultimate sigma' in sampler_options @@ -200,16 +220,13 @@ def create_sampler_and_steps_selection(choices, tabname): shared.opts.data['schedulers_rescale_betas'] = 'rescale beta' in sampler_options shared.opts.save(shared.config_filename, silent=True) - with gr.Row(elem_classes=['flex-break']): - sampler_index = gr.Dropdown(label='Sampling method', elem_id=f"{tabname}_sampling", choices=[x.name for x in choices], value='Default', type="index") - steps = gr.Slider(minimum=1, maximum=99, step=1, label="Sampling steps", elem_id=f"{tabname}_steps", value=20) if shared.backend == shared.Backend.ORIGINAL: with gr.Row(elem_classes=['flex-break']): - choices = ['brownian noise', 'discard penultimate sigma'] + options = ['brownian noise', 'discard penultimate sigma'] values = [] values += ['brownian noise'] if shared.opts.data.get('schedulers_brownian_noise', False) else [] values += ['discard penultimate sigma'] if shared.opts.data.get('schedulers_discard_penultimate', True) else [] - sampler_options = gr.CheckboxGroup(label='Sampler options', elem_id=f"{tabname}_sampler_options", choices=choices, value=values, type='value') + sampler_options = gr.CheckboxGroup(label='Sampler options', elem_id=f"{tabname}_sampler_options", choices=options, value=values, type='value') with gr.Row(elem_classes=['flex-break']): shared.opts.data['schedulers_sigma'] = shared.opts.data.get('schedulers_sigma', 'default') sampler_algo = gr.Radio(label='Sigma algorithm', elem_id=f"{tabname}_sigma_algo", choices=['default', 'karras', 'exponential', 'polyexponential'], value=shared.opts.data['schedulers_sigma'], type='value') @@ -217,15 +234,14 @@ def create_sampler_and_steps_selection(choices, tabname): sampler_algo.change(fn=set_sampler_original_options, inputs=[sampler_options, sampler_algo], outputs=[]) else: with gr.Row(elem_classes=['flex-break']): - choices = ['karras', 'dynamic threshold', 'low order', 'rescale beta'] + options = ['karras', 'dynamic threshold', 'low order', 'rescale beta'] values = [] values += ['karras'] if shared.opts.data.get('schedulers_use_karras', True) else [] values += ['dynamic threshold'] if shared.opts.data.get('schedulers_use_thresholding', False) else [] values += ['low order'] if shared.opts.data.get('schedulers_use_loworder', True) else [] values += ['rescale beta'] if shared.opts.data.get('schedulers_rescale_betas', False) else [] - sampler_options = gr.CheckboxGroup(label='Sampler options', elem_id=f"{tabname}_sampler_options", choices=choices, value=values, type='value') + sampler_options = gr.CheckboxGroup(label='Sampler options', elem_id=f"{tabname}_sampler_options", choices=options, value=values, type='value') sampler_options.change(fn=set_sampler_diffuser_options, inputs=[sampler_options], outputs=[]) - return steps, sampler_index def create_hires_inputs(tab): diff --git a/modules/ui_txt2img.py b/modules/ui_txt2img.py index 4741f0267..b957492bb 100644 --- a/modules/ui_txt2img.py +++ b/modules/ui_txt2img.py @@ -37,12 +37,16 @@ def create_ui(): with gr.Row(): width, height = ui_sections.create_resolution_inputs('txt2img') - with gr.Group(elem_classes="settings-accordion"): + batch_count, batch_size = ui_sections.create_batch_inputs('txt2img', accordion=False) + cfg_scale, cfg_end = ui_sections.create_cfg_inputs('txt2img') + steps, sampler_index = ui_sections.create_sampler_and_steps_selection(None, "txt2img") + full_quality, restore_faces, tiling, hidiffusion = ui_sections.create_options('txt2img') - steps, sampler_index = ui_sections.create_sampler_inputs('txt2img') - batch_count, batch_size = ui_sections.create_batch_inputs('txt2img') + with gr.Group(elem_classes="settings-accordion"): + with gr.Accordion(open=False, label="Samplers", elem_classes=["small-accordion"], elem_id="txt2img_sampler_group"): + ui_sections.create_sampler_options('txt2img') seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = ui_sections.create_seed_inputs('txt2img') - cfg_scale, clip_skip, image_cfg_scale, diffusers_guidance_rescale, sag_scale, cfg_end, full_quality, restore_faces, tiling, hidiffusion = ui_sections.create_advanced_inputs('txt2img') + _cfg_scale, clip_skip, image_cfg_scale, diffusers_guidance_rescale, sag_scale, _cfg_end = ui_sections.create_advanced_inputs('txt2img', base=False) hdr_mode, hdr_brightness, hdr_color, hdr_sharpen, hdr_clamp, hdr_boundary, hdr_threshold, hdr_maximize, hdr_max_center, hdr_max_boundry, hdr_color_picker, hdr_tint_ratio, = ui_sections.create_correction_inputs('txt2img') enable_hr, hr_sampler_index, denoising_strength, hr_upscaler, hr_force, hr_second_pass_steps, hr_scale, hr_resize_x, hr_resize_y, refiner_steps, refiner_start, refiner_prompt, refiner_negative = ui_sections.create_hires_inputs('txt2img') override_settings = ui_common.create_override_inputs('txt2img')