ImageGen-Studio / ui /shared /studio_ui.py
BlueSkyXN's picture
Deploy GitHub a51e6f6df2b2d5093fd2526a7953c2ee6a422e37
8a28a8d verified
Raw
History Blame Contribute Delete
17 kB
"""One shared, task-switchable UI instead of five duplicated forms."""
from __future__ import annotations
import gradio as gr
from comfy_integration.nodes import SAMPLER_CHOICES, SCHEDULER_CHOICES
from core.execution_plan import RUN_MODE_CHOICES
from core.runtime_config import CONFIG
from core.settings import (
ARCHITECTURES_CONFIG,
MODEL_DEFAULTS_CONFIG,
MODEL_MAP_CHECKPOINT,
MODEL_TYPE_MAP,
RESOLUTION_MAP,
)
from ui.guidance import QUICK_PRESETS, model_hint, task_help
from .ui_components import (
create_anima_controlnet_lllite_ui,
create_boogu_edit_ui,
create_category_filter_ui,
create_conditioning_ui,
create_controlnet_ui,
create_diffsynth_controlnet_ui,
create_embedding_ui,
create_flux1_ipadapter_ui,
create_hidream_o1_reference_ui,
create_ipadapter_ui,
create_joyai_reference_ui,
create_krea2_controlnet_ui,
create_krea2_identity_edit_ui,
create_krea2_reference_edit_ui,
create_lora_settings_ui,
create_model_architecture_filter_ui,
create_pid_ui,
create_qwen_image_edit_ui,
create_reference_image_ui,
create_reference_latent_ui,
create_sd3_ipadapter_ui,
create_style_ui,
create_vae_override_ui,
)
TASK_CHOICES = [
("✨ 文生图", "txt2img"),
("🖼️ 图生图", "img2img"),
("🖌️ 局部重绘", "inpaint"),
("↔️ 扩图", "outpaint"),
("🔎 高清修复", "hires_fix"),
]
PROMPT_EXAMPLES = [
["一位银发少女站在雨夜车站,半身构图,电影感侧光,细腻插画,高质量"],
["现代东方客厅,浅木色与米白配色,午后自然光,广角室内摄影"],
["未来城市夜景,湿润街道倒影,霓虹灯,低机位,强烈纵深,电影概念设计"],
]
def create_ui() -> dict:
prefix = "studio"
components: dict = {"_task_prefixes": [(prefix, None)]}
default_values = MODEL_DEFAULTS_CONFIG.get("Default", {})
default_model = next(iter(MODEL_MAP_CHECKPOINT), None)
default_architecture = MODEL_TYPE_MAP.get(default_model, "SDXL")
default_architecture_info = ARCHITECTURES_CONFIG.get("architectures", {}).get(
default_architecture, {}
)
default_resolution_key = default_architecture_info.get("model_type", "sdxl")
default_resolution_map = RESOLUTION_MAP.get(
default_resolution_key, RESOLUTION_MAP.get("sdxl", {})
)
default_aspect = next(iter(default_resolution_map), "1:1 (Square)")
default_width, default_height = default_resolution_map.get(default_aspect, (1024, 1024))
components[f"task_type_{prefix}"] = gr.Radio(
choices=TASK_CHOICES,
value="txt2img",
label="1. 选择任务",
elem_id="task-switcher",
)
components[f"task_help_{prefix}"] = gr.Markdown(
task_help("txt2img"), elem_classes="task-help"
)
with gr.Row(equal_height=False, elem_id="workspace-row"):
with gr.Column(scale=7, min_width=360):
with gr.Accordion("2. 选择模型", open=True):
components[f"quick_preset_{prefix}"] = gr.Dropdown(
choices=QUICK_PRESETS,
value="__manual__",
label="快速方案",
info="只是帮你定位模型;仍可继续调整所有参数。",
interactive=True,
)
components.update(create_model_architecture_filter_ui(prefix))
with gr.Row():
components.update(create_category_filter_ui(prefix))
components[f"base_model_{prefix}"] = gr.Dropdown(
label="具体模型",
choices=list(MODEL_MAP_CHECKPOINT.keys()),
value=default_model,
filterable=True,
allow_custom_value=False,
scale=3,
interactive=True,
)
components[f"model_hint_{prefix}"] = gr.Markdown(
model_hint(default_model) if default_model else "暂无可用模型。",
elem_classes="model-hint",
)
with gr.Accordion("批量、多图与模型 PK(可选)", open=False):
components[f"run_mode_{prefix}"] = gr.Dropdown(
choices=RUN_MODE_CHOICES,
value="single",
label="运行方式",
info="所有模式都顺序执行,不会同时把多个大模型塞进显存。",
)
components[f"pk_models_{prefix}"] = gr.Dropdown(
choices=list(MODEL_MAP_CHECKPOINT.keys()),
value=[],
multiselect=True,
max_choices=max(1, CONFIG.max_pk_models - 1),
label="额外对比模型",
info=f"当前模型会自动加入;最多合计 {CONFIG.max_pk_models} 个。",
filterable=True,
visible=False,
)
components[f"batch_images_{prefix}"] = gr.File(
file_count="multiple",
file_types=["image"],
type="filepath",
allow_reordering=True,
label="批量输入 / 多图参考",
visible=False,
)
with gr.Row():
components[f"reference_role_{prefix}"] = gr.Dropdown(
choices=[
("自动匹配模型能力", "auto"),
("人物 / 身份参考", "identity"),
("风格参考", "style"),
],
value="auto",
label="多图融合用途",
visible=False,
)
components[f"pk_model_defaults_{prefix}"] = gr.Checkbox(
value=True,
label="PK 使用各模型推荐采样参数",
info="Prompt、尺寸与种子保持一致;步数和 CFG 按模型推荐值。",
visible=False,
)
gr.Markdown(
"**怎么选:** 多图独立是 A→A′、B→B′;多图×多模型会执行图片和模型的组合;"
"多图融合是把整组图片同时交给 Qwen/Mage-Flow/JoyAI/Boogu/Krea/Flux 等兼容模型。"
)
with gr.Group(elem_classes="prompt-card"):
components[f"prompt_{prefix}"] = gr.Textbox(
label="3. 描述你想要的画面",
placeholder="主体 + 动作 + 场景 + 构图 + 风格 + 光线",
lines=4,
max_lines=8,
value=default_values.get("positive_prompt", ""),
)
with gr.Accordion("负面提示词(可选)", open=False):
components[f"neg_prompt_{prefix}"] = gr.Textbox(
label="不希望出现的内容",
placeholder="例如:低质量、模糊、多余手指、水印",
lines=2,
value=default_values.get("negative_prompt", ""),
)
gr.Examples(
examples=PROMPT_EXAMPLES,
inputs=[components[f"prompt_{prefix}"]],
label="点一下填入示例",
)
with gr.Group(visible=False) as source_panel:
components[f"source_image_{prefix}"] = gr.Image(
type="pil",
label="源图片",
sources=["upload", "clipboard"],
height=300,
)
components[f"source_panel_{prefix}"] = source_panel
with gr.Group(visible=False) as inpaint_panel:
gr.Markdown("涂抹需要替换的区域;未涂抹部分会尽量保留。")
components[f"inpaint_image_dict_{prefix}"] = gr.ImageEditor(
type="pil",
label="图片与蒙版",
sources=["upload", "clipboard"],
height=420,
)
with gr.Row():
components[f"inpaint_denoise_{prefix}"] = gr.Slider(
0.0, 1.0, value=1.0, step=0.05, label="局部重绘幅度"
)
components[f"grow_mask_by_{prefix}"] = gr.Slider(
0, 64, value=6, step=1, label="蒙版外扩像素"
)
components[f"inpaint_panel_{prefix}"] = inpaint_panel
with gr.Group(visible=False) as img2img_panel:
components[f"img2img_denoise_{prefix}"] = gr.Slider(
0.0,
1.0,
value=0.7,
step=0.01,
label="重绘幅度",
info="0 更接近原图,1 改动最大。",
)
components[f"img2img_panel_{prefix}"] = img2img_panel
with gr.Group(visible=False) as outpaint_panel:
with gr.Row():
components[f"left_{prefix}"] = gr.Slider(0, 512, 64, step=64, label="向左扩展")
components[f"right_{prefix}"] = gr.Slider(0, 512, 64, step=64, label="向右扩展")
with gr.Row():
components[f"top_{prefix}"] = gr.Slider(0, 512, 64, step=64, label="向上扩展")
components[f"bottom_{prefix}"] = gr.Slider(0, 512, 64, step=64, label="向下扩展")
components[f"feathering_{prefix}"] = gr.Slider(
0, 100, 10, step=1, label="接缝羽化 / 蒙版外扩"
)
components[f"outpaint_panel_{prefix}"] = outpaint_panel
with gr.Group(visible=False) as hires_panel:
with gr.Row():
components[f"hires_upscaler_{prefix}"] = gr.Dropdown(
choices=["nearest-exact", "bilinear", "area", "bicubic", "bislerp"],
value="nearest-exact",
label="放大算法",
)
components[f"hires_scale_by_{prefix}"] = gr.Slider(
1.0, 4.0, 1.5, step=0.1, label="放大倍数"
)
components[f"hires_denoise_{prefix}"] = gr.Slider(
0.0, 1.0, 0.55, step=0.01, label="细节重绘幅度"
)
components[f"hires_panel_{prefix}"] = hires_panel
with gr.Group() as size_panel:
with gr.Row():
components[f"aspect_ratio_{prefix}"] = gr.Dropdown(
label="画幅",
choices=list(default_resolution_map) or [default_aspect],
value=default_aspect,
interactive=True,
scale=2,
)
components[f"width_{prefix}"] = gr.Number(
label="宽度", value=default_width, precision=0, interactive=True
)
components[f"height_{prefix}"] = gr.Number(
label="高度", value=default_height, precision=0, interactive=True
)
components[f"size_panel_{prefix}"] = size_panel
with gr.Accordion("高级采样与运行参数", open=False):
with gr.Row():
components[f"sampler_{prefix}"] = gr.Dropdown(
choices=SAMPLER_CHOICES,
value=default_values.get("sampler_name", "euler"),
label="采样器",
)
components[f"scheduler_{prefix}"] = gr.Dropdown(
choices=SCHEDULER_CHOICES,
value=default_values.get("scheduler", "simple"),
label="调度器",
)
with gr.Row():
components[f"steps_{prefix}"] = gr.Slider(
1, 100, default_values.get("steps", 8), step=1, label="采样步数"
)
components[f"cfg_{prefix}"] = gr.Slider(
1.0, 20.0, default_values.get("cfg", 1.0), step=0.1, label="CFG"
)
with gr.Row():
components[f"seed_{prefix}"] = gr.Number(
label="随机种子(-1 为随机)", value=-1, precision=0
)
components[f"batch_size_{prefix}"] = gr.Slider(
1,
CONFIG.max_batch_size,
1,
step=1,
label="单次生成数量",
info="数量越大,显存与 ZeroGPU 配额消耗越高。",
)
with gr.Row():
components[f"clip_skip_{prefix}"] = gr.Slider(
1, 2, 1, step=1, label="Clip Skip", visible=False
)
components[f"guidance_{prefix}"] = gr.Slider(
1.0, 10.0, 3.5, step=0.1, label="FLUX Guidance", visible=False
)
components[f"zero_gpu_{prefix}"] = gr.Dropdown(
choices=[("自动估算", 0), ("60 秒", 60), ("90 秒", 90), ("120 秒", 120)],
value=0,
label="ZeroGPU 运行时长",
info="通常保持自动;复杂高分辨率任务可手动提高。",
)
with gr.Row():
components[f"auto_model_params_{prefix}"] = gr.Checkbox(
value=True,
label="切模型时自动应用推荐采样参数",
info="关闭后会保留你手调的步数、CFG、采样器和调度器。",
scale=3,
)
components[f"reset_model_params_{prefix}"] = gr.Button(
"恢复当前模型推荐值", size="sm", scale=1
)
with gr.Column(scale=5, min_width=340, elem_id="result-column"):
with gr.Row():
components[f"run_{prefix}"] = gr.Button(
"开始生成", variant="primary", size="lg", scale=3
)
components[f"cancel_{prefix}"] = gr.Button(
"取消排队", variant="stop", size="lg", scale=1
)
components[f"result_{prefix}"] = gr.Gallery(
label="生成结果",
show_label=True,
columns=2,
object_fit="contain",
height=720,
)
components[f"run_summary_{prefix}"] = gr.Markdown(
"普通模式会在这里显示执行结果;PK/批量模式会逐项列出成功与失败。"
)
components[f"clear_result_{prefix}"] = gr.Button("清空结果", size="sm")
gr.Markdown(
"生成结果包含 Prompt、参数和完整 ComfyUI 工作流元数据,下载 PNG 后可继续复现。",
elem_classes="result-note",
)
gr.Markdown("## 当前模型可用的扩展能力", elem_classes="advanced-title")
gr.Markdown("下方只显示当前模型兼容的能力;切换模型后会自动更新。")
components.update(create_lora_settings_ui(prefix))
components.update(create_controlnet_ui(prefix))
components.update(create_anima_controlnet_lllite_ui(prefix))
components.update(create_diffsynth_controlnet_ui(prefix))
components.update(create_krea2_controlnet_ui(prefix))
components.update(create_ipadapter_ui(prefix))
components.update(create_flux1_ipadapter_ui(prefix))
components.update(create_sd3_ipadapter_ui(prefix))
components.update(create_embedding_ui(prefix))
components.update(create_style_ui(prefix))
components.update(create_conditioning_ui(prefix))
components.update(create_reference_latent_ui(prefix))
components.update(create_hidream_o1_reference_ui(prefix))
components.update(create_joyai_reference_ui(prefix))
components.update(create_krea2_identity_edit_ui(prefix))
components.update(create_krea2_reference_edit_ui(prefix))
components.update(create_qwen_image_edit_ui(prefix))
components.update(create_boogu_edit_ui(prefix))
components.update(create_reference_image_ui(prefix))
components.update(create_vae_override_ui(prefix))
components.update(create_pid_ui(prefix))
return components