Fooocus/webui.py

2470 lines
148 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import gradio as gr
import random
import os
import json
import html as py_html
import time
import shared
import modules.config
import fooocus_version
import modules.html
import modules.async_worker as worker
import modules.constants as constants
import modules.flags as flags
import modules.zimage_poc
import modules.gradio_hijack as grh
import modules.style_sorter as style_sorter
import modules.style_manager as style_manager
import modules.meta_parser
import args_manager
import copy
import launch
from extras.inpaint_mask import SAMOptions
from modules.sdxl_styles import legal_style_names
from modules.ui_gradio_extensions import reload_javascript
from modules.auth import auth_enabled, check_auth
from modules.util import is_json
def get_task(*args):
args = list(args)
args.pop(0)
return worker.AsyncTask(args=args)
def generate_clicked(task: worker.AsyncTask):
import ldm_patched.modules.model_management as model_management
with model_management.interrupt_processing_mutex:
model_management.interrupt_processing = False
# outputs=[progress_html, progress_window, progress_gallery, gallery]
if len(task.args) == 0:
return
execution_start_time = time.perf_counter()
finished = False
yield gr.update(visible=True, value=modules.html.make_progress_html(1, 'Waiting for task to start ...')), \
gr.update(visible=True, value=None), \
gr.update(visible=False, value=None), \
gr.update(visible=False)
worker.async_tasks.append(task)
while not finished:
time.sleep(0.01)
if len(task.yields) > 0:
flag, product = task.yields.pop(0)
if flag == 'preview':
# help bad internet connection by skipping duplicated preview
if len(task.yields) > 0: # if we have the next item
if task.yields[0][0] == 'preview': # if the next item is also a preview
# print('Skipped one preview for better internet connection.')
continue
percentage, title, image = product
yield gr.update(visible=True, value=modules.html.make_progress_html(percentage, title)), \
gr.update(visible=True, value=image) if image is not None else gr.update(), \
gr.update(), \
gr.update(visible=False)
if flag == 'results':
yield gr.update(visible=True), \
gr.update(visible=True), \
gr.update(visible=True, value=product), \
gr.update(visible=False)
if flag == 'finish':
if not args_manager.args.disable_enhance_output_sorting:
product = sort_enhance_images(product, task)
yield gr.update(visible=False), \
gr.update(visible=False), \
gr.update(visible=False), \
gr.update(visible=True, value=product)
finished = True
# delete Fooocus temp images, only keep gradio temp images
if args_manager.args.disable_image_log:
for filepath in product:
if isinstance(filepath, str) and os.path.exists(filepath):
os.remove(filepath)
execution_time = time.perf_counter() - execution_start_time
print(f'Total time: {execution_time:.2f} seconds')
return
def sort_enhance_images(images, task):
if not task.should_enhance or len(images) <= task.images_to_enhance_count:
return images
sorted_images = []
walk_index = task.images_to_enhance_count
for index, enhanced_img in enumerate(images[:task.images_to_enhance_count]):
sorted_images.append(enhanced_img)
if index not in task.enhance_stats:
continue
target_index = walk_index + task.enhance_stats[index]
if walk_index < len(images) and target_index <= len(images):
sorted_images += images[walk_index:target_index]
walk_index += task.enhance_stats[index]
return sorted_images
def _zit_component_selector_updates(base_model_name, text_encoder_selection, vae_selection):
auto_choice = modules.zimage_poc.ZIMAGE_COMPONENT_AUTO
try:
is_zit, detection_reason = modules.zimage_poc.inspect_zimage_checkpoint_detection(
base_model_name, modules.config.paths_checkpoints
)
except Exception as e:
is_zit = False
detection_reason = f"detection error: {e}"
text_encoder_choices = [auto_choice]
vae_choices = [auto_choice]
if is_zit:
text_encoder_choices += modules.zimage_poc.list_zimage_component_choices(
"text_encoder", modules.config.paths_checkpoints
)
vae_choices += modules.zimage_poc.list_zimage_component_choices("vae", modules.config.paths_checkpoints)
text_encoder_value = text_encoder_selection if text_encoder_selection in text_encoder_choices else auto_choice
vae_value = vae_selection if vae_selection in vae_choices else auto_choice
status_text = (
f"ZIT detection: {'YES' if is_zit else 'NO'} | base model: `{base_model_name}` | "
f"{detection_reason} | text encoders: {max(0, len(text_encoder_choices) - 1)} | vaes: {max(0, len(vae_choices) - 1)}"
)
print(f"[ZIT UI] {status_text}")
return (
gr.update(visible=is_zit, choices=text_encoder_choices, value=text_encoder_value),
gr.update(visible=is_zit, choices=vae_choices, value=vae_value),
gr.update(value=status_text),
)
def inpaint_mode_change(mode, inpaint_engine_version):
assert mode in modules.flags.inpaint_options
# inpaint_additional_prompt, outpaint_selections, example_inpaint_prompts,
# inpaint_disable_initial_latent, inpaint_engine,
# inpaint_strength, inpaint_respective_field
if mode == modules.flags.inpaint_option_detail:
return [
gr.update(visible=True), gr.update(visible=False, value=[]),
gr.Dataset.update(visible=True, samples=modules.config.example_inpaint_prompts),
False, 'None', 0.5, 0.0
]
if inpaint_engine_version == 'empty':
inpaint_engine_version = modules.config.default_inpaint_engine_version
if mode == modules.flags.inpaint_option_modify:
return [
gr.update(visible=True), gr.update(visible=False, value=[]),
gr.Dataset.update(visible=False, samples=modules.config.example_inpaint_prompts),
True, inpaint_engine_version, 1.0, 0.0
]
return [
gr.update(visible=False, value=''), gr.update(visible=True),
gr.Dataset.update(visible=False, samples=modules.config.example_inpaint_prompts),
False, inpaint_engine_version, 1.0, 0.618
]
reload_javascript()
title = f'Fooocus {fooocus_version.version}'
if isinstance(args_manager.args.preset, str):
title += ' ' + args_manager.args.preset
shared.gradio_root = gr.Blocks(title=title).queue()
with shared.gradio_root:
currentTask = gr.State(worker.AsyncTask(args=[]))
inpaint_engine_state = gr.State('empty')
with gr.Row():
# Left panel for prompts
with gr.Column(scale=2, min_width=300):
gr.HTML("<h3>Prompts</h3>")
prompt = gr.Textbox(label='Positive Prompt', placeholder="Type prompt here or paste parameters.",
elem_id='positive_prompt', autofocus=True, lines=3, container=False)
default_prompt = modules.config.default_prompt
if isinstance(default_prompt, str) and default_prompt != '':
shared.gradio_root.load(lambda: default_prompt, outputs=prompt)
negative_prompt = gr.Textbox(label='Negative Prompt', placeholder="Describing what you do not want to see.",
lines=3, elem_id='negative_prompt', container=False,
value=modules.config.default_prompt_negative)
# Tags input for image organization
image_tags = gr.Textbox(label='Tags', placeholder="Enter tags separated by comma (e.g., landscape, nature, portrait)",
lines=1, elem_id='image_tags', container=False,
value='')
# Generate button
generate_button = gr.Button(label="Generate", value="Generate", elem_classes='type_row', elem_id='generate_button', visible=True)
# Control buttons row
with gr.Row():
reset_button = gr.Button(label="Reconnect", value="Reconnect", elem_classes='type_row', elem_id='reset_button', visible=False)
load_parameter_button = gr.Button(label="Load Parameters", value="Load Parameters", elem_classes='type_row', elem_id='load_parameter_button', visible=False)
with gr.Row():
skip_button = gr.Button(label="Skip", value="Skip", elem_classes='type_row_half', elem_id='skip_button', visible=False)
stop_button = gr.Button(label="Stop", value="Stop", elem_classes='type_row_half', elem_id='stop_button', visible=False)
def stop_clicked(currentTask):
import ldm_patched.modules.model_management as model_management
currentTask.last_stop = 'stop'
if (currentTask.processing):
model_management.interrupt_current_processing()
return currentTask
def skip_clicked(currentTask):
import ldm_patched.modules.model_management as model_management
currentTask.last_stop = 'skip'
if (currentTask.processing):
model_management.interrupt_current_processing()
return currentTask
stop_button.click(stop_clicked, inputs=currentTask, outputs=currentTask, queue=False, show_progress=False, _js='cancelGenerateForever')
skip_button.click(skip_clicked, inputs=currentTask, outputs=currentTask, queue=False, show_progress=False)
# Control checkboxes
with gr.Row(elem_classes='advanced_check_row'):
input_image_checkbox = gr.Checkbox(label='Input Image', value=modules.config.default_image_prompt_checkbox, container=False, elem_classes='min_check')
enhance_checkbox = gr.Checkbox(label='Enhance', value=modules.config.default_enhance_checkbox, container=False, elem_classes='min_check')
advanced_checkbox = gr.Checkbox(label='Advanced', value=modules.config.default_advanced_checkbox, container=False, elem_classes='min_check')
# Center panel for gallery and controls
with gr.Column(scale=3):
with gr.Row():
progress_window = grh.Image(label='Preview', show_label=True, visible=False, height=768,
elem_classes=['main_view'])
progress_gallery = gr.Gallery(label='Finished Images', show_label=True, object_fit='contain',
height=768, visible=False, elem_classes=['main_view', 'image_gallery'])
progress_html = gr.HTML(value=modules.html.make_progress_html(32, 'Progress 32%'), visible=False,
elem_id='progress-bar', elem_classes='progress-bar')
gallery = gr.Gallery(label='Gallery', show_label=False, object_fit='contain', visible=True, height=768,
elem_classes=['resizable_area', 'main_view', 'final_gallery', 'image_gallery'],
elem_id='final_gallery')
with gr.Row(visible=modules.config.default_image_prompt_checkbox) as image_input_panel:
with gr.Tabs(selected=modules.config.default_selected_image_input_tab_id):
with gr.Tab(label='Upscale or Variation', id='uov_tab') as uov_tab:
with gr.Row():
with gr.Column():
uov_input_image = grh.Image(label='Image', source='upload', type='numpy', show_label=False)
with gr.Column():
uov_method = gr.Radio(label='Upscale or Variation:', choices=flags.uov_list, value=modules.config.default_uov_method)
gr.HTML('<a href="https://github.com/lllyasviel/Fooocus/discussions/390" target="_blank">\U0001F4D4 Documentation</a>')
with gr.Tab(label='Image Prompt', id='ip_tab') as ip_tab:
with gr.Row():
ip_images = []
ip_types = []
ip_stops = []
ip_weights = []
ip_ctrls = []
ip_ad_cols = []
for image_count in range(modules.config.default_controlnet_image_count):
image_count += 1
with gr.Column():
ip_image = grh.Image(label='Image', source='upload', type='numpy', show_label=False, height=300, value=modules.config.default_ip_images[image_count])
ip_images.append(ip_image)
ip_ctrls.append(ip_image)
with gr.Column(visible=modules.config.default_image_prompt_advanced_checkbox) as ad_col:
with gr.Row():
ip_stop = gr.Slider(label='Stop At', minimum=0.0, maximum=1.0, step=0.001, value=modules.config.default_ip_stop_ats[image_count])
ip_stops.append(ip_stop)
ip_ctrls.append(ip_stop)
ip_weight = gr.Slider(label='Weight', minimum=0.0, maximum=2.0, step=0.001, value=modules.config.default_ip_weights[image_count])
ip_weights.append(ip_weight)
ip_ctrls.append(ip_weight)
ip_type = gr.Radio(label='Type', choices=flags.ip_list, value=modules.config.default_ip_types[image_count], container=False)
ip_types.append(ip_type)
ip_ctrls.append(ip_type)
ip_type.change(lambda x: flags.default_parameters[x], inputs=[ip_type], outputs=[ip_stop, ip_weight], queue=False, show_progress=False)
ip_ad_cols.append(ad_col)
ip_advanced = gr.Checkbox(label='Advanced', value=modules.config.default_image_prompt_advanced_checkbox, container=False)
gr.HTML('* \"Image Prompt\" is powered by Fooocus Image Mixture Engine (v1.0.1). <a href="https://github.com/lllyasviel/Fooocus/discussions/557" target="_blank">\U0001F4D4 Documentation</a>')
def ip_advance_checked(x):
return [gr.update(visible=x)] * len(ip_ad_cols) + \
[flags.default_ip] * len(ip_types) + \
[flags.default_parameters[flags.default_ip][0]] * len(ip_stops) + \
[flags.default_parameters[flags.default_ip][1]] * len(ip_weights)
ip_advanced.change(ip_advance_checked, inputs=ip_advanced,
outputs=ip_ad_cols + ip_types + ip_stops + ip_weights,
queue=False, show_progress=False)
with gr.Tab(label='Inpaint or Outpaint', id='inpaint_tab') as inpaint_tab:
with gr.Row():
with gr.Column():
inpaint_input_image = grh.Image(label='Image', source='upload', type='numpy', tool='sketch', height=500, brush_color="#FFFFFF", elem_id='inpaint_canvas', show_label=False)
inpaint_advanced_masking_checkbox = gr.Checkbox(label='Enable Advanced Masking Features', value=modules.config.default_inpaint_advanced_masking_checkbox)
inpaint_mode = gr.Dropdown(choices=modules.flags.inpaint_options, value=modules.config.default_inpaint_method, label='Method')
inpaint_additional_prompt = gr.Textbox(placeholder="Describe what you want to inpaint.", elem_id='inpaint_additional_prompt', label='Inpaint Additional Prompt', visible=False)
outpaint_selections = gr.CheckboxGroup(choices=['Left', 'Right', 'Top', 'Bottom'], value=[], label='Outpaint Direction')
example_inpaint_prompts = gr.Dataset(samples=modules.config.example_inpaint_prompts,
label='Additional Prompt Quick List',
components=[inpaint_additional_prompt],
visible=False)
gr.HTML('* Powered by Fooocus Inpaint Engine <a href="https://github.com/lllyasviel/Fooocus/discussions/414" target="_blank">\U0001F4D4 Documentation</a>')
example_inpaint_prompts.click(lambda x: x[0], inputs=example_inpaint_prompts, outputs=inpaint_additional_prompt, show_progress=False, queue=False)
with gr.Column(visible=modules.config.default_inpaint_advanced_masking_checkbox) as inpaint_mask_generation_col:
inpaint_mask_image = grh.Image(label='Mask Upload', source='upload', type='numpy', tool='sketch', height=500, brush_color="#FFFFFF", mask_opacity=1, elem_id='inpaint_mask_canvas')
invert_mask_checkbox = gr.Checkbox(label='Invert Mask When Generating', value=modules.config.default_invert_mask_checkbox)
inpaint_mask_model = gr.Dropdown(label='Mask generation model',
choices=flags.inpaint_mask_models,
value=modules.config.default_inpaint_mask_model)
inpaint_mask_cloth_category = gr.Dropdown(label='Cloth category',
choices=flags.inpaint_mask_cloth_category,
value=modules.config.default_inpaint_mask_cloth_category,
visible=False)
inpaint_mask_dino_prompt_text = gr.Textbox(label='Detection prompt', value='', visible=False, info='Use singular whenever possible', placeholder='Describe what you want to detect.')
example_inpaint_mask_dino_prompt_text = gr.Dataset(
samples=modules.config.example_enhance_detection_prompts,
label='Detection Prompt Quick List',
components=[inpaint_mask_dino_prompt_text],
visible=modules.config.default_inpaint_mask_model == 'sam')
example_inpaint_mask_dino_prompt_text.click(lambda x: x[0],
inputs=example_inpaint_mask_dino_prompt_text,
outputs=inpaint_mask_dino_prompt_text,
show_progress=False, queue=False)
with gr.Accordion("Advanced options", visible=False, open=False) as inpaint_mask_advanced_options:
inpaint_mask_sam_model = gr.Dropdown(label='SAM model', choices=flags.inpaint_mask_sam_model, value=modules.config.default_inpaint_mask_sam_model)
inpaint_mask_box_threshold = gr.Slider(label="Box Threshold", minimum=0.0, maximum=1.0, value=0.3, step=0.05)
inpaint_mask_text_threshold = gr.Slider(label="Text Threshold", minimum=0.0, maximum=1.0, value=0.25, step=0.05)
inpaint_mask_sam_max_detections = gr.Slider(label="Maximum number of detections", info="Set to 0 to detect all", minimum=0, maximum=10, value=modules.config.default_sam_max_detections, step=1, interactive=True)
generate_mask_button = gr.Button(value='Generate mask from image')
def generate_mask(image, mask_model, cloth_category, dino_prompt_text, sam_model, box_threshold, text_threshold, sam_max_detections, dino_erode_or_dilate, dino_debug):
from extras.inpaint_mask import generate_mask_from_image
extras = {}
sam_options = None
if mask_model == 'u2net_cloth_seg':
extras['cloth_category'] = cloth_category
elif mask_model == 'sam':
sam_options = SAMOptions(
dino_prompt=dino_prompt_text,
dino_box_threshold=box_threshold,
dino_text_threshold=text_threshold,
dino_erode_or_dilate=dino_erode_or_dilate,
dino_debug=dino_debug,
max_detections=sam_max_detections,
model_type=sam_model
)
mask, _, _, _ = generate_mask_from_image(image, mask_model, extras, sam_options)
return mask
inpaint_mask_model.change(lambda x: [gr.update(visible=x == 'u2net_cloth_seg')] +
[gr.update(visible=x == 'sam')] * 2 +
[gr.Dataset.update(visible=x == 'sam',
samples=modules.config.example_enhance_detection_prompts)],
inputs=inpaint_mask_model,
outputs=[inpaint_mask_cloth_category,
inpaint_mask_dino_prompt_text,
inpaint_mask_advanced_options,
example_inpaint_mask_dino_prompt_text],
queue=False, show_progress=False)
with gr.Tab(label='Describe', id='describe_tab') as describe_tab:
with gr.Row():
with gr.Column():
describe_input_image = grh.Image(label='Image', source='upload', type='numpy', show_label=False)
with gr.Column():
describe_methods = gr.CheckboxGroup(
label='Content Type',
choices=flags.describe_types,
value=modules.config.default_describe_content_type)
describe_apply_styles = gr.Checkbox(label='Apply Styles', value=modules.config.default_describe_apply_prompts_checkbox)
describe_btn = gr.Button(value='Describe this Image into Prompt')
describe_image_size = gr.Textbox(label='Image Size and Recommended Size', elem_id='describe_image_size', visible=False)
gr.HTML('<a href="https://github.com/lllyasviel/Fooocus/discussions/1363" target="_blank">\U0001F4D4 Documentation</a>')
def trigger_show_image_properties(image):
value = modules.util.get_image_size_info(image, modules.flags.sdxl_aspect_ratios)
return gr.update(value=value, visible=True)
describe_input_image.upload(trigger_show_image_properties, inputs=describe_input_image,
outputs=describe_image_size, show_progress=False, queue=False)
with gr.Tab(label='Enhance', id='enhance_tab') as enhance_tab:
with gr.Row():
with gr.Column():
enhance_input_image = grh.Image(label='Use with Enhance, skips image generation', source='upload', type='numpy')
gr.HTML('<a href="https://github.com/lllyasviel/Fooocus/discussions/3281" target="_blank">\U0001F4D4 Documentation</a>')
with gr.Tab(label='Metadata', id='metadata_tab') as metadata_tab:
with gr.Column():
metadata_input_image = grh.Image(label='For images created by Fooocus', source='upload', type='pil')
metadata_json = gr.JSON(label='Metadata')
metadata_import_button = gr.Button(value='Apply Metadata')
def trigger_metadata_preview(file):
parameters, metadata_scheme = modules.meta_parser.read_info_from_image(file)
results = {}
if parameters is not None:
results['parameters'] = parameters
if isinstance(metadata_scheme, flags.MetadataScheme):
results['metadata_scheme'] = metadata_scheme.value
return results
metadata_input_image.upload(trigger_metadata_preview, inputs=metadata_input_image,
outputs=metadata_json, queue=False, show_progress=True)
with gr.Row(visible=modules.config.default_enhance_checkbox) as enhance_input_panel:
with gr.Tabs():
with gr.Tab(label='Upscale or Variation'):
with gr.Row():
with gr.Column():
enhance_uov_method = gr.Radio(label='Upscale or Variation:', choices=flags.uov_list,
value=modules.config.default_enhance_uov_method)
enhance_uov_processing_order = gr.Radio(label='Order of Processing',
info='Use before to enhance small details and after to enhance large areas.',
choices=flags.enhancement_uov_processing_order,
value=modules.config.default_enhance_uov_processing_order)
enhance_uov_prompt_type = gr.Radio(label='Prompt',
info='Choose which prompt to use for Upscale or Variation.',
choices=flags.enhancement_uov_prompt_types,
value=modules.config.default_enhance_uov_prompt_type,
visible=modules.config.default_enhance_uov_processing_order == flags.enhancement_uov_after)
enhance_uov_processing_order.change(lambda x: gr.update(visible=x == flags.enhancement_uov_after),
inputs=enhance_uov_processing_order,
outputs=enhance_uov_prompt_type,
queue=False, show_progress=False)
gr.HTML('<a href="https://github.com/lllyasviel/Fooocus/discussions/3281" target="_blank">\U0001F4D4 Documentation</a>')
enhance_ctrls = []
enhance_inpaint_mode_ctrls = []
enhance_inpaint_engine_ctrls = []
enhance_inpaint_update_ctrls = []
for index in range(modules.config.default_enhance_tabs):
with gr.Tab(label=f'#{index + 1}') as enhance_tab_item:
enhance_enabled = gr.Checkbox(label='Enable', value=False, elem_classes='min_check',
container=False)
enhance_mask_dino_prompt_text = gr.Textbox(label='Detection prompt',
info='Use singular whenever possible',
placeholder='Describe what you want to detect.',
interactive=True,
visible=modules.config.default_enhance_inpaint_mask_model == 'sam')
example_enhance_mask_dino_prompt_text = gr.Dataset(
samples=modules.config.example_enhance_detection_prompts,
label='Detection Prompt Quick List',
components=[enhance_mask_dino_prompt_text],
visible=modules.config.default_enhance_inpaint_mask_model == 'sam')
example_enhance_mask_dino_prompt_text.click(lambda x: x[0],
inputs=example_enhance_mask_dino_prompt_text,
outputs=enhance_mask_dino_prompt_text,
show_progress=False, queue=False)
enhance_prompt = gr.Textbox(label="Enhancement positive prompt",
placeholder="Uses original prompt instead if empty.",
elem_id='enhance_prompt')
enhance_negative_prompt = gr.Textbox(label="Enhancement negative prompt",
placeholder="Uses original negative prompt instead if empty.",
elem_id='enhance_negative_prompt')
with gr.Accordion("Detection", open=False):
enhance_mask_model = gr.Dropdown(label='Mask generation model',
choices=flags.inpaint_mask_models,
value=modules.config.default_enhance_inpaint_mask_model)
enhance_mask_cloth_category = gr.Dropdown(label='Cloth category',
choices=flags.inpaint_mask_cloth_category,
value=modules.config.default_inpaint_mask_cloth_category,
visible=modules.config.default_enhance_inpaint_mask_model == 'u2net_cloth_seg',
interactive=True)
with gr.Accordion("SAM Options",
visible=modules.config.default_enhance_inpaint_mask_model == 'sam',
open=False) as sam_options:
enhance_mask_sam_model = gr.Dropdown(label='SAM model',
choices=flags.inpaint_mask_sam_model,
value=modules.config.default_inpaint_mask_sam_model,
interactive=True)
enhance_mask_box_threshold = gr.Slider(label="Box Threshold", minimum=0.0,
maximum=1.0, value=0.3, step=0.05,
interactive=True)
enhance_mask_text_threshold = gr.Slider(label="Text Threshold", minimum=0.0,
maximum=1.0, value=0.25, step=0.05,
interactive=True)
enhance_mask_sam_max_detections = gr.Slider(label="Maximum number of detections",
info="Set to 0 to detect all",
minimum=0, maximum=10,
value=modules.config.default_sam_max_detections,
step=1, interactive=True)
with gr.Accordion("Inpaint", visible=True, open=False):
enhance_inpaint_mode = gr.Dropdown(choices=modules.flags.inpaint_options,
value=modules.config.default_inpaint_method,
label='Method', interactive=True)
enhance_inpaint_disable_initial_latent = gr.Checkbox(
label='Disable initial latent in inpaint', value=False)
enhance_inpaint_engine = gr.Dropdown(label='Inpaint Engine',
value=modules.config.default_inpaint_engine_version,
choices=flags.inpaint_engine_versions,
info='Version of Fooocus inpaint model. If set, use performance Quality or Speed (no performance LoRAs) for best results.')
enhance_inpaint_strength = gr.Slider(label='Inpaint Denoising Strength',
minimum=0.0, maximum=1.0, step=0.001,
value=1.0,
info='Same as the denoising strength in A1111 inpaint. '
'Only used in inpaint, not used in outpaint. '
'(Outpaint always use 1.0)')
enhance_inpaint_respective_field = gr.Slider(label='Inpaint Respective Field',
minimum=0.0, maximum=1.0, step=0.001,
value=0.618,
info='The area to inpaint. '
'Value 0 is same as "Only Masked" in A1111. '
'Value 1 is same as "Whole Image" in A1111. '
'Only used in inpaint, not used in outpaint. '
'(Outpaint always use 1.0)')
enhance_inpaint_erode_or_dilate = gr.Slider(label='Mask Erode or Dilate',
minimum=-64, maximum=64, step=1, value=0,
info='Positive value will make white area in the mask larger, '
'negative value will make white area smaller. '
'(default is 0, always processed before any mask invert)')
enhance_mask_invert = gr.Checkbox(label='Invert Mask', value=False)
gr.HTML('<a href="https://github.com/lllyasviel/Fooocus/discussions/3281" target="_blank">\U0001F4D4 Documentation</a>')
enhance_ctrls += [
enhance_enabled,
enhance_mask_dino_prompt_text,
enhance_prompt,
enhance_negative_prompt,
enhance_mask_model,
enhance_mask_cloth_category,
enhance_mask_sam_model,
enhance_mask_text_threshold,
enhance_mask_box_threshold,
enhance_mask_sam_max_detections,
enhance_inpaint_disable_initial_latent,
enhance_inpaint_engine,
enhance_inpaint_strength,
enhance_inpaint_respective_field,
enhance_inpaint_erode_or_dilate,
enhance_mask_invert
]
enhance_inpaint_mode_ctrls += [enhance_inpaint_mode]
enhance_inpaint_engine_ctrls += [enhance_inpaint_engine]
enhance_inpaint_update_ctrls += [[
enhance_inpaint_mode, enhance_inpaint_disable_initial_latent, enhance_inpaint_engine,
enhance_inpaint_strength, enhance_inpaint_respective_field
]]
enhance_inpaint_mode.change(inpaint_mode_change, inputs=[enhance_inpaint_mode, inpaint_engine_state], outputs=[
inpaint_additional_prompt, outpaint_selections, example_inpaint_prompts,
enhance_inpaint_disable_initial_latent, enhance_inpaint_engine,
enhance_inpaint_strength, enhance_inpaint_respective_field
], show_progress=False, queue=False)
enhance_mask_model.change(
lambda x: [gr.update(visible=x == 'u2net_cloth_seg')] +
[gr.update(visible=x == 'sam')] * 2 +
[gr.Dataset.update(visible=x == 'sam',
samples=modules.config.example_enhance_detection_prompts)],
inputs=enhance_mask_model,
outputs=[enhance_mask_cloth_category, enhance_mask_dino_prompt_text, sam_options,
example_enhance_mask_dino_prompt_text],
queue=False, show_progress=False)
switch_js = "(x) => {if(x){viewer_to_bottom(100);viewer_to_bottom(500);}else{viewer_to_top();} return x;}"
down_js = "() => {viewer_to_bottom();}"
input_image_checkbox.change(lambda x: gr.update(visible=x), inputs=input_image_checkbox,
outputs=image_input_panel, queue=False, show_progress=False, _js=switch_js)
ip_advanced.change(lambda: None, queue=False, show_progress=False, _js=down_js)
current_tab = gr.Textbox(value='uov', visible=False)
uov_tab.select(lambda: 'uov', outputs=current_tab, queue=False, _js=down_js, show_progress=False)
inpaint_tab.select(lambda: 'inpaint', outputs=current_tab, queue=False, _js=down_js, show_progress=False)
ip_tab.select(lambda: 'ip', outputs=current_tab, queue=False, _js=down_js, show_progress=False)
describe_tab.select(lambda: 'desc', outputs=current_tab, queue=False, _js=down_js, show_progress=False)
enhance_tab.select(lambda: 'enhance', outputs=current_tab, queue=False, _js=down_js, show_progress=False)
metadata_tab.select(lambda: 'metadata', outputs=current_tab, queue=False, _js=down_js, show_progress=False)
enhance_checkbox.change(lambda x: gr.update(visible=x), inputs=enhance_checkbox,
outputs=enhance_input_panel, queue=False, show_progress=False, _js=switch_js)
with gr.Column(scale=2, visible=modules.config.default_advanced_checkbox) as advanced_column:
with gr.Tab(label='Settings'):
if not args_manager.args.disable_preset_selection:
preset_selection = gr.Dropdown(label='Preset',
choices=modules.config.available_presets,
value=args_manager.args.preset if args_manager.args.preset else "initial",
interactive=True)
with gr.Row():
steps_slider = gr.Slider(label='Steps', minimum=1, maximum=200, step=1, value=modules.config.default_steps,
info='Number of sampling steps for image generation')
upscale_steps_slider = gr.Slider(label='Upscale Steps', minimum=1, maximum=200, step=1, value=modules.config.default_upscale_steps,
info='Number of steps for upscaling operations')
with gr.Accordion(label='Aspect Ratios', open=False, elem_id='aspect_ratios_accordion') as aspect_ratios_accordion:
aspect_ratios_selection = gr.Radio(label='Aspect Ratios', show_label=False,
choices=modules.config.available_aspect_ratios_labels,
value=modules.config.default_aspect_ratio,
info='width × height',
elem_classes='aspect_ratios')
aspect_ratios_selection.change(lambda x: None, inputs=aspect_ratios_selection, queue=False, show_progress=False, _js='(x)=>{refresh_aspect_ratios_label(x);}')
shared.gradio_root.load(lambda x: None, inputs=aspect_ratios_selection, queue=False, show_progress=False, _js='(x)=>{refresh_aspect_ratios_label(x);}')
image_number = gr.Slider(label='Image Number', minimum=1, maximum=modules.config.default_max_image_number, step=1, value=modules.config.default_image_number)
output_format = gr.Radio(label='Output Format',
choices=flags.OutputFormat.list(),
value=modules.config.default_output_format)
# negative_prompt moved to main prompt area under the positive prompt
seed_random = gr.Checkbox(label='Random', value=True)
image_seed = gr.Textbox(label='Seed', value=0, max_lines=1, visible=False) # workaround for https://github.com/gradio-app/gradio/issues/5354
def random_checked(r):
return gr.update(visible=not r)
def refresh_seed(r, seed_string):
if r:
return random.randint(constants.MIN_SEED, constants.MAX_SEED)
else:
try:
seed_value = int(seed_string)
if constants.MIN_SEED <= seed_value <= constants.MAX_SEED:
return seed_value
except ValueError:
pass
return random.randint(constants.MIN_SEED, constants.MAX_SEED)
seed_random.change(random_checked, inputs=[seed_random], outputs=[image_seed],
queue=False, show_progress=False)
# Image Library button replaces history log
open_library_btn = gr.Button('📚 Image Library', variant='secondary', elem_id='open_library_btn', elem_classes=['open-library-btn'])
with gr.Tab(label='Styles', elem_classes=['style_selections_tab']):
style_sorter.try_load_sorted_styles(
style_names=legal_style_names,
default_selected=modules.config.default_styles)
# Style Selection Panel (visible by default)
with gr.Group(visible=True, elem_classes=['style-selection-panel']) as style_selection_panel:
style_search_bar = gr.Textbox(show_label=False, container=False,
placeholder="\U0001F50E Type here to search styles ...",
value="",
label='Search Styles')
style_selections = gr.CheckboxGroup(show_label=False, container=False,
choices=copy.deepcopy(style_sorter.all_styles),
value=copy.deepcopy(modules.config.default_styles),
label='Selected Styles',
elem_classes=['style_selections'])
gradio_receiver_style_selections = gr.Textbox(elem_id='gradio_receiver_style_selections', visible=False)
# Modify Styles button
modify_styles_btn = gr.Button('\U0001f4dd Modify Styles', variant='secondary', elem_classes=['modify-styles-btn'])
# Style Editor Panel (hidden by default, replaces selection panel)
with gr.Group(visible=False, elem_classes=['style-editor-panel']) as style_editor_panel:
gr.HTML("<h4>Style Editor</h4>")
style_editor_dropdown = gr.Dropdown(
label='Select Style',
choices=style_manager.get_styles_for_dropdown(),
interactive=True
)
with gr.Row():
style_editor_new_btn = gr.Button('\U0001f4dd New', variant='primary', size='sm')
style_editor_close_btn = gr.Button('\u2716 Done', variant='secondary', size='sm')
style_editor_name = gr.Textbox(
label='Style Name',
placeholder='Enter style name...',
interactive=True
)
style_editor_prompt = gr.Textbox(
label='Prompt Template',
placeholder='Use {prompt} as placeholder for user prompt...',
interactive=True,
lines=3
)
style_editor_negative = gr.Textbox(
label='Negative Prompt',
placeholder='Negative prompt for this style...',
interactive=True,
lines=3
)
style_editor_type = gr.HTML(visible=False)
with gr.Row():
style_editor_save_btn = gr.Button('Save Style', variant='primary')
style_editor_delete_btn = gr.Button('Delete Style', variant='stop')
style_editor_message = gr.HTML(visible=False)
# Hidden state for tracking selected style in editor
selected_style_state = gr.State('')
editor_visible_state = gr.State(False)
shared.gradio_root.load(lambda: gr.update(choices=copy.deepcopy(style_sorter.all_styles)),
outputs=style_selections)
style_search_bar.change(style_sorter.search_styles,
inputs=[style_selections, style_search_bar],
outputs=style_selections,
queue=False,
show_progress=False).then(
lambda: None, _js='()=>{refresh_style_localization();}')
gradio_receiver_style_selections.input(style_sorter.sort_styles,
inputs=style_selections,
outputs=style_selections,
queue=False,
show_progress=False).then(
lambda: None, _js='()=>{refresh_style_localization();}')
# Style Editor Event Handlers
def toggle_editor(is_visible):
# Toggle: show editor, hide selection panel (or vice versa)
return gr.Group.update(visible=is_visible), gr.Group.update(visible=not is_visible), not is_visible
def close_editor():
# Close editor, show selection panel
return gr.Group.update(visible=False), gr.Group.update(visible=True), False
def refresh_style_list():
style_manager.reload_styles()
return gr.Dropdown.update(choices=style_manager.get_styles_for_dropdown()), \
gr.CheckboxGroup.update(choices=copy.deepcopy(style_sorter.all_styles))
def on_style_editor_selected(style_name):
if not style_name:
return (
gr.Textbox.update(value='', interactive=True),
gr.Textbox.update(value='', interactive=True),
gr.Textbox.update(value='', interactive=True),
gr.HTML.update(value='', visible=False),
''
)
prompt, negative = style_manager.get_style_details(style_name)
is_user = style_manager.is_user_style(style_name)
is_system = style_manager.is_system_style(style_name)
if is_user and is_system:
# User style overriding system style
style_type_html = '<span style="color: #f59e0b; font-style: italic;">User style (overrides system)</span>'
elif is_user:
style_type_html = '<span style="color: green; font-style: italic;">User style</span>'
else:
style_type_html = '<span style="color: #888; font-style: italic;">System style template</span>'
return (
gr.Textbox.update(value=style_name, interactive=True),
gr.Textbox.update(value=prompt, interactive=True),
gr.Textbox.update(value=negative, interactive=True),
gr.HTML.update(value=style_type_html, visible=True),
style_name
)
def on_new_style():
return (
gr.Textbox.update(value='', interactive=True),
gr.Textbox.update(value='', interactive=True),
gr.Textbox.update(value='', interactive=True),
gr.HTML.update(value='<span style="color: blue; font-style: italic;">New style</span>', visible=True),
''
)
def on_save_style(old_name, new_name, prompt, negative):
if not new_name or not new_name.strip():
return '<span style="color: red;">Error: Style name cannot be empty!</span>', \
gr.Dropdown.update(), gr.CheckboxGroup.update(), gr.HTML.update(visible=True)
success, message = style_manager.save_style(new_name, prompt, negative)
if success:
return f'<span style="color: green;">{message}</span>', \
gr.Dropdown.update(choices=style_manager.get_styles_for_dropdown(), value=new_name), \
gr.CheckboxGroup.update(choices=copy.deepcopy(style_sorter.all_styles)), \
gr.HTML.update(visible=True)
else:
return f'<span style="color: red;">{message}</span>', \
gr.Dropdown.update(), gr.CheckboxGroup.update(), gr.HTML.update(visible=True)
def on_delete_style(style_name):
if not style_name:
return '<span style="color: red;">Error: No style selected!</span>', \
gr.Dropdown.update(), gr.CheckboxGroup.update(), gr.HTML.update(visible=True)
success, message = style_manager.delete_style(style_name)
if success:
return f'<span style="color: green;">{message}</span>', \
gr.Dropdown.update(choices=style_manager.get_styles_for_dropdown(), value=None), \
gr.CheckboxGroup.update(choices=copy.deepcopy(style_sorter.all_styles)), \
gr.HTML.update(visible=True)
else:
return f'<span style="color: red;">{message}</span>', \
gr.Dropdown.update(), gr.CheckboxGroup.update(), gr.HTML.update(visible=True)
# Wire up Style Editor events
modify_styles_btn.click(
toggle_editor,
inputs=[editor_visible_state],
outputs=[style_selection_panel, style_editor_panel, editor_visible_state]
)
style_editor_close_btn.click(
close_editor,
outputs=[style_editor_panel, style_selection_panel, editor_visible_state]
)
style_editor_dropdown.change(
on_style_editor_selected,
inputs=[style_editor_dropdown],
outputs=[style_editor_name, style_editor_prompt, style_editor_negative, style_editor_type, selected_style_state]
)
style_editor_new_btn.click(
on_new_style,
outputs=[style_editor_name, style_editor_prompt, style_editor_negative, style_editor_type, selected_style_state]
)
style_editor_save_btn.click(
on_save_style,
inputs=[selected_style_state, style_editor_name, style_editor_prompt, style_editor_negative],
outputs=[style_editor_message, style_editor_dropdown, style_selections, style_editor_message]
)
style_editor_delete_btn.click(
on_delete_style,
inputs=[selected_style_state],
outputs=[style_editor_message, style_editor_dropdown, style_selections, style_editor_message]
)
with gr.Tab(label='Models'):
with gr.Group():
with gr.Row():
base_model = gr.Dropdown(label='Base Model (SDXL only)', choices=modules.config.model_filenames, value=modules.config.default_base_model_name, show_label=True)
refiner_model = gr.Dropdown(label='Refiner (SDXL or SD 1.5)', choices=['None'] + modules.config.model_filenames, value=modules.config.default_refiner_model_name, show_label=True)
refiner_switch = gr.Slider(label='Refiner Switch At', minimum=0.1, maximum=1.0, step=0.0001,
info='Use 0.4 for SD1.5 realistic models; '
'or 0.667 for SD1.5 anime models; '
'or 0.8 for XL-refiners; '
'or any value for switching two SDXL models.',
value=modules.config.default_refiner_switch,
visible=modules.config.default_refiner_model_name != 'None')
refiner_model.change(lambda x: gr.update(visible=x != 'None'),
inputs=refiner_model, outputs=refiner_switch, show_progress=False, queue=False)
with gr.Row():
zit_text_encoder = gr.Dropdown(
label='ZIT Text Encoder',
choices=[modules.zimage_poc.ZIMAGE_COMPONENT_AUTO],
value=modules.zimage_poc.ZIMAGE_COMPONENT_AUTO,
visible=False,
info='Only used for ZIT/Z-Image models.',
show_label=True,
)
zit_vae = gr.Dropdown(
label='ZIT VAE',
choices=[modules.zimage_poc.ZIMAGE_COMPONENT_AUTO],
value=modules.zimage_poc.ZIMAGE_COMPONENT_AUTO,
visible=False,
info='Only used for ZIT/Z-Image models.',
show_label=True,
)
zit_selector_status = gr.Markdown(value='ZIT detection: waiting for model selection...')
with gr.Group():
lora_ctrls = []
for i, (enabled, filename, weight) in enumerate(modules.config.default_loras):
with gr.Row():
lora_enabled = gr.Checkbox(label='Enable', value=enabled,
elem_classes=['lora_enable', 'min_check'], scale=1)
lora_model = gr.Dropdown(label=f'LoRA {i + 1}',
choices=['None', modules.config.random_lora_name] + modules.config.lora_filenames, value=filename,
elem_classes='lora_model', scale=5)
lora_weight = gr.Slider(label='Weight', minimum=modules.config.default_loras_min_weight,
maximum=modules.config.default_loras_max_weight, step=0.01, value=weight,
elem_classes='lora_weight', scale=5)
lora_ctrls += [lora_enabled, lora_model, lora_weight]
with gr.Row():
refresh_files = gr.Button(label='Refresh', value='\U0001f504 Refresh All Files', variant='secondary', elem_classes='refresh_button')
with gr.Tab(label='Advanced'):
guidance_scale = gr.Slider(label='Guidance Scale', minimum=1.0, maximum=30.0, step=0.01,
value=modules.config.default_cfg_scale,
info='Higher value means style is cleaner, vivider, and more artistic.')
sharpness = gr.Slider(label='Image Sharpness', minimum=0.0, maximum=30.0, step=0.001,
value=modules.config.default_sample_sharpness,
info='Higher value means image and texture are sharper.')
gr.HTML('<a href="https://github.com/lllyasviel/Fooocus/discussions/117" target="_blank">\U0001F4D4 Documentation</a>')
dev_mode = gr.Checkbox(label='Developer Debug Mode', value=modules.config.default_developer_debug_mode_checkbox, container=False)
with gr.Column(visible=modules.config.default_developer_debug_mode_checkbox) as dev_tools:
with gr.Tab(label='Debug Tools'):
adm_scaler_positive = gr.Slider(label='Positive ADM Guidance Scaler', minimum=0.1, maximum=3.0,
step=0.001, value=1.5, info='The scaler multiplied to positive ADM (use 1.0 to disable). ')
adm_scaler_negative = gr.Slider(label='Negative ADM Guidance Scaler', minimum=0.1, maximum=3.0,
step=0.001, value=0.8, info='The scaler multiplied to negative ADM (use 1.0 to disable). ')
adm_scaler_end = gr.Slider(label='ADM Guidance End At Step', minimum=0.0, maximum=1.0,
step=0.001, value=0.3,
info='When to end the guidance from positive/negative ADM. ')
refiner_swap_method = gr.Dropdown(label='Refiner swap method', value=flags.refiner_swap_method,
choices=['joint', 'separate', 'vae'])
adaptive_cfg = gr.Slider(label='CFG Mimicking from TSNR', minimum=1.0, maximum=30.0, step=0.01,
value=modules.config.default_cfg_tsnr,
info='Enabling Fooocus\'s implementation of CFG mimicking for TSNR '
'(effective when real CFG > mimicked CFG).')
clip_skip = gr.Slider(label='CLIP Skip', minimum=1, maximum=flags.clip_skip_max, step=1,
value=modules.config.default_clip_skip,
info='Bypass CLIP layers to avoid overfitting (use 1 to not skip any layers, 2 is recommended).')
sampler_name = gr.Dropdown(label='Sampler', choices=flags.sampler_list,
value=modules.config.default_sampler)
scheduler_name = gr.Dropdown(label='Scheduler', choices=flags.scheduler_list,
value=modules.config.default_scheduler)
vae_name = gr.Dropdown(label='VAE', choices=[modules.flags.default_vae] + modules.config.vae_filenames,
value=modules.config.default_vae, show_label=True)
generate_image_grid = gr.Checkbox(label='Generate Image Grid for Each Batch',
info='(Experimental) This may cause performance problems on some computers and certain internet conditions.',
value=False)
overwrite_step = gr.Slider(label='Forced Overwrite of Sampling Step',
minimum=-1, maximum=200, step=1,
value=modules.config.default_overwrite_step,
info='Set as -1 to disable. For developer debugging.')
overwrite_switch = gr.Slider(label='Forced Overwrite of Refiner Switch Step',
minimum=-1, maximum=200, step=1,
value=modules.config.default_overwrite_switch,
info='Set as -1 to disable. For developer debugging.')
overwrite_width = gr.Slider(label='Forced Overwrite of Generating Width',
minimum=-1, maximum=2048, step=1, value=-1,
info='Set as -1 to disable. For developer debugging. '
'Results will be worse for non-standard numbers that SDXL is not trained on.')
overwrite_height = gr.Slider(label='Forced Overwrite of Generating Height',
minimum=-1, maximum=2048, step=1, value=-1,
info='Set as -1 to disable. For developer debugging. '
'Results will be worse for non-standard numbers that SDXL is not trained on.')
overwrite_vary_strength = gr.Slider(label='Forced Overwrite of Denoising Strength of "Vary"',
minimum=-1, maximum=1.0, step=0.001, value=-1,
info='Set as negative number to disable. For developer debugging.')
overwrite_upscale_strength = gr.Slider(label='Forced Overwrite of Denoising Strength of "Upscale"',
minimum=-1, maximum=1.0, step=0.001,
value=modules.config.default_overwrite_upscale,
info='Set as negative number to disable. For developer debugging.')
disable_preview = gr.Checkbox(label='Disable Preview', value=modules.config.default_black_out_nsfw,
interactive=not modules.config.default_black_out_nsfw,
info='Disable preview during generation.')
disable_intermediate_results = gr.Checkbox(label='Disable Intermediate Results',
value=False,
info='Disable intermediate results during generation, only show final gallery.')
disable_seed_increment = gr.Checkbox(label='Disable seed increment',
info='Disable automatic seed increment when image number is > 1.',
value=False)
read_wildcards_in_order = gr.Checkbox(label="Read wildcards in order", value=False)
black_out_nsfw = gr.Checkbox(label='Black Out NSFW', value=modules.config.default_black_out_nsfw,
interactive=not modules.config.default_black_out_nsfw,
info='Use black image if NSFW is detected.')
black_out_nsfw.change(lambda x: gr.update(value=x, interactive=not x),
inputs=black_out_nsfw, outputs=disable_preview, queue=False,
show_progress=False)
if not args_manager.args.disable_image_log:
save_final_enhanced_image_only = gr.Checkbox(label='Save only final enhanced image',
value=modules.config.default_save_only_final_enhanced_image)
if not args_manager.args.disable_metadata:
save_metadata_to_images = gr.Checkbox(label='Save Metadata to Images', value=modules.config.default_save_metadata_to_images,
info='Adds parameters to generated images allowing manual regeneration.')
metadata_scheme = gr.Radio(label='Metadata Scheme', choices=flags.metadata_scheme, value=modules.config.default_metadata_scheme,
info='Image Prompt parameters are not included. Use png and a1111 for compatibility with Civitai.',
visible=modules.config.default_save_metadata_to_images)
save_metadata_to_images.change(lambda x: gr.update(visible=x), inputs=[save_metadata_to_images], outputs=[metadata_scheme],
queue=False, show_progress=False)
with gr.Tab(label='Control'):
debugging_cn_preprocessor = gr.Checkbox(label='Debug Preprocessors', value=False,
info='See the results from preprocessors.')
skipping_cn_preprocessor = gr.Checkbox(label='Skip Preprocessors', value=False,
info='Do not preprocess images. (Inputs are already canny/depth/cropped-face/etc.)')
mixing_image_prompt_and_vary_upscale = gr.Checkbox(label='Mixing Image Prompt and Vary/Upscale',
value=False)
mixing_image_prompt_and_inpaint = gr.Checkbox(label='Mixing Image Prompt and Inpaint',
value=False)
controlnet_softness = gr.Slider(label='Softness of ControlNet', minimum=0.0, maximum=1.0,
step=0.001, value=0.25,
info='Similar to the Control Mode in A1111 (use 0.0 to disable). ')
with gr.Tab(label='Canny'):
canny_low_threshold = gr.Slider(label='Canny Low Threshold', minimum=1, maximum=255,
step=1, value=64)
canny_high_threshold = gr.Slider(label='Canny High Threshold', minimum=1, maximum=255,
step=1, value=128)
with gr.Tab(label='Inpaint'):
debugging_inpaint_preprocessor = gr.Checkbox(label='Debug Inpaint Preprocessing', value=False)
debugging_enhance_masks_checkbox = gr.Checkbox(label='Debug Enhance Masks', value=False,
info='Show enhance masks in preview and final results')
debugging_dino = gr.Checkbox(label='Debug GroundingDINO', value=False,
info='Use GroundingDINO boxes instead of more detailed SAM masks')
inpaint_disable_initial_latent = gr.Checkbox(label='Disable initial latent in inpaint', value=False)
inpaint_engine = gr.Dropdown(label='Inpaint Engine',
value=modules.config.default_inpaint_engine_version,
choices=flags.inpaint_engine_versions,
info='Version of Fooocus inpaint model. If set, use performance Quality or Speed (no performance LoRAs) for best results.')
inpaint_strength = gr.Slider(label='Inpaint Denoising Strength',
minimum=0.0, maximum=1.0, step=0.001, value=1.0,
info='Same as the denoising strength in A1111 inpaint. '
'Only used in inpaint, not used in outpaint. '
'(Outpaint always use 1.0)')
inpaint_respective_field = gr.Slider(label='Inpaint Respective Field',
minimum=0.0, maximum=1.0, step=0.001, value=0.618,
info='The area to inpaint. '
'Value 0 is same as "Only Masked" in A1111. '
'Value 1 is same as "Whole Image" in A1111. '
'Only used in inpaint, not used in outpaint. '
'(Outpaint always use 1.0)')
inpaint_erode_or_dilate = gr.Slider(label='Mask Erode or Dilate',
minimum=-64, maximum=64, step=1, value=0,
info='Positive value will make white area in the mask larger, '
'negative value will make white area smaller. '
'(default is 0, always processed before any mask invert)')
dino_erode_or_dilate = gr.Slider(label='GroundingDINO Box Erode or Dilate',
minimum=-64, maximum=64, step=1, value=0,
info='Positive value will make white area in the mask larger, '
'negative value will make white area smaller. '
'(default is 0, processed before SAM)')
inpaint_mask_color = gr.ColorPicker(label='Inpaint brush color', value='#FFFFFF', elem_id='inpaint_brush_color')
inpaint_ctrls = [debugging_inpaint_preprocessor, inpaint_disable_initial_latent, inpaint_engine,
inpaint_strength, inpaint_respective_field,
inpaint_advanced_masking_checkbox, invert_mask_checkbox, inpaint_erode_or_dilate]
inpaint_advanced_masking_checkbox.change(lambda x: [gr.update(visible=x)] * 2,
inputs=inpaint_advanced_masking_checkbox,
outputs=[inpaint_mask_image, inpaint_mask_generation_col],
queue=False, show_progress=False)
inpaint_mask_color.change(lambda x: gr.update(brush_color=x), inputs=inpaint_mask_color,
outputs=inpaint_input_image,
queue=False, show_progress=False)
with gr.Tab(label='FreeU'):
freeu_enabled = gr.Checkbox(label='Enabled', value=False)
freeu_b1 = gr.Slider(label='B1', minimum=0, maximum=2, step=0.01, value=1.01)
freeu_b2 = gr.Slider(label='B2', minimum=0, maximum=2, step=0.01, value=1.02)
freeu_s1 = gr.Slider(label='S1', minimum=0, maximum=4, step=0.01, value=0.99)
freeu_s2 = gr.Slider(label='S2', minimum=0, maximum=4, step=0.01, value=0.95)
freeu_ctrls = [freeu_enabled, freeu_b1, freeu_b2, freeu_s1, freeu_s2]
def dev_mode_checked(r):
return gr.update(visible=r)
dev_mode.change(dev_mode_checked, inputs=[dev_mode], outputs=[dev_tools],
queue=False, show_progress=False)
def refresh_files_clicked(current_base_model, current_zit_text_encoder, current_zit_vae):
modules.config.update_files()
results = [gr.update(choices=modules.config.model_filenames)]
results += [gr.update(choices=['None'] + modules.config.model_filenames)]
results += [gr.update(choices=[flags.default_vae] + modules.config.vae_filenames)]
results += list(
_zit_component_selector_updates(
current_base_model, current_zit_text_encoder, current_zit_vae
)
)
if not args_manager.args.disable_preset_selection:
results += [gr.update(choices=modules.config.available_presets)]
for i in range(modules.config.default_max_lora_number):
results += [gr.update(interactive=True),
gr.update(choices=['None', modules.config.random_lora_name] + modules.config.lora_filenames), gr.update()]
return results
refresh_files_output = [base_model, refiner_model, vae_name, zit_text_encoder, zit_vae]
refresh_files_output += [zit_selector_status]
if not args_manager.args.disable_preset_selection:
refresh_files_output += [preset_selection]
refresh_files.click(refresh_files_clicked, [base_model, zit_text_encoder, zit_vae], refresh_files_output + lora_ctrls,
queue=False, show_progress=False)
base_model.change(
_zit_component_selector_updates,
inputs=[base_model, zit_text_encoder, zit_vae],
outputs=[zit_text_encoder, zit_vae, zit_selector_status],
queue=False,
show_progress=False,
)
shared.gradio_root.load(
_zit_component_selector_updates,
inputs=[base_model, zit_text_encoder, zit_vae],
outputs=[zit_text_encoder, zit_vae, zit_selector_status],
queue=False,
show_progress=False,
)
# =========================================================================
# Configuration Tab - Manage application configuration
# =========================================================================
with gr.Tab(label='Configuration'):
# --- Save/Restore Buttons at TOP (always visible) ---
with gr.Row(equal_height=True):
save_config_btn = gr.Button('💾 Save Configuration', variant='primary', scale=2, size='lg')
restore_all_btn = gr.Button('↺ Restore All to Defaults', variant='secondary', scale=1, size='lg')
config_status = gr.HTML('')
gr.HTML('<hr style="margin: 10px 0;">')
# Store for config UI elements
config_ui_elements = {}
# --- Model Folders Section ---
with gr.Accordion(label='📁 Model Folders', open=True, elem_classes=['config-section']):
gr.HTML('<p style="color: var(--body-text-color-subdued); font-size: 0.9em;">Add or remove folders where models are stored. Changes take effect immediately.</p>')
# Checkpoints folders
with gr.Group():
gr.HTML('<label class="config-label">Checkpoint Folders</label>')
checkpoint_folders_state = gr.State(value=lambda: modules.config.get_model_folders('path_checkpoints'))
checkpoint_folders_display = gr.Dataframe(
headers=['Path'],
datatype=['str'],
col_count=(1, 'fixed'),
row_count=1,
value=lambda: [[p] for p in modules.config.get_model_folders('path_checkpoints')],
interactive=False,
elem_classes=['folder-display']
)
with gr.Row():
checkpoint_folder_input = gr.Textbox(label='Add Checkpoint Folder', placeholder='/path/to/checkpoints', scale=4)
checkpoint_add_btn = gr.Button('Add', variant='secondary', scale=1, elem_classes=['folder-btn'])
checkpoint_reset_btn = gr.Button('↺ Reset', variant='secondary', scale=1, elem_classes=['reset-btn'])
# LoRA folders
with gr.Group():
gr.HTML('<label class="config-label">LoRA Folders</label>')
lora_folders_display = gr.Dataframe(
headers=['Path'],
datatype=['str'],
col_count=(1, 'fixed'),
row_count=1,
value=lambda: [[p] for p in modules.config.get_model_folders('path_loras')],
interactive=False,
elem_classes=['folder-display']
)
with gr.Row():
lora_folder_input = gr.Textbox(label='Add LoRA Folder', placeholder='/path/to/loras', scale=4)
lora_add_btn = gr.Button('Add', variant='secondary', scale=1, elem_classes=['folder-btn'])
lora_reset_btn = gr.Button('↺ Reset', variant='secondary', scale=1, elem_classes=['reset-btn'])
# Other model paths (single folder)
with gr.Group():
gr.HTML('<label class="config-label">Other Model Paths</label>')
with gr.Row():
embeddings_path = gr.Textbox(label='Embeddings', value=modules.config.path_embeddings, scale=3, interactive=True)
embeddings_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
with gr.Row():
vae_path = gr.Textbox(label='VAE', value=modules.config.path_vae, scale=3, interactive=True)
vae_path_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
with gr.Row():
controlnet_path = gr.Textbox(label='ControlNet', value=modules.config.path_controlnet, scale=3, interactive=True)
controlnet_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
with gr.Row():
upscale_path = gr.Textbox(label='Upscale Models', value=modules.config.path_upscale_models, scale=3, interactive=True)
upscale_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
# --- Output & Paths Section ---
with gr.Accordion(label='📂 Output & Paths', open=False, elem_classes=['config-section']):
with gr.Row():
output_path = gr.Textbox(label='Output Path', value=modules.config.path_outputs, scale=3, interactive=True)
output_path_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
with gr.Row():
temp_path_config = gr.Textbox(label='Temp Path', value=modules.config.temp_path, scale=3, interactive=True)
temp_path_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
with gr.Row():
temp_cleanup = gr.Checkbox(label='Cleanup Temp on Launch', value=modules.config.temp_path_cleanup_on_launch, interactive=True)
temp_cleanup_reset = gr.Button('↺ Reset', variant='secondary', elem_classes=['reset-btn'])
# --- Default Models Section ---
with gr.Accordion(label='🎨 Default Models', open=False, elem_classes=['config-section']):
with gr.Row():
config_default_model = gr.Dropdown(
label='Default Base Model',
choices=modules.config.model_filenames,
value=modules.config.default_base_model_name,
scale=3,
interactive=True
)
default_model_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
with gr.Row():
config_default_refiner = gr.Dropdown(
label='Default Refiner Model',
choices=['None'] + modules.config.model_filenames,
value=modules.config.default_refiner_model_name,
scale=3,
interactive=True
)
default_refiner_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
with gr.Row():
config_refiner_switch = gr.Slider(
label='Default Refiner Switch',
minimum=0.1, maximum=1.0, step=0.01,
value=modules.config.default_refiner_switch,
scale=3,
interactive=True
)
refiner_switch_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
with gr.Row():
config_default_vae = gr.Dropdown(
label='Default VAE',
choices=[flags.default_vae] + modules.config.vae_filenames,
value=modules.config.default_vae,
scale=3,
interactive=True
)
default_vae_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
# --- Default Generation Settings Section ---
with gr.Accordion(label='⚡ Default Generation Settings', open=False, elem_classes=['config-section']):
with gr.Row():
config_default_steps = gr.Slider(
label='Default Steps',
minimum=1, maximum=200, step=1,
value=modules.config.default_steps,
scale=3,
interactive=True
)
default_steps_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
with gr.Row():
config_upscale_steps = gr.Slider(
label='Default Upscale Steps',
minimum=1, maximum=200, step=1,
value=modules.config.default_upscale_steps,
scale=3,
interactive=True
)
upscale_steps_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
with gr.Row():
config_cfg_scale = gr.Slider(
label='Default CFG Scale',
minimum=1.0, maximum=30.0, step=0.1,
value=modules.config.default_cfg_scale,
scale=3,
interactive=True
)
cfg_scale_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
with gr.Row():
config_sharpness = gr.Slider(
label='Default Sharpness',
minimum=0.0, maximum=30.0, step=0.01,
value=modules.config.default_sample_sharpness,
scale=3,
interactive=True
)
sharpness_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
with gr.Row():
config_sampler = gr.Dropdown(
label='Default Sampler',
choices=flags.sampler_list,
value=modules.config.default_sampler,
scale=3,
interactive=True
)
sampler_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
with gr.Row():
config_scheduler = gr.Dropdown(
label='Default Scheduler',
choices=flags.scheduler_list,
value=modules.config.default_scheduler,
scale=3,
interactive=True
)
scheduler_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
with gr.Row():
config_clip_skip = gr.Slider(
label='Default CLIP Skip',
minimum=1, maximum=flags.clip_skip_max, step=1,
value=modules.config.default_clip_skip,
scale=3,
interactive=True
)
clip_skip_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
with gr.Row():
config_adaptive_cfg = gr.Slider(
label='Default Adaptive CFG',
minimum=1.0, maximum=30.0, step=0.1,
value=modules.config.default_cfg_tsnr,
scale=3,
interactive=True
)
adaptive_cfg_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
# --- Default Styles Section ---
with gr.Accordion(label='🎭 Default Styles', open=False, elem_classes=['config-section']):
config_default_styles = gr.CheckboxGroup(
label='Default Styles',
choices=modules.sdxl_styles.legal_style_names,
value=modules.config.default_styles,
elem_classes=['config-styles'],
interactive=True
)
default_styles_reset = gr.Button('↺ Reset to Default Styles', variant='secondary', elem_classes=['reset-btn'])
# --- Image Settings Section ---
with gr.Accordion(label='🖼️ Image Settings', open=False, elem_classes=['config-section']):
with gr.Row():
config_image_number = gr.Slider(
label='Default Image Number',
minimum=1, maximum=modules.config.default_max_image_number, step=1,
value=modules.config.default_image_number,
scale=3,
interactive=True
)
image_number_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
with gr.Row():
config_max_images = gr.Slider(
label='Max Image Number',
minimum=1, maximum=100, step=1,
value=modules.config.default_max_image_number,
scale=3,
interactive=True
)
max_images_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
with gr.Row():
config_output_format = gr.Radio(
label='Default Output Format',
choices=flags.OutputFormat.list(),
value=modules.config.default_output_format,
scale=3,
interactive=True
)
output_format_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
with gr.Row():
config_aspect_ratio = gr.Radio(
label='Default Aspect Ratio',
choices=modules.config.available_aspect_ratios,
value=modules.config.default_aspect_ratio.replace('×', '*').split(' ')[0],
scale=3,
interactive=True
)
aspect_ratio_reset = gr.Button('', variant='secondary', scale=1, elem_classes=['reset-btn-mini'])
# --- UI Defaults Section ---
with gr.Accordion(label='🔧 UI Defaults', open=False, elem_classes=['config-section']):
with gr.Row():
config_advanced_cb = gr.Checkbox(
label='Show Advanced Panel by Default',
value=modules.config.default_advanced_checkbox,
interactive=True
)
advanced_cb_reset = gr.Button('↺ Reset', variant='secondary', elem_classes=['reset-btn'])
with gr.Row():
config_debug_mode = gr.Checkbox(
label='Developer Debug Mode by Default',
value=modules.config.default_developer_debug_mode_checkbox,
interactive=True
)
debug_mode_reset = gr.Button('↺ Reset', variant='secondary', elem_classes=['reset-btn'])
with gr.Row():
config_save_metadata = gr.Checkbox(
label='Save Metadata to Images by Default',
value=modules.config.default_save_metadata_to_images,
interactive=True
)
save_metadata_reset = gr.Button('↺ Reset', variant='secondary', elem_classes=['reset-btn'])
with gr.Row():
config_metadata_scheme = gr.Radio(
label='Default Metadata Scheme',
choices=flags.metadata_scheme,
value=modules.config.default_metadata_scheme,
visible=modules.config.default_save_metadata_to_images,
interactive=True
)
with gr.Row():
config_blackout_nsfw = gr.Checkbox(
label='Black Out NSFW by Default',
value=modules.config.default_black_out_nsfw,
interactive=True
)
blackout_nsfw_reset = gr.Button('↺ Reset', variant='secondary', elem_classes=['reset-btn'])
# =========================================================================
# Image Library Modal - Full screen overlay
# =========================================================================
with gr.Group(elem_id='image_library_modal', elem_classes=['library-modal'], visible=False) as library_modal:
with gr.Column(elem_classes=['library-modal-content']):
with gr.Row(elem_classes=['library-modal-header']):
gr.HTML('<h2>Image Library</h2>')
close_library_btn = gr.Button('×', variant='secondary', elem_classes=['library-close-btn'], scale=0, min_width=40)
# Main content: Left panel for navigation, Right panel for preview
with gr.Row():
# Left panel: Navigation and gallery
with gr.Column(scale=1, elem_classes=['library-left-panel']):
# Filter section
library_search = gr.Textbox(label='Search', placeholder='Search prompts...', elem_id='library_search')
library_tags_filter = gr.Dropdown(label='Filter Tags', multiselect=True, choices=[], value=[], elem_id='library_tags_filter')
with gr.Row():
library_refresh_btn = gr.Button('🔄 Refresh', variant='secondary', scale=0, min_width=100)
library_auto_load = gr.Checkbox(label='Auto-reload on open', value=modules.config.default_image_library_auto_load, scale=1)
library_column_slider = gr.Slider(minimum=2, maximum=6, value=3, step=1, label='Columns', elem_id='library_column_slider', scale=0, min_width=100)
# Gallery - columns will be updated by slider
library_gallery = gr.Gallery(label='Generated Images', show_label=False, elem_id='library_gallery', columns=3, rows=4, object_fit='contain', height='auto')
# Right panel: Preview and metadata
with gr.Column(scale=1, elem_classes=['library-right-panel']):
# Image preview - constrained height
library_selected_image = gr.Image(label='Selected Image', type='filepath', interactive=False, elem_id='library_preview_image')
# Metadata - scrollable
library_image_info = gr.JSON(label='Image Metadata', visible=True, elem_id='library_image_info')
# Selected images list (shown when multiple selected)
library_selected_list = gr.HTML(value='', elem_id='library_selected_list', visible=False)
# Selection count badge
library_selected_count = gr.HTML(value='', elem_id='library_selected_count', visible=False)
# Action buttons
with gr.Row():
library_load_settings_btn = gr.Button('📋 Load Settings', variant='primary', visible=False)
library_delete_btn = gr.Button('🗑️ Delete', variant='stop', visible=False)
# Tag editing (for both single and multiple images)
library_edit_tags = gr.Textbox(label='Edit Tags', placeholder='Enter tags separated by comma', visible=False)
library_save_tags_btn = gr.Button('💾 Save Tags', variant='secondary', visible=False)
# Hidden state for selected image path
library_selected_path = gr.State(value=None)
# State for multiselect - stores list of selected image paths
library_selected_paths = gr.State(value=[])
# Hidden inputs for JavaScript -> Python communication
library_checkbox_path = gr.Textbox(value='', elem_id='library_checkbox_path', visible=False)
library_checkbox_selected = gr.Checkbox(value=False, elem_id='library_checkbox_selected', visible=False)
library_checkbox_trigger = gr.Button('Checkbox Trigger', elem_id='library_checkbox_trigger', visible=False)
# =========================================================================
# Configuration Tab Event Handlers
# =========================================================================
def update_folder_display(folder_type):
"""Get updated folder list for display."""
folders = modules.config.get_model_folders(folder_type)
return gr.update(value=[[p] for p in folders])
def add_checkpoint_folder(folder_path):
"""Add a checkpoint folder and return updated display and model dropdowns."""
if not folder_path or folder_path.strip() == '':
return gr.update(), gr.update(), gr.update()
success, message, new_folders = modules.config.add_model_folder('path_checkpoints', folder_path.strip())
if success:
reload_result = modules.config.reload_model_files()
new_count = reload_result.get('model_count', 0)
print(f"{message}. Found {new_count} new models.")
print(f" Total models now: {len(modules.config.model_filenames)}")
print(f" First few: {modules.config.model_filenames[:3]}")
return (
gr.update(value=[[p] for p in new_folders]),
gr.update(choices=modules.config.model_filenames),
gr.update(choices=['None'] + modules.config.model_filenames)
)
else:
print(f"{message}")
return gr.update(), gr.update(), gr.update()
def add_lora_folder(folder_path):
"""Add a LoRA folder and return updated display."""
if not folder_path or folder_path.strip() == '':
# Return updates for folder display and all LoRA dropdowns (no change)
lora_updates = [gr.update() for _ in range(len(lora_ctrls))]
return (gr.update(), *lora_updates)
success, message, new_folders = modules.config.add_model_folder('path_loras', folder_path.strip())
if success:
reload_result = modules.config.reload_model_files()
new_count = reload_result.get('lora_count', 0)
print(f"{message}. Found {new_count} new LoRAs.")
# Return updates for folder display and all LoRA dropdowns
lora_updates = []
for i in range(modules.config.default_max_lora_number):
lora_updates.append(gr.update()) # enabled checkbox - no change
lora_updates.append(gr.update(choices=['None', modules.config.random_lora_name] + modules.config.lora_filenames)) # model dropdown
lora_updates.append(gr.update()) # weight slider - no change
return (
gr.update(value=[[p] for p in new_folders]),
*lora_updates
)
else:
print(f"{message}")
lora_updates = [gr.update() for _ in range(len(lora_ctrls))]
return (gr.update(), *lora_updates)
def reset_checkpoint_folders():
"""Reset checkpoint folders to default."""
default = modules.config.get_default_config_value('path_checkpoints')
modules.config.config_dict['path_checkpoints'] = default
modules.config.reload_model_files()
return gr.update(value=[[p] for p in default])
def reset_lora_folders():
"""Reset LoRA folders to default."""
default = modules.config.get_default_config_value('path_loras')
modules.config.config_dict['path_loras'] = default
modules.config.reload_model_files()
return gr.update(value=[[p] for p in default])
def reset_single_path(key, current_value):
"""Reset a single path config to default."""
default = modules.config.get_default_config_value(key)
if default:
modules.config.config_dict[key] = default
return gr.update(value=default)
return gr.update()
def reset_slider_value(key):
"""Reset a slider value to default."""
default = modules.config.get_default_config_value(key)
if default is not None:
modules.config.config_dict[key] = default
return gr.update(value=default)
return gr.update()
def reset_dropdown_value(key, choices=None):
"""Reset a dropdown value to default."""
default = modules.config.get_default_config_value(key)
if default is not None:
modules.config.config_dict[key] = default
return gr.update(value=default)
return gr.update()
def reset_checkbox_value(key):
"""Reset a checkbox value to default."""
default = modules.config.get_default_config_value(key)
if default is not None:
modules.config.config_dict[key] = default
return gr.update(value=default)
return gr.update()
def save_all_config():
"""Save all configuration to file."""
if modules.config.save_config():
return '<p style="color: green; padding: 10px;">✓ Configuration saved successfully!</p>'
else:
return '<p style="color: red; padding: 10px;">✗ Failed to save configuration</p>'
def restore_all_defaults():
"""Restore all configuration to defaults."""
modules.config.restore_all_to_defaults()
modules.config.reload_model_files()
return '<p style="color: green; padding: 10px;">✓ All settings restored to defaults!</p>'
# Wire up folder management events
checkpoint_add_btn.click(
add_checkpoint_folder,
inputs=[checkpoint_folder_input],
outputs=[checkpoint_folders_display, base_model, refiner_model]
)
checkpoint_reset_btn.click(
reset_checkpoint_folders,
outputs=[checkpoint_folders_display]
)
lora_add_btn.click(
add_lora_folder,
inputs=[lora_folder_input],
outputs=[lora_folders_display] + lora_ctrls
)
lora_reset_btn.click(
reset_lora_folders,
outputs=[lora_folders_display]
)
# Wire up single path resets
embeddings_reset.click(
lambda: reset_single_path('path_embeddings', modules.config.path_embeddings),
outputs=[embeddings_path]
)
vae_path_reset.click(
lambda: reset_single_path('path_vae', modules.config.path_vae),
outputs=[vae_path]
)
controlnet_reset.click(
lambda: reset_single_path('path_controlnet', modules.config.path_controlnet),
outputs=[controlnet_path]
)
upscale_reset.click(
lambda: reset_single_path('path_upscale_models', modules.config.path_upscale_models),
outputs=[upscale_path]
)
output_path_reset.click(
lambda: reset_single_path('path_outputs', modules.config.path_outputs),
outputs=[output_path]
)
temp_path_reset.click(
lambda: reset_single_path('temp_path', modules.config.temp_path),
outputs=[temp_path_config]
)
temp_cleanup_reset.click(
lambda: reset_checkbox_value('temp_path_cleanup_on_launch'),
outputs=[temp_cleanup]
)
# Wire up default model resets
default_model_reset.click(
lambda: reset_dropdown_value('default_model'),
outputs=[config_default_model]
)
default_refiner_reset.click(
lambda: reset_dropdown_value('default_refiner'),
outputs=[config_default_refiner]
)
refiner_switch_reset.click(
lambda: reset_slider_value('default_refiner_switch'),
outputs=[config_refiner_switch]
)
default_vae_reset.click(
lambda: reset_dropdown_value('default_vae'),
outputs=[config_default_vae]
)
# Wire up generation settings resets
default_steps_reset.click(
lambda: reset_slider_value('default_steps'),
outputs=[config_default_steps]
)
upscale_steps_reset.click(
lambda: reset_slider_value('default_upscale_steps'),
outputs=[config_upscale_steps]
)
cfg_scale_reset.click(
lambda: reset_slider_value('default_cfg_scale'),
outputs=[config_cfg_scale]
)
sharpness_reset.click(
lambda: reset_slider_value('default_sample_sharpness'),
outputs=[config_sharpness]
)
sampler_reset.click(
lambda: reset_dropdown_value('default_sampler'),
outputs=[config_sampler]
)
scheduler_reset.click(
lambda: reset_dropdown_value('default_scheduler'),
outputs=[config_scheduler]
)
clip_skip_reset.click(
lambda: reset_slider_value('default_clip_skip'),
outputs=[config_clip_skip]
)
adaptive_cfg_reset.click(
lambda: reset_slider_value('default_cfg_tsnr'),
outputs=[config_adaptive_cfg]
)
# Wire up styles reset
default_styles_reset.click(
lambda: reset_dropdown_value('default_styles'),
outputs=[config_default_styles]
)
# Wire up image settings resets
image_number_reset.click(
lambda: reset_slider_value('default_image_number'),
outputs=[config_image_number]
)
max_images_reset.click(
lambda: reset_slider_value('default_max_image_number'),
outputs=[config_max_images]
)
output_format_reset.click(
lambda: reset_dropdown_value('default_output_format'),
outputs=[config_output_format]
)
aspect_ratio_reset.click(
lambda: reset_dropdown_value('default_aspect_ratio'),
outputs=[config_aspect_ratio]
)
# Wire up UI defaults resets
advanced_cb_reset.click(
lambda: reset_checkbox_value('default_advanced_checkbox'),
outputs=[config_advanced_cb]
)
debug_mode_reset.click(
lambda: reset_checkbox_value('default_developer_debug_mode_checkbox'),
outputs=[config_debug_mode]
)
save_metadata_reset.click(
lambda: reset_checkbox_value('default_save_metadata_to_images'),
outputs=[config_save_metadata]
)
blackout_nsfw_reset.click(
lambda: reset_checkbox_value('default_black_out_nsfw'),
outputs=[config_blackout_nsfw]
)
# Wire up save/restore all
save_config_btn.click(save_all_config, outputs=[config_status])
restore_all_btn.click(restore_all_defaults, outputs=[config_status])
# Auto-save on config changes
def auto_save_config(key, value):
"""Update config value and auto-save."""
modules.config.update_config_value(key, value)
modules.config.save_config()
return ''
# Wire auto-save for key settings
config_default_model.change(lambda v: auto_save_config('default_model', v), inputs=[config_default_model])
config_default_refiner.change(lambda v: auto_save_config('default_refiner', v), inputs=[config_default_refiner])
config_refiner_switch.change(lambda v: auto_save_config('default_refiner_switch', v), inputs=[config_refiner_switch])
config_default_vae.change(lambda v: auto_save_config('default_vae', v), inputs=[config_default_vae])
config_default_steps.change(lambda v: auto_save_config('default_steps', v), inputs=[config_default_steps])
config_upscale_steps.change(lambda v: auto_save_config('default_upscale_steps', v), inputs=[config_upscale_steps])
config_cfg_scale.change(lambda v: auto_save_config('default_cfg_scale', v), inputs=[config_cfg_scale])
config_sharpness.change(lambda v: auto_save_config('default_sample_sharpness', v), inputs=[config_sharpness])
config_sampler.change(lambda v: auto_save_config('default_sampler', v), inputs=[config_sampler])
config_scheduler.change(lambda v: auto_save_config('default_scheduler', v), inputs=[config_scheduler])
config_clip_skip.change(lambda v: auto_save_config('default_clip_skip', v), inputs=[config_clip_skip])
config_adaptive_cfg.change(lambda v: auto_save_config('default_cfg_tsnr', v), inputs=[config_adaptive_cfg])
config_default_styles.change(lambda v: auto_save_config('default_styles', v), inputs=[config_default_styles])
config_image_number.change(lambda v: auto_save_config('default_image_number', v), inputs=[config_image_number])
config_max_images.change(lambda v: auto_save_config('default_max_image_number', v), inputs=[config_max_images])
config_output_format.change(lambda v: auto_save_config('default_output_format', v), inputs=[config_output_format])
config_aspect_ratio.change(lambda v: auto_save_config('default_aspect_ratio', v), inputs=[config_aspect_ratio])
config_advanced_cb.change(lambda v: auto_save_config('default_advanced_checkbox', v), inputs=[config_advanced_cb])
config_debug_mode.change(lambda v: auto_save_config('default_developer_debug_mode_checkbox', v), inputs=[config_debug_mode])
config_save_metadata.change(lambda v: auto_save_config('default_save_metadata_to_images', v), inputs=[config_save_metadata])
config_metadata_scheme.change(lambda v: auto_save_config('default_metadata_scheme', v), inputs=[config_metadata_scheme])
config_blackout_nsfw.change(lambda v: auto_save_config('default_black_out_nsfw', v), inputs=[config_blackout_nsfw])
library_auto_load.change(lambda v: auto_save_config('default_image_library_auto_load', v), inputs=[library_auto_load])
# Wire auto-save for path settings
embeddings_path.change(lambda v: auto_save_config('path_embeddings', v), inputs=[embeddings_path])
vae_path.change(lambda v: auto_save_config('path_vae', v), inputs=[vae_path])
controlnet_path.change(lambda v: auto_save_config('path_controlnet', v), inputs=[controlnet_path])
upscale_path.change(lambda v: auto_save_config('path_upscale_models', v), inputs=[upscale_path])
output_path.change(lambda v: auto_save_config('path_outputs', v), inputs=[output_path])
temp_path_config.change(lambda v: auto_save_config('temp_path', v), inputs=[temp_path_config])
temp_cleanup.change(lambda v: auto_save_config('temp_path_cleanup_on_launch', v), inputs=[temp_cleanup])
state_is_generating = gr.State(False)
load_data_outputs = [advanced_checkbox, image_number, prompt, negative_prompt, style_selections,
steps_slider, upscale_steps_slider, overwrite_step, overwrite_switch, aspect_ratios_selection,
overwrite_width, overwrite_height, guidance_scale, sharpness, adm_scaler_positive,
adm_scaler_negative, adm_scaler_end, refiner_swap_method, adaptive_cfg, clip_skip,
base_model, refiner_model, refiner_switch, sampler_name, scheduler_name, vae_name,
seed_random, image_seed, inpaint_engine, inpaint_engine_state,
inpaint_mode] + enhance_inpaint_mode_ctrls + [generate_button,
load_parameter_button] + freeu_ctrls + lora_ctrls
# =========================================================================
# Image Library Event Handlers
# =========================================================================
def get_image_library():
"""Get shared image library instance."""
from modules.image_library import get_library
return get_library()
def build_gallery_items(images):
"""
Convert image info list to gallery items.
Keep relative path as caption so we can resolve real file paths on selection.
"""
return [[img['path'], img.get('rel_path', img.get('filename', ''))] for img in images]
def extract_gallery_selection(selected) -> tuple[str, str]:
"""Extract raw image path and caption from Gradio gallery value."""
if isinstance(selected, dict):
image_path = selected.get('image', selected.get('name', ''))
caption = selected.get('caption', '')
elif isinstance(selected, (list, tuple)):
image_path = selected[0] if len(selected) > 0 else ''
caption = selected[1] if len(selected) > 1 else ''
else:
image_path = str(selected)
caption = ''
while isinstance(image_path, dict):
image_path = image_path.get('path', image_path.get('name', image_path.get('image', '')))
while isinstance(caption, dict):
caption = caption.get('caption', caption.get('name', ''))
if not isinstance(image_path, str):
image_path = str(image_path) if image_path else ''
if not isinstance(caption, str):
caption = str(caption) if caption else ''
return image_path, caption
def resolve_library_image_path(raw_path: str, caption: str = '') -> str:
"""
Resolve gallery-selected path to canonical file path under the configured output folder.
This avoids deleting transient display-cache files.
"""
lib = get_image_library()
output_folder = lib.get_output_folder()
candidates = []
if caption:
if os.path.isabs(caption):
candidates.append(caption)
elif output_folder:
candidates.append(os.path.join(output_folder, caption))
if raw_path:
candidates.append(raw_path)
if output_folder and not os.path.isabs(raw_path):
candidates.append(os.path.join(output_folder, raw_path))
for candidate in candidates:
try:
resolved = os.path.realpath(os.path.expanduser(candidate))
except Exception:
continue
if os.path.isfile(resolved):
return resolved
filename = os.path.basename(raw_path) or os.path.basename(caption)
if filename:
matches = [img['path'] for img in lib.scan_images(force_refresh=True) if img.get('filename') == filename]
if len(matches) == 1:
return matches[0]
return raw_path
def parse_library_path_payload(path_payload) -> tuple[str, str]:
"""Parse JS payload that may contain JSON with path/caption."""
if isinstance(path_payload, dict):
raw_path = path_payload.get('path', '')
caption = path_payload.get('caption', '')
return str(raw_path or ''), str(caption or '')
if not isinstance(path_payload, str):
return str(path_payload or ''), ''
payload = path_payload.strip()
if not payload:
return '', ''
if payload.startswith('{'):
try:
parsed = json.loads(payload)
if isinstance(parsed, dict):
return str(parsed.get('path', '') or ''), str(parsed.get('caption', '') or '')
except Exception:
pass
return payload, ''
def normalize_selected_paths(selected_paths) -> list[str]:
"""Normalize and deduplicate selected paths."""
normalized = []
seen = set()
for item in selected_paths or []:
raw_path, caption = parse_library_path_payload(item)
resolved = resolve_library_image_path(raw_path, caption)
if not resolved:
continue
try:
canonical = os.path.realpath(os.path.expanduser(resolved))
except Exception:
continue
if not os.path.isfile(canonical):
continue
if canonical in seen:
continue
normalized.append(canonical)
seen.add(canonical)
return normalized
def build_selected_list_html(selected_paths: list[str]) -> str:
list_html = '<div class="selected-images-list">'
for path in selected_paths:
filename = py_html.escape(os.path.basename(path) if path else 'Unknown')
list_html += f'<div class="selected-image-item"><span class="selected-image-name">{filename}</span></div>'
list_html += '</div>'
return list_html
def library_refresh_images(search_text=None, filter_tags=None):
"""Refresh the image library gallery."""
lib = get_image_library()
images = lib.scan_images(force_refresh=True)
# Filter by search text
if search_text:
images = lib.filter_images(images, search=search_text)
# Filter by tags
if filter_tags:
images = lib.filter_images(images, tags=filter_tags)
# Get all available tags for dropdown
all_tags = list(lib.get_all_tags().keys())
# Return gallery images and tag choices
gallery_images = build_gallery_items(images)
return gr.update(value=gallery_images), gr.update(choices=all_tags)
def library_on_image_select(evt: gr.SelectData, gallery_value, selected_paths):
"""Handle image selection in gallery.
Behavior:
- Click on image (not checkbox): Show preview, single select mode
- Checkbox handled by JavaScript for multi-select
"""
if gallery_value and evt.index < len(gallery_value):
selected = gallery_value[evt.index]
raw_path, caption = extract_gallery_selection(selected)
resolved_path = resolve_library_image_path(raw_path, caption)
# Single select mode - show preview
lib = get_image_library()
info = lib.get_image_info(resolved_path)
if info is None:
return gr.update(), None, gr.update(visible=False), gr.update(visible=False, value='🗑️ Delete'), gr.update(visible=False), gr.update(visible=False), None, [], gr.update(visible=False), gr.update(visible=False)
# Get current tags - handle both list and string formats
tags = info.get('tags', [])
if isinstance(tags, list):
current_tags = ', '.join(tags)
else:
current_tags = str(tags) if tags else ''
# Return metadata for JSON display
metadata = info.get('metadata', {})
return (
gr.update(value=info['path']),
metadata,
gr.update(visible=True),
gr.update(visible=True, value='🗑️ Delete'),
gr.update(visible=True, value=current_tags),
gr.update(visible=True),
info['path'],
[], # Clear multiselect when single-selecting
gr.update(visible=False),
gr.update(visible=False) # Hide selected list
)
return gr.update(), None, gr.update(visible=False), gr.update(visible=False, value='🗑️ Delete'), gr.update(visible=False), gr.update(visible=False), None, selected_paths, gr.update(visible=False), gr.update(visible=False)
def library_update_multiselect(selected_paths):
"""Update UI when multi-selection changes via checkboxes."""
selected_paths = normalize_selected_paths(selected_paths)
count = len(selected_paths)
if count == 0:
return (
gr.update(visible=False), # count
gr.update(visible=False, value='🗑️ Delete'), # delete btn
gr.update(visible=False, value=''), # selected list
gr.update(visible=False), # edit tags
gr.update(visible=False) # save tags btn
)
# Build selected images list HTML
list_html = build_selected_list_html(selected_paths)
count_html = f'<span class="selected-count-badge">{count} image{"s" if count != 1 else ""} selected</span>'
return (
gr.update(value=count_html, visible=True), # count
gr.update(visible=True, value=f'🗑️ Delete Selected ({count})'), # delete btn
gr.update(value=list_html, visible=True), # selected list
gr.update(visible=True), # edit tags
gr.update(visible=True) # save tags btn
)
def library_multiselect_checkbox(is_selected, image_path, selected_paths):
"""Handle checkbox click for multi-select."""
print(f"[Library] Checkbox click: is_selected={is_selected}, image_path={image_path}, current_selected={selected_paths}")
selected_paths = normalize_selected_paths(selected_paths)
if not image_path:
count_update, delete_update, list_update, tags_update, save_update = library_update_multiselect(selected_paths)
return selected_paths, count_update, delete_update, list_update, tags_update, save_update
raw_path, caption = parse_library_path_payload(image_path)
print(f"[Library] Parsed: raw_path={raw_path}, caption={caption}")
# Resolve the path
resolved_path = resolve_library_image_path(raw_path, caption)
print(f"[Library] Resolved path: {resolved_path}")
if not resolved_path:
count_update, delete_update, list_update, tags_update, save_update = library_update_multiselect(selected_paths)
return selected_paths, count_update, delete_update, list_update, tags_update, save_update
resolved_path = os.path.realpath(os.path.expanduser(resolved_path))
if not os.path.isfile(resolved_path):
print(f"[Library] Ignoring non-file path from checkbox: {resolved_path}")
count_update, delete_update, list_update, tags_update, save_update = library_update_multiselect(selected_paths)
return selected_paths, count_update, delete_update, list_update, tags_update, save_update
if is_selected:
# Add to selection
if resolved_path not in selected_paths:
selected_paths = selected_paths + [resolved_path]
else:
# Remove from selection
selected_paths = [p for p in selected_paths if p != resolved_path]
print(f"[Library] Updated selected_paths: {selected_paths}")
selected_paths = normalize_selected_paths(selected_paths)
count_update, delete_update, list_update, tags_update, save_update = library_update_multiselect(selected_paths)
return (
selected_paths,
count_update,
delete_update,
list_update,
tags_update,
save_update
)
def library_load_settings(image_info):
"""Load settings from selected image to the generator."""
if not image_info:
return [gr.update()] * len(load_data_outputs)
# The image_info is already the metadata dict from library_on_image_select
# We need to convert it to the format expected by load_parameter_button_click
import json
params = json.dumps(image_info) if isinstance(image_info, dict) else str(image_info)
try:
return modules.meta_parser.load_parameter_button_click(params, False, inpaint_mode=None)
except Exception as e:
print(f"Error loading settings: {e}")
return [gr.update()] * len(load_data_outputs)
def library_save_tags(image_path, new_tags, selected_paths):
"""Save tags to the selected image(s)."""
lib = get_image_library()
# Convert comma-separated tags to list
if isinstance(new_tags, str):
tags_list = [t.strip() for t in new_tags.split(',') if t.strip()]
else:
tags_list = new_tags
# If multiple images selected, apply tags to all
if selected_paths and len(selected_paths) > 0:
for path in selected_paths:
lib.update_image_tags(path, tags_list)
# Refresh tags dropdown
all_tags = list(lib.get_all_tags().keys())
return gr.update(choices=all_tags)
elif image_path:
# Single image
success = lib.update_image_tags(image_path, tags_list)
if success:
all_tags = list(lib.get_all_tags().keys())
return gr.update(choices=all_tags)
return gr.update()
def library_delete_action(image_path, selected_paths):
"""Delete selected image(s) using a single button for single and multiselect modes."""
selected_paths = normalize_selected_paths(selected_paths)
lib = get_image_library()
if selected_paths:
print(f"[Library] Deleting {len(selected_paths)} selected image(s)")
success_count, failed_paths = lib.delete_images(selected_paths)
print(f"[Library] Deleted {success_count} image(s), {len(failed_paths)} failed")
else:
print(f"[Library] Delete requested for: {image_path}")
if not image_path:
print("[Library] No image path provided")
return (
gr.update(),
gr.update(),
image_path,
selected_paths,
gr.update(),
gr.update(),
gr.update()
)
resolved_path = resolve_library_image_path(image_path)
print(f"[Library] Attempting to delete: {resolved_path}")
success = lib.delete_image(resolved_path)
print(f"[Library] Delete result: {success}")
if success:
success_count, failed_paths = 1, []
else:
success_count, failed_paths = 0, [resolved_path]
if success_count == 0:
return (
gr.update(),
gr.update(),
image_path,
selected_paths,
gr.update(),
gr.update(),
gr.update()
)
lib.clear_cache()
images = lib.scan_images(force_refresh=True)
gallery_images = build_gallery_items(images)
print(f"[Library] Gallery refreshed with {len(gallery_images)} images")
return (
gr.update(value=gallery_images),
gr.update(value=None),
None,
[],
gr.update(visible=False, value=''),
gr.update(visible=False, value=''),
gr.update(visible=False, value='🗑️ Delete')
)
# Modal open/close handlers
def open_library_modal(auto_load):
if auto_load:
lib = get_image_library()
images = lib.scan_images(force_refresh=True)
gallery_images = build_gallery_items(images)
tags = lib.get_all_tags()
return gr.update(visible=True), gr.update(value=gallery_images), gr.update(choices=list(tags.keys()))
return gr.update(visible=True), gr.update(), gr.update()
def close_library_modal():
return gr.update(visible=False)
open_library_btn.click(open_library_modal, inputs=[library_auto_load], outputs=[library_modal, library_gallery, library_tags_filter])
close_library_btn.click(close_library_modal, outputs=[library_modal])
# Wire up Image Library events
library_refresh_btn.click(
library_refresh_images,
inputs=[library_search, library_tags_filter],
outputs=[library_gallery, library_tags_filter]
)
# Column slider updates gallery columns
library_column_slider.change(
lambda cols: gr.update(columns=int(cols)),
inputs=[library_column_slider],
outputs=[library_gallery]
)
library_gallery.select(
library_on_image_select,
inputs=[library_gallery, library_selected_paths],
outputs=[library_selected_image, library_image_info, library_load_settings_btn,
library_delete_btn, library_edit_tags, library_save_tags_btn, library_selected_path,
library_selected_paths, library_selected_count, library_selected_list],
_js='(gallery, selected_paths) => { if (typeof clearAllSelections === "function") { clearAllSelections(); } return [gallery, selected_paths]; }'
)
# Checkbox trigger for multi-select
library_checkbox_trigger.click(
library_multiselect_checkbox,
inputs=[library_checkbox_selected, library_checkbox_path, library_selected_paths],
outputs=[library_selected_paths, library_selected_count, library_delete_btn,
library_selected_list, library_edit_tags, library_save_tags_btn]
)
library_load_settings_btn.click(
library_load_settings,
inputs=[library_image_info],
outputs=load_data_outputs
)
library_save_tags_btn.click(
library_save_tags,
inputs=[library_selected_path, library_edit_tags, library_selected_paths],
outputs=[library_tags_filter]
)
library_delete_btn.click(
library_delete_action,
inputs=[library_selected_path, library_selected_paths],
outputs=[library_gallery, library_selected_image, library_selected_path,
library_selected_paths, library_selected_count, library_selected_list, library_delete_btn],
_js='(selected_path, selected_paths) => { if (typeof clearAllSelections === "function") { clearAllSelections(); } return [selected_path, selected_paths]; }'
)
# Search and filter events
library_search.submit(
library_refresh_images,
inputs=[library_search, library_tags_filter],
outputs=[library_gallery, library_tags_filter]
)
library_tags_filter.change(
library_refresh_images,
inputs=[library_search, library_tags_filter],
outputs=[library_gallery, library_tags_filter]
)
if not args_manager.args.disable_preset_selection:
def preset_selection_change(preset, is_generating, inpaint_mode):
preset_content = modules.config.try_get_preset_content(preset) if preset != 'initial' else {}
preset_prepared = modules.meta_parser.parse_meta_from_preset(preset_content)
default_model = preset_prepared.get('base_model')
previous_default_models = preset_prepared.get('previous_default_models', [])
checkpoint_downloads = preset_prepared.get('checkpoint_downloads', {})
embeddings_downloads = preset_prepared.get('embeddings_downloads', {})
lora_downloads = preset_prepared.get('lora_downloads', {})
vae_downloads = preset_prepared.get('vae_downloads', {})
preset_prepared['base_model'], preset_prepared['checkpoint_downloads'] = launch.download_models(
default_model, previous_default_models, checkpoint_downloads, embeddings_downloads, lora_downloads,
vae_downloads)
if 'prompt' in preset_prepared and preset_prepared.get('prompt') == '':
del preset_prepared['prompt']
return modules.meta_parser.load_parameter_button_click(json.dumps(preset_prepared), is_generating, inpaint_mode)
def inpaint_engine_state_change(inpaint_engine_version, *args):
if inpaint_engine_version == 'empty':
inpaint_engine_version = modules.config.default_inpaint_engine_version
result = []
for inpaint_mode in args:
if inpaint_mode != modules.flags.inpaint_option_detail:
result.append(gr.update(value=inpaint_engine_version))
else:
result.append(gr.update())
return result
preset_selection.change(preset_selection_change, inputs=[preset_selection, state_is_generating, inpaint_mode], outputs=load_data_outputs, queue=False, show_progress=True) \
.then(fn=style_sorter.sort_styles, inputs=style_selections, outputs=style_selections, queue=False, show_progress=False) \
.then(lambda: None, _js='()=>{refresh_style_localization();}') \
.then(inpaint_engine_state_change, inputs=[inpaint_engine_state] + enhance_inpaint_mode_ctrls, outputs=enhance_inpaint_engine_ctrls, queue=False, show_progress=False)
output_format.input(lambda x: gr.update(output_format=x), inputs=output_format)
advanced_checkbox.change(lambda x: gr.update(visible=x), advanced_checkbox, advanced_column,
queue=False, show_progress=False) \
.then(fn=lambda: None, _js='refresh_grid_delayed', queue=False, show_progress=False)
inpaint_mode.change(inpaint_mode_change, inputs=[inpaint_mode, inpaint_engine_state], outputs=[
inpaint_additional_prompt, outpaint_selections, example_inpaint_prompts,
inpaint_disable_initial_latent, inpaint_engine,
inpaint_strength, inpaint_respective_field
], show_progress=False, queue=False)
# load configured default_inpaint_method
default_inpaint_ctrls = [inpaint_mode, inpaint_disable_initial_latent, inpaint_engine, inpaint_strength, inpaint_respective_field]
for mode, disable_initial_latent, engine, strength, respective_field in [default_inpaint_ctrls] + enhance_inpaint_update_ctrls:
shared.gradio_root.load(inpaint_mode_change, inputs=[mode, inpaint_engine_state], outputs=[
inpaint_additional_prompt, outpaint_selections, example_inpaint_prompts, disable_initial_latent,
engine, strength, respective_field
], show_progress=False, queue=False)
generate_mask_button.click(fn=generate_mask,
inputs=[inpaint_input_image, inpaint_mask_model, inpaint_mask_cloth_category,
inpaint_mask_dino_prompt_text, inpaint_mask_sam_model,
inpaint_mask_box_threshold, inpaint_mask_text_threshold,
inpaint_mask_sam_max_detections, dino_erode_or_dilate, debugging_dino],
outputs=inpaint_mask_image, show_progress=True, queue=True)
ctrls = [currentTask, generate_image_grid]
ctrls += [
prompt, negative_prompt, style_selections,
steps_slider, upscale_steps_slider, aspect_ratios_selection, image_number, output_format, image_seed,
read_wildcards_in_order, sharpness, guidance_scale
]
ctrls += [base_model, refiner_model, refiner_switch] + lora_ctrls
ctrls += [input_image_checkbox, current_tab]
ctrls += [uov_method, uov_input_image]
ctrls += [outpaint_selections, inpaint_input_image, inpaint_additional_prompt, inpaint_mask_image]
ctrls += [disable_preview, disable_intermediate_results, disable_seed_increment, black_out_nsfw]
ctrls += [adm_scaler_positive, adm_scaler_negative, adm_scaler_end, adaptive_cfg, clip_skip]
ctrls += [sampler_name, scheduler_name, vae_name, zit_text_encoder, zit_vae]
ctrls += [overwrite_step, overwrite_switch, overwrite_width, overwrite_height, overwrite_vary_strength]
ctrls += [overwrite_upscale_strength, mixing_image_prompt_and_vary_upscale, mixing_image_prompt_and_inpaint]
ctrls += [debugging_cn_preprocessor, skipping_cn_preprocessor, canny_low_threshold, canny_high_threshold]
ctrls += [refiner_swap_method, controlnet_softness]
ctrls += freeu_ctrls
ctrls += inpaint_ctrls
if not args_manager.args.disable_image_log:
ctrls += [save_final_enhanced_image_only]
if not args_manager.args.disable_metadata:
ctrls += [save_metadata_to_images, metadata_scheme]
ctrls += ip_ctrls
ctrls += [debugging_dino, dino_erode_or_dilate, debugging_enhance_masks_checkbox,
enhance_input_image, enhance_checkbox, enhance_uov_method, enhance_uov_processing_order,
enhance_uov_prompt_type]
ctrls += enhance_ctrls
# Add tags input
ctrls += [image_tags]
def parse_meta(raw_prompt_txt, is_generating):
loaded_json = None
if is_json(raw_prompt_txt):
loaded_json = json.loads(raw_prompt_txt)
if loaded_json is None:
if is_generating:
return gr.update(), gr.update(), gr.update()
else:
return gr.update(), gr.update(visible=True), gr.update(visible=False)
return json.dumps(loaded_json), gr.update(visible=False), gr.update(visible=True)
prompt.input(parse_meta, inputs=[prompt, state_is_generating], outputs=[prompt, generate_button, load_parameter_button], queue=False, show_progress=False)
load_parameter_button.click(modules.meta_parser.load_parameter_button_click, inputs=[prompt, state_is_generating, inpaint_mode], outputs=load_data_outputs, queue=False, show_progress=False)
def trigger_metadata_import(file, state_is_generating):
parameters, metadata_scheme = modules.meta_parser.read_info_from_image(file)
if parameters is None:
print('Could not find metadata in the image!')
parsed_parameters = {}
else:
metadata_parser = modules.meta_parser.get_metadata_parser(metadata_scheme)
parsed_parameters = metadata_parser.to_json(parameters)
return modules.meta_parser.load_parameter_button_click(parsed_parameters, state_is_generating, inpaint_mode)
metadata_import_button.click(trigger_metadata_import, inputs=[metadata_input_image, state_is_generating], outputs=load_data_outputs, queue=False, show_progress=True) \
.then(style_sorter.sort_styles, inputs=style_selections, outputs=style_selections, queue=False, show_progress=False)
generate_button.click(lambda: (gr.update(visible=True, interactive=True), gr.update(visible=True, interactive=True), gr.update(visible=False, interactive=False), [], True),
outputs=[stop_button, skip_button, generate_button, gallery, state_is_generating]) \
.then(fn=refresh_seed, inputs=[seed_random, image_seed], outputs=image_seed) \
.then(fn=get_task, inputs=ctrls, outputs=currentTask) \
.then(fn=generate_clicked, inputs=currentTask, outputs=[progress_html, progress_window, progress_gallery, gallery]) \
.then(lambda: (gr.update(visible=True, interactive=True), gr.update(visible=False, interactive=False), gr.update(visible=False, interactive=False), False),
outputs=[generate_button, stop_button, skip_button, state_is_generating]) \
.then(fn=lambda: None, _js='playNotification').then(fn=lambda: None, _js='refresh_grid_delayed')
reset_button.click(lambda: [worker.AsyncTask(args=[]), False, gr.update(visible=True, interactive=True)] +
[gr.update(visible=False)] * 6 +
[gr.update(visible=True, value=[])],
outputs=[currentTask, state_is_generating, generate_button,
reset_button, stop_button, skip_button,
progress_html, progress_window, progress_gallery, gallery],
queue=False)
for notification_file in ['notification.ogg', 'notification.mp3']:
if os.path.exists(notification_file):
gr.Audio(interactive=False, value=notification_file, elem_id='audio_notification', visible=False)
break
def trigger_describe(modes, img, apply_styles):
describe_prompts = []
styles = set()
if flags.describe_type_photo in modes:
from extras.interrogate import default_interrogator as default_interrogator_photo
describe_prompts.append(default_interrogator_photo(img))
styles.update(["Fooocus V2", "Fooocus Enhance", "Fooocus Sharp"])
if flags.describe_type_anime in modes:
from extras.wd14tagger import default_interrogator as default_interrogator_anime
describe_prompts.append(default_interrogator_anime(img))
styles.update(["Fooocus V2", "Fooocus Masterpiece"])
if len(styles) == 0 or not apply_styles:
styles = gr.update()
else:
styles = list(styles)
if len(describe_prompts) == 0:
describe_prompt = gr.update()
else:
describe_prompt = ', '.join(describe_prompts)
return describe_prompt, styles
describe_btn.click(trigger_describe, inputs=[describe_methods, describe_input_image, describe_apply_styles],
outputs=[prompt, style_selections], show_progress=True, queue=True) \
.then(fn=style_sorter.sort_styles, inputs=style_selections, outputs=style_selections, queue=False, show_progress=False) \
.then(lambda: None, _js='()=>{refresh_style_localization();}')
if args_manager.args.enable_auto_describe_image:
def trigger_auto_describe(mode, img, prompt, apply_styles):
# keep prompt if not empty
if prompt == '':
return trigger_describe(mode, img, apply_styles)
return gr.update(), gr.update()
uov_input_image.upload(trigger_auto_describe, inputs=[describe_methods, uov_input_image, prompt, describe_apply_styles],
outputs=[prompt, style_selections], show_progress=True, queue=True) \
.then(fn=style_sorter.sort_styles, inputs=style_selections, outputs=style_selections, queue=False, show_progress=False) \
.then(lambda: None, _js='()=>{refresh_style_localization();}')
enhance_input_image.upload(lambda: gr.update(value=True), outputs=enhance_checkbox, queue=False, show_progress=False) \
.then(trigger_auto_describe, inputs=[describe_methods, enhance_input_image, prompt, describe_apply_styles],
outputs=[prompt, style_selections], show_progress=True, queue=True) \
.then(fn=style_sorter.sort_styles, inputs=style_selections, outputs=style_selections, queue=False, show_progress=False) \
.then(lambda: None, _js='()=>{refresh_style_localization();}')
def dump_default_english_config():
from modules.localization import dump_english_config
dump_english_config(grh.all_components)
# dump_default_english_config()
shared.gradio_root.launch(
inbrowser=args_manager.args.in_browser,
server_name=args_manager.args.listen,
server_port=args_manager.args.port,
share=args_manager.args.share,
auth=check_auth if (args_manager.args.share or args_manager.args.listen) and auth_enabled else None,
allowed_paths=[modules.config.path_outputs],
blocked_paths=[constants.AUTH_FILENAME]
)