diff --git a/modules/ui_choices.py b/modules/ui_choices.py
deleted file mode 100644
index f5c59a986..000000000
--- a/modules/ui_choices.py
+++ /dev/null
@@ -1,17 +0,0 @@
-"""Helpers for filtering display-only UI choice lists."""
-
-
-def filter_ui_choices(choices: list[str], preferences: list[str] | None = None, selected: str | None = None) -> tuple[list[str], bool]:
- """Return choices selected for display without changing the underlying catalog.
-
- Empty or stale preferences leave the complete list visible. A current value is
- always retained so loading a saved workflow cannot silently replace it.
- """
- available = list(dict.fromkeys(choices))
- preferred = set(preferences or [])
- filtered = [choice for choice in available if choice in preferred]
- if not filtered:
- return available, False
- if selected in available and selected not in filtered:
- filtered.append(selected)
- return filtered, len(filtered) < len(available)
diff --git a/modules/ui_common.py b/modules/ui_common.py
index ad2612868..000194773 100644
--- a/modules/ui_common.py
+++ b/modules/ui_common.py
@@ -18,6 +18,21 @@ debug('Trace: PASTE')
warn_once_set = WeakSet()
+def filter_ui_choices(choices: list[str], preferences: list[str] | None = None, selected: str | None = None) -> tuple[list[str], bool]:
+ """Return display choices while preserving the current selection."""
+ available = list(dict.fromkeys(choices))
+ preferred = set(preferences or [])
+ filtered = [choice for choice in available if choice in preferred]
+ if not filtered:
+ return available, False
+ if selected in available and selected not in filtered:
+ filtered.append(selected)
+ if 'None' in filtered:
+ filtered.remove('None')
+ filtered.insert(0, 'None')
+ return filtered, len(filtered) < len(available)
+
+
def gr_show(visible=True):
return {"visible": visible, "__type__": "update"}
diff --git a/modules/ui_definitions.py b/modules/ui_definitions.py
index 865e55f8e..ed06e2fac 100644
--- a/modules/ui_definitions.py
+++ b/modules/ui_definitions.py
@@ -596,6 +596,10 @@ def create_settings(cmd_opts):
"quicksetting_sep_images": OptionInfo("
Quicksettings
", "", gr.HTML),
"quicksettings_list": OptionInfo(["sd_model_checkpoint"], "Quicksettings list", gr.Dropdown, lambda: {"multiselect":True, "choices": list(options_templates.keys())}),
+ "selections_sep_ui": OptionInfo("Selections
", "", gr.HTML),
+ "show_samplers": OptionInfo([], "Show samplers in user interface", gr.Dropdown, lambda: {"multiselect": True, "choices": [x.name for x in list_samplers()]}, refresh=list_samplers),
+ "show_upscalers": OptionInfo([], "Show upscalers in user interface", gr.Dropdown, lambda: {"multiselect": True, "choices": list_upscalers()}, refresh=refresh_upscalers),
+
"server_sep_ui": OptionInfo("Startup & Server Options
", "", gr.HTML),
"autolaunch": OptionInfo(False, "Autolaunch browser upon startup"),
"motd": OptionInfo(False, "Show MOTD"),
@@ -774,12 +778,6 @@ def create_settings(cmd_opts):
"disable_all_extensions": OptionInfo("none", "Disable all extensions", gr.Radio, {"choices": ["none", "user", "all"]}),
}))
- # --- Sampler Settings ---
- options_templates.update(options_section(('sampler', "Sampler Settings"), {
- "show_samplers": OptionInfo([], "Show samplers in user interface", gr.CheckboxGroup, lambda: {"choices": [x.name for x in list_samplers()]}, refresh=list_samplers),
- "show_upscalers": OptionInfo([], "Show upscalers in user interface", gr.CheckboxGroup, lambda: {"choices": list_upscalers()}, refresh=refresh_upscalers),
- }))
-
# --- Hidden Options ---
options_templates.update(
options_section(
diff --git a/modules/ui_sections.py b/modules/ui_sections.py
index 4212ffce7..19ca8c127 100644
--- a/modules/ui_sections.py
+++ b/modules/ui_sections.py
@@ -2,7 +2,6 @@ import gradio as gr
from modules import shared, modelloader, ui_symbols, ui_common, sd_samplers
from modules.logger import log
from modules.ui_components import ToolButton
-from modules.ui_choices import filter_ui_choices
from modules.caption import caption
@@ -220,7 +219,7 @@ def sampler_choices(choices=None, selected='Default', same_as_primary=False):
sd_samplers.set_samplers()
choices = [sampler for sampler in sd_samplers.samplers if sampler.name != 'Same as primary']
names = [choice.name if hasattr(choice, 'name') else choice for choice in choices]
- visible, filtered = filter_ui_choices(names, shared.opts.show_samplers, selected)
+ visible, filtered = ui_common.filter_ui_choices(names, shared.opts.show_samplers, selected)
if same_as_primary:
visible.insert(0, 'Same as primary')
value = selected if selected in visible else visible[0]
@@ -229,14 +228,14 @@ def sampler_choices(choices=None, selected='Default', same_as_primary=False):
def upscaler_choices(choices, selected=None):
"""Build display-only upscaler choices without changing available upscalers."""
- return filter_ui_choices(choices, shared.opts.show_upscalers, selected)
+ return ui_common.filter_ui_choices(choices, shared.opts.show_upscalers, selected)
def create_filter_indicator(tabname, kind, filtered):
if not filtered:
return None
- indicator = gr.Button(value=f'{kind} list filtered', elem_id=f'{tabname}_{kind.lower()}_filter_indicator', elem_classes=['filter-indicator'])
- indicator.click(fn=None, _js="() => openSettingsSection('sampler')", inputs=[], outputs=[], show_progress='hidden')
+ indicator = ToolButton(value=ui_symbols.info, tooltip=f'{kind} list filtered', elem_id=f'{tabname}_{kind.lower()}_filter_indicator', elem_classes=['filter-indicator'])
+ indicator.click(fn=None, _js="() => openSettingsSection('ui')", inputs=[], outputs=[], show_progress='hidden')
return indicator
@@ -247,8 +246,8 @@ def create_sampler_and_steps_selection(choices, tabname, default_steps:int=20):
dropdown_choices, default_value, filtered = sampler_choices(choices)
with gr.Row(elem_id=f"{tabname}_sampler_row", elem_classes=['flex-break', 'flexbox']):
steps = gr.Slider(minimum=1, maximum=100, step=1, label="Steps", elem_id=f"{tabname}_steps", value=default_steps)
- create_filter_indicator(tabname, 'Sampler', filtered)
sampler_index = gr.Dropdown(label='Sampling method', elem_id=f"{tabname}_sampling", choices=dropdown_choices, value=default_value, type="value")
+ create_filter_indicator(tabname, 'Sampler', filtered)
return steps, sampler_index
@@ -372,8 +371,8 @@ def create_hires_inputs(tab):
hr_force = gr.Checkbox(label='Force HiRes', value=False, elem_id=f"{tab}_hr_force")
with gr.Row(elem_id=f"{tab}_hires_fix_row2"):
dropdown_choices, _default_value, filtered = sampler_choices(selected='Same as primary', same_as_primary=True)
- create_filter_indicator(tab, 'Sampler', filtered)
hr_sampler_index = gr.Dropdown(label='Refine sampler', elem_id=f"{tab}_sampling_alt", choices=dropdown_choices, value='Same as primary', type="value")
+ create_filter_indicator(tab, 'Sampler', filtered)
with gr.Row(elem_id=f"{tab}_hires_row2"):
hr_second_pass_steps = gr.Slider(minimum=0, maximum=99, step=1, label='HiRes steps', elem_id=f"{tab}_steps_alt", value=20)
denoising_strength = gr.Slider(minimum=0.0, maximum=0.99, step=0.01, label='Strength', value=0.3, elem_id=f"{tab}_denoising_strength")
@@ -398,8 +397,8 @@ def create_resize_inputs(tab, images, accordion=True, latent=False, non_zero=Tru
available_upscalers = [x for x in available_upscalers if not x.lower().startswith('latent')]
available_upscalers, filtered = upscaler_choices(available_upscalers, available_upscalers[0])
resize_mode = gr.Dropdown(label=f"Mode{prefix}" if non_zero else "Resize mode", elem_id=f"{tab}_resize_mode", choices=shared.resize_modes, type="index", value='Fixed')
- create_filter_indicator(tab, 'Upscaler', filtered)
resize_name = gr.Dropdown(label=f"Method{prefix}" if non_zero else "Resize method", elem_id=f"{tab}_resize_name", choices=available_upscalers, value=available_upscalers[0], visible=True)
+ create_filter_indicator(tab, 'Upscaler', filtered)
resize_context_choices = ["Add with forward", "Remove with forward", "Add with backward", "Remove with backward"]
resize_context = gr.Dropdown(label=f"Context{prefix}", elem_id=f"{tab}_resize_context", choices=resize_context_choices, value=resize_context_choices[0], visible=False)
diff --git a/test/test-ui-choice-filters.py b/test/test-ui-choice-filters.py
index 7bb975a13..fa5600aff 100644
--- a/test/test-ui-choice-filters.py
+++ b/test/test-ui-choice-filters.py
@@ -5,7 +5,7 @@ import sys
import unittest
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1]))
-from modules.ui_choices import filter_ui_choices
+from modules.ui_common import filter_ui_choices
class TestUiChoiceFilters(unittest.TestCase):
@@ -32,6 +32,11 @@ class TestUiChoiceFilters(unittest.TestCase):
self.assertEqual(visible, ['Euler', 'DPM++ 2M'])
self.assertTrue(filtered)
+ def test_none_remains_the_first_choice(self):
+ visible, filtered = filter_ui_choices(['Lanczos', 'None', 'Nearest'], ['Lanczos'], selected='None')
+ self.assertEqual(visible, ['None', 'Lanczos'])
+ self.assertTrue(filtered)
+
if __name__ == '__main__':
unittest.main()