mirror of
https://github.com/vladmandic/sdnext.git
synced 2026-01-27 15:02:48 +03:00
437 lines
22 KiB
Python
437 lines
22 KiB
Python
import os
|
|
import gradio as gr
|
|
from modules import timer, shared, paths, theme, sd_models, modelloader, generation_parameters_copypaste, call_queue, script_callbacks
|
|
from modules import ui_common, ui_loadsave, ui_history, ui_components, ui_symbols
|
|
|
|
|
|
text_settings = None # holds json of entire shared.opts
|
|
ui_system_tabs = None # required for system-info
|
|
dummy_component = gr.Textbox(visible=False, value='dummy')
|
|
loadsave = ui_loadsave.UiLoadsave(shared.cmd_opts.ui_config)
|
|
quicksettings_names = {x: i for i, x in enumerate(shared.opts.quicksettings_list) if x != 'quicksettings'}
|
|
quicksettings_list = []
|
|
hidden_list = []
|
|
components = []
|
|
|
|
|
|
def apply_setting(key, value):
|
|
if value is None:
|
|
return gr.update()
|
|
if shared.cmd_opts.freeze:
|
|
return gr.update()
|
|
if key == 'sd_backend':
|
|
return gr.update()
|
|
if key in shared.opts.disable_apply_metadata:
|
|
gr.update()
|
|
if key == "sd_model_checkpoint":
|
|
ckpt_info = sd_models.get_closest_checkpoint_match(value)
|
|
if ckpt_info is not None:
|
|
value = ckpt_info.title
|
|
else:
|
|
return gr.update()
|
|
comp_args = shared.opts.data_labels[key].component_args
|
|
if comp_args and isinstance(comp_args, dict) and comp_args.get('visible') is False:
|
|
return gr.update()
|
|
valtype = type(shared.opts.data_labels[key].default)
|
|
oldval = shared.opts.data.get(key, None)
|
|
shared.opts.data[key] = valtype(value) if valtype != type(None) else value
|
|
if oldval != value and shared.opts.data_labels[key].onchange is not None:
|
|
shared.opts.data_labels[key].onchange()
|
|
shared.opts.save()
|
|
return getattr(shared.opts, key)
|
|
|
|
|
|
def get_value_for_setting(key):
|
|
value = getattr(shared.opts, key)
|
|
info = shared.opts.data_labels[key]
|
|
args = info.component_args() if callable(info.component_args) else info.component_args or {}
|
|
args = {k: v for k, v in args.items() if k not in {'precision', 'multiselect', 'visible'}}
|
|
return gr.update(value=value, **args)
|
|
|
|
|
|
def create_setting_component(key, is_quicksettings=False):
|
|
def fun():
|
|
return shared.opts.data[key] if key in shared.opts.data else shared.opts.data_labels[key].default
|
|
|
|
info = shared.opts.data_labels[key]
|
|
t = type(info.default)
|
|
args = (info.component_args() if callable(info.component_args) else info.component_args) or {}
|
|
if 'settings' in shared.opts.ui_disabled:
|
|
args['visible'] = False
|
|
if info.component is not None:
|
|
comp = info.component
|
|
elif t == str:
|
|
comp = gr.Textbox
|
|
elif t == int:
|
|
comp = gr.Number
|
|
elif t == bool:
|
|
comp = gr.Checkbox
|
|
else:
|
|
raise ValueError(f'bad options item type: {t} for key {key}')
|
|
elem_id = f"setting_{key}"
|
|
dirty_indicator = None
|
|
|
|
if not is_quicksettings:
|
|
dirtyable_setting = gr.Group(elem_classes="dirtyable", visible=args.get("visible", True))
|
|
dirtyable_setting.__enter__()
|
|
dirty_indicator = gr.Button("", elem_classes="modification-indicator", elem_id=f"modification_indicator_{key}")
|
|
|
|
if info.refresh is not None:
|
|
if is_quicksettings:
|
|
res = comp(label=info.label, value=fun(), elem_id=elem_id, **args)
|
|
ui_common.create_refresh_button(res, info.refresh, info.component_args, f"settings_{key}_refresh")
|
|
else:
|
|
with gr.Row():
|
|
res = comp(label=info.label, value=fun(), elem_id=elem_id, **args)
|
|
ui_common.create_refresh_button(res, info.refresh, info.component_args, f"settings_{key}_refresh")
|
|
elif info.folder:
|
|
with gr.Row():
|
|
res = comp(label=info.label, value=fun(), elem_id=elem_id, elem_classes="folder-selector", **args)
|
|
else:
|
|
try:
|
|
res = comp(label=info.label, value=fun(), elem_id=elem_id, **args)
|
|
except Exception as e:
|
|
shared.log.error(f'Error creating setting: {key} {e}')
|
|
res = None
|
|
|
|
if res is not None and not is_quicksettings:
|
|
try:
|
|
res.change(fn=None, inputs=res, _js=f'(val) => markIfModified("{key}", val)')
|
|
except Exception as e:
|
|
shared.log.error(f'Quicksetting: component={res} {e}')
|
|
if dirty_indicator is not None:
|
|
dirty_indicator.click(fn=lambda: shared.opts.get_default(key), outputs=[res], show_progress='hidden')
|
|
dirtyable_setting.__exit__()
|
|
|
|
return res
|
|
|
|
def create_dirty_indicator(key, keys_to_reset, **kwargs):
|
|
def get_default_values():
|
|
values = [shared.opts.get_default(key) for key in keys_to_reset]
|
|
shared.log.debug(f'Settings restore: section={key} keys={keys_to_reset} values={values}')
|
|
return values
|
|
|
|
elements_to_reset = [shared.settings_components[_key] for _key in keys_to_reset if shared.settings_components[_key] is not None]
|
|
indicator = gr.Button('', elem_classes="modification-indicator", elem_id=f"modification_indicator_{key}", **kwargs)
|
|
indicator.click(fn=get_default_values, outputs=elements_to_reset, show_progress='full')
|
|
return indicator
|
|
|
|
|
|
def run_settings(*args):
|
|
changed = []
|
|
for key, value, comp in zip(shared.opts.data_labels.keys(), args, components):
|
|
if comp == dummy_component or value=='dummy': # or getattr(comp, 'visible', True) is False or key in hidden_list:
|
|
# actual = shared.opts.data.get(key, None) # ensure the key is in data
|
|
# default = shared.opts.data_labels[key].default
|
|
# shared.log.warning(f'Setting skip: key={key} value={value} actual={actual} default={default} comp={comp}')
|
|
continue
|
|
if not shared.opts.same_type(value, shared.opts.data_labels[key].default):
|
|
shared.log.error(f'Setting bad value: {key}={value} expecting={type(shared.opts.data_labels[key].default).__name__}')
|
|
continue
|
|
if shared.opts.set(key, value):
|
|
changed.append(key)
|
|
if shared.opts.cuda_compile_backend == "olive-ai":
|
|
from modules.onnx_impl import install_olive, initialize_onnx_pipelines
|
|
install_olive()
|
|
initialize_onnx_pipelines()
|
|
if shared.cmd_opts.use_directml:
|
|
from modules.dml import directml_override_opts
|
|
directml_override_opts()
|
|
if shared.cmd_opts.use_openvino:
|
|
if "Model" not in shared.opts.cuda_compile:
|
|
shared.log.warning("OpenVINO: Enabling Torch Compile Model")
|
|
shared.opts.cuda_compile.append("Model")
|
|
if shared.opts.cuda_compile_backend != "openvino_fx":
|
|
shared.log.warning("OpenVINO: Setting Torch Compiler backend to OpenVINO FX")
|
|
shared.opts.cuda_compile_backend = "openvino_fx"
|
|
if shared.opts.sd_backend != "diffusers":
|
|
shared.log.error('Legacy option: backend=original is no longer supported')
|
|
shared.opts.sd_backend = "diffusers"
|
|
try:
|
|
if len(changed) > 0:
|
|
shared.opts.save()
|
|
shared.log.info(f'Settings: changed={len(changed)} {changed}')
|
|
except RuntimeError:
|
|
shared.log.error(f'Settings failed: change={len(changed)} {changed}')
|
|
return shared.opts.dumpjson(), f'{len(changed)} Settings changed without save: {", ".join(changed)}'
|
|
return shared.opts.dumpjson(), f'{len(changed)} Settings changed{": " if len(changed) > 0 else ""}{", ".join(changed)}'
|
|
|
|
def run_settings_single(value, key, progress=False):
|
|
if not shared.opts.same_type(value, shared.opts.data_labels[key].default):
|
|
return gr.update(visible=True), shared.opts.dumpjson()
|
|
if not shared.opts.set(key, value):
|
|
return gr.update(value=getattr(shared.opts, key)), shared.opts.dumpjson()
|
|
if key == "cuda_compile_backend" and value == "olive-ai":
|
|
from modules.onnx_impl import install_olive
|
|
install_olive()
|
|
if shared.cmd_opts.use_directml:
|
|
from modules.dml import directml_override_opts
|
|
directml_override_opts()
|
|
shared.opts.save()
|
|
if key not in ['sd_model_checkpoint', 'sd_model_refiner', 'sd_vae', 'sd_te', 'sd_unet']:
|
|
shared.log.debug(f'Setting changed: {key}={value} progress={progress}')
|
|
return get_value_for_setting(key), shared.opts.dumpjson()
|
|
|
|
|
|
def create_ui(disabled_tabs=[]):
|
|
shared.log.debug('UI initialize: tab=settings')
|
|
global text_settings # pylint: disable=global-statement
|
|
text_settings = gr.Textbox(elem_id="settings_json", elem_classes=["settings_json"], value=lambda: shared.opts.dumpjson(), visible=False)
|
|
|
|
def unload_sd_weights():
|
|
sd_models.unload_model_weights(op='model')
|
|
sd_models.unload_model_weights(op='refiner')
|
|
|
|
def reload_sd_weights():
|
|
sd_models.reload_model_weights(force=True)
|
|
|
|
def switch_profiling():
|
|
shared.cmd_opts.profile = not shared.cmd_opts.profile
|
|
shared.log.warning(f'Profiling: {shared.cmd_opts.profile}')
|
|
return 'Stop profiling' if shared.cmd_opts.profile else 'Start profiling'
|
|
|
|
if 'system' not in disabled_tabs:
|
|
with gr.Row(elem_id="system_row"):
|
|
unload_sd_model = gr.Button(value='Unload model', variant='primary', elem_id="sett_unload_sd_model")
|
|
reload_sd_model = gr.Button(value='Reload model', variant='primary', elem_id="sett_reload_sd_model")
|
|
restart_submit = gr.Button(value="Restart server", variant='primary', elem_id="restart_submit")
|
|
shutdown_submit = gr.Button(value="Shutdown server", variant='primary', elem_id="shutdown_submit")
|
|
enable_profiling = gr.Button(value='Start profiling', variant='primary', elem_id="enable_profiling")
|
|
unload_sd_model.click(fn=unload_sd_weights, inputs=[], outputs=[])
|
|
reload_sd_model.click(fn=reload_sd_weights, inputs=[], outputs=[])
|
|
enable_profiling.click(fn=switch_profiling, inputs=[], outputs=[enable_profiling])
|
|
restart_submit.click(fn=lambda: shared.restart_server(restart=True), _js="restartReload")
|
|
shutdown_submit.click(fn=lambda: shared.restart_server(restart=False), _js="restartReload")
|
|
|
|
with gr.Tabs(elem_id="system") as system_tabs:
|
|
global ui_system_tabs # pylint: disable=global-statement
|
|
ui_system_tabs = system_tabs
|
|
with gr.TabItem("Settings", id="system_settings", elem_id="tab_settings"):
|
|
with gr.Row(elem_id="settings_row"):
|
|
settings_submit = gr.Button(value="Apply settings", variant='primary', elem_id="settings_submit")
|
|
defaults_submit = gr.Button(value="Restore defaults", variant='primary', elem_id="defaults_submit")
|
|
with gr.Row():
|
|
_settings_search = gr.Textbox(label="Search", elem_id="settings_search")
|
|
|
|
result = gr.HTML(elem_id="settings_result")
|
|
script_callbacks.ui_settings_callback() # let extensions create settings
|
|
sections = []
|
|
options_count = len(shared.opts.data_labels)
|
|
for item in shared.opts.data_labels.values(): # get unique sections from all items
|
|
if len(item.section) == 2:
|
|
section_id, section_text = item.section
|
|
elif len(item.section) == 3: # compatibility item with a1111 extensions
|
|
_category, section_id, section_text = item.section
|
|
item.section = section_id, section_text
|
|
else:
|
|
section_id = None
|
|
item.section = None, 'Hidden'
|
|
if (section_id, section_text) not in sections:
|
|
sections.append((section_id, section_text))
|
|
|
|
with gr.Tabs(elem_id="settings"):
|
|
quicksettings_list.clear()
|
|
for (section_id, section_text) in sections:
|
|
items = [item for item in shared.opts.data_labels.items() if item[1].section[0] == section_id] # find all items in this section
|
|
hidden = section_id is None or 'hidden' in section_id.lower() or 'hidden' in section_text.lower()
|
|
# shared.log.trace(f'Settings: section="{section_id}" title="{section_text}" items={len(items)} hidden={hidden}')
|
|
if hidden:
|
|
for (key, _item) in items:
|
|
hidden_list.append(key)
|
|
components.append(dummy_component)
|
|
else:
|
|
with gr.TabItem(elem_id=f"settings_section_tab_{section_id}", label=section_text):
|
|
current_items = []
|
|
for (key, item) in items:
|
|
if key in quicksettings_names:
|
|
quicksettings_list.append((key, item))
|
|
components.append(dummy_component)
|
|
else:
|
|
with gr.Row(elem_id=f"settings_section_row_{section_id}", elem_classes=["settings_section"]): # only so we can add dirty indicator at the start of the row
|
|
component = create_setting_component(key)
|
|
shared.settings_components[key] = component
|
|
current_items.append(key)
|
|
components.append(component)
|
|
create_dirty_indicator(section_id, current_items)
|
|
components_count = len(components)
|
|
if components_count != options_count:
|
|
shared.log.error(f'Settings: count mismatch: options={options_count} components={components_count}')
|
|
|
|
with gr.TabItem("Show all pages", elem_id="settings_show_all_pages"):
|
|
create_dirty_indicator("show_all_pages", [])
|
|
request_notifications = gr.Button(value='Request browser notifications', elem_id="request_notifications", visible=False)
|
|
|
|
shared.log.debug(f'Settings: sections={len(sections)} settings={len(shared.opts.list())}/{len(list(shared.opts.data_labels))} quicksettings={len(quicksettings_list)}')
|
|
|
|
if 'update' not in disabled_tabs:
|
|
with gr.TabItem("Update", id="system_update", elem_id="tab_update"):
|
|
from modules import update
|
|
update.create_ui()
|
|
|
|
if 'config' not in disabled_tabs:
|
|
with gr.TabItem("User interface", id="system_config", elem_id="tab_config"):
|
|
loadsave.create_ui()
|
|
create_dirty_indicator("tab_defaults", [], interactive=False)
|
|
|
|
if 'history' not in disabled_tabs:
|
|
with gr.TabItem("History", id="system_history", elem_id="tab_history"):
|
|
ui_history.create_ui()
|
|
|
|
if 'monitor' not in disabled_tabs:
|
|
with gr.TabItem("GPU Monitor", id="system_gpu", elem_id="tab_gpu"):
|
|
with gr.Row(elem_id='gpu-controls'):
|
|
gpu_start = gr.Button(value="Start", elem_id="gpu_start", variant="primary")
|
|
gpu_stop = gr.Button(value="Stop", elem_id="gpu_stop", variant="primary")
|
|
gpu_start.click(fn=lambda: None, _js='startGPU', inputs=[], outputs=[])
|
|
gpu_stop.click(fn=lambda: None, _js='disableGPU', inputs=[], outputs=[])
|
|
gr.HTML('''
|
|
<div class="gpu" id="gpu">
|
|
<table class="gpu-table" id="gpu-table">
|
|
<thead><tr><th></th><th></th></tr></thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
<div id="gpuChart"></div>
|
|
</div>
|
|
''', elem_id='gpu-container', visible=True)
|
|
|
|
if 'onnx' not in disabled_tabs:
|
|
with gr.TabItem("ONNX", id="onnx_config", elem_id="tab_onnx"):
|
|
from modules.onnx_impl import ui as ui_onnx
|
|
ui_onnx.create_ui()
|
|
|
|
if request_notifications:
|
|
request_notifications.click(fn=lambda: None, inputs=[], outputs=[], _js='function(){}')
|
|
settings_submit.click(
|
|
fn=call_queue.wrap_gradio_call(run_settings, extra_outputs=[gr.update()]),
|
|
inputs=components,
|
|
outputs=[text_settings, result],
|
|
)
|
|
if defaults_submit:
|
|
defaults_submit.click(fn=lambda: shared.restore_defaults(restart=True), _js="restartReload")
|
|
|
|
|
|
def reset_quicksettings(quick_components):
|
|
quick_components = quick_components.split(',')
|
|
updates = []
|
|
for key in quick_components:
|
|
shared.log.warning(f'Reset: setting={key}')
|
|
updates.append(gr.update(value=shared.opts.get_default(key)))
|
|
return updates
|
|
|
|
|
|
def create_quicksettings(interfaces):
|
|
shared.tab_names = []
|
|
for _interface, label, _ifid in interfaces:
|
|
shared.tab_names.append(label)
|
|
|
|
with gr.Blocks(theme=theme.gradio_theme, analytics_enabled=False, title="SD.Next") as ui_app:
|
|
with gr.Row(elem_id="quicksettings", variant="compact"):
|
|
quicksetting_components = []
|
|
quicksetting_keys = []
|
|
for k, _item in sorted(quicksettings_list, key=lambda x: quicksettings_names.get(x[1], x[0])):
|
|
component = create_setting_component(k, is_quicksettings=True)
|
|
quicksetting_components.append(component)
|
|
quicksetting_keys.append(k)
|
|
shared.settings_components[k] = component
|
|
quicksetting_keys = gr.State(value=','.join(quicksetting_keys), elem_id="quicksettings_keys")
|
|
btn_reset = ui_components.ToolButton(value=ui_symbols.clear, visible=True, elem_id="quicksettings_clear")
|
|
btn_reset.click(fn=reset_quicksettings, inputs=[quicksetting_keys], outputs=quicksetting_components)
|
|
|
|
generation_parameters_copypaste.connect_paste_params_buttons()
|
|
|
|
with gr.Tabs(elem_id="tabs") as tabs:
|
|
for interface, label, ifid in interfaces:
|
|
if interface is None:
|
|
continue
|
|
with gr.TabItem(label, id=ifid, elem_id=f"tab_{ifid}"):
|
|
interface.render()
|
|
for interface, _label, ifid in interfaces:
|
|
if interface is None:
|
|
continue
|
|
if ifid in ["extensions", "system"]:
|
|
continue
|
|
loadsave.add_block(interface, ifid)
|
|
loadsave.add_component(f"webui/Tabs@{tabs.elem_id}", tabs)
|
|
loadsave.setup_ui()
|
|
|
|
if shared.opts.notification_audio_enable and os.path.exists(os.path.join(paths.script_path, shared.opts.notification_audio_path)):
|
|
gr.Audio(interactive=False, value=os.path.join(paths.script_path, shared.opts.notification_audio_path), elem_id="audio_notification", visible=False)
|
|
|
|
for k, _item in quicksettings_list:
|
|
component = shared.settings_components[k]
|
|
info = shared.opts.data_labels[k]
|
|
if isinstance(component, gr.components.Textbox):
|
|
change_handlers = [component.blur, component.submit]
|
|
else:
|
|
change_handlers = [component.release if hasattr(component, 'release') else component.change]
|
|
for change_handler in change_handlers:
|
|
change_handler(
|
|
fn=lambda value, k=k, progress=info.refresh is not None: run_settings_single(value, key=k, progress=progress),
|
|
inputs=[component],
|
|
outputs=[component, text_settings],
|
|
show_progress='full' if info.refresh is not None else 'hidden',
|
|
)
|
|
|
|
button_set_checkpoint = gr.Button('Change model', elem_id='change_checkpoint', visible=False)
|
|
button_set_checkpoint.click(
|
|
fn=lambda value, _: run_settings_single(value, key='sd_model_checkpoint'),
|
|
_js="function(v){ var res = desiredCheckpointName; desiredCheckpointName = ''; return [res || v, null]; }",
|
|
inputs=[shared.settings_components['sd_model_checkpoint'], dummy_component],
|
|
outputs=[shared.settings_components['sd_model_checkpoint'], text_settings],
|
|
)
|
|
button_set_refiner = gr.Button('Change refiner', elem_id='change_refiner', visible=False)
|
|
button_set_refiner.click(
|
|
fn=lambda value, _: run_settings_single(value, key='sd_model_checkpoint'),
|
|
_js="function(v){ var res = desiredCheckpointName; desiredCheckpointName = ''; return [res || v, null]; }",
|
|
inputs=[shared.settings_components['sd_model_refiner'], dummy_component],
|
|
outputs=[shared.settings_components['sd_model_refiner'], text_settings],
|
|
)
|
|
button_set_vae = gr.Button('Change VAE', elem_id='change_vae', visible=False)
|
|
button_set_vae.click(
|
|
fn=lambda value, _: run_settings_single(value, key='sd_vae'),
|
|
_js="function(v){ var res = desiredVAEName; desiredVAEName = ''; return [res || v, null]; }",
|
|
inputs=[shared.settings_components['sd_vae'], dummy_component],
|
|
outputs=[shared.settings_components['sd_vae'], text_settings],
|
|
)
|
|
|
|
def reference_submit(model):
|
|
if '@' not in model: # diffusers
|
|
loaded = modelloader.load_reference(model)
|
|
if loaded:
|
|
shared.opts.sd_model_checkpoint = model
|
|
sd_models.reload_model_weights(force=True)
|
|
return model
|
|
return shared.opts.sd_model_checkpoint
|
|
else: # civitai
|
|
model, url = model.split('@')
|
|
loaded = modelloader.load_civitai(model, url)
|
|
if loaded is not None:
|
|
shared.opts.sd_model_checkpoint = loaded
|
|
sd_models.reload_model_weights(force=True)
|
|
return loaded
|
|
return shared.opts.sd_model_checkpoint
|
|
|
|
button_set_reference = gr.Button('Change reference', elem_id='change_reference', visible=False)
|
|
button_set_reference.click(
|
|
fn=reference_submit,
|
|
_js="function(v){ return desiredCheckpointName; }",
|
|
inputs=[shared.settings_components['sd_model_checkpoint']],
|
|
outputs=[shared.settings_components['sd_model_checkpoint']],
|
|
)
|
|
component_keys = [k for k in shared.opts.data_labels.keys() if k in shared.settings_components]
|
|
|
|
def get_settings_values():
|
|
return [get_value_for_setting(key) for key in component_keys]
|
|
|
|
ui_app.load(
|
|
fn=get_settings_values,
|
|
inputs=[],
|
|
outputs=[shared.settings_components[k] for k in component_keys if shared.settings_components[k] is not None],
|
|
queue=False,
|
|
)
|
|
|
|
timer.startup.record("ui-defaults")
|
|
loadsave.dump_defaults()
|
|
ui_app.ui_loadsave = loadsave
|
|
return ui_app
|