ui.py 58.2 KB
Newer Older
1 2 3 4
import base64
import html
import io
import json
A
AUTOMATIC 已提交
5
import math
6 7
import mimetypes
import os
A
AUTOMATIC 已提交
8
import random
9 10 11
import sys
import time
import traceback
M
Michoko 已提交
12 13
import platform
import subprocess as sp
14

A
AUTOMATIC 已提交
15 16
import numpy as np
import torch
17
from PIL import Image, PngImagePlugin
18
import piexif
19 20 21

import gradio as gr
import gradio.utils
A
AUTOMATIC 已提交
22
import gradio.routes
23

24
from modules import sd_hijack
25 26 27 28
from modules.paths import script_path
from modules.shared import opts, cmd_opts
import modules.shared as shared
from modules.sd_samplers import samplers, samplers_for_img2img
29
from modules.sd_hijack import model_hijack
30
import modules.ldsr_model
A
AUTOMATIC 已提交
31
import modules.scripts
32 33
import modules.gfpgan_model
import modules.codeformer_model
A
AUTOMATIC 已提交
34
import modules.styles
35
import modules.generation_parameters_copypaste
36
from modules.images import apply_filename_pattern, get_next_sequence_number
37
import modules.textual_inversion.ui
38 39 40 41 42 43

# this is a fix for Windows users. Without it, javascript files will be served with text/html content-type and the bowser will not show any UI
mimetypes.init()
mimetypes.add_type('application/javascript', '.js')


44
if not cmd_opts.share and not cmd_opts.listen:
45 46 47 48 49 50 51 52 53 54 55 56 57 58
    # fix gradio phoning home
    gradio.utils.version_check = lambda: None
    gradio.utils.get_local_ip_address = lambda: '127.0.0.1'


def gr_show(visible=True):
    return {"visible": visible, "__type__": "update"}


sample_img2img = "assets/stable-samples/img2img/sketch-mountains-input.jpg"
sample_img2img = sample_img2img if os.path.exists(sample_img2img) else None

css_hide_progressbar = """
.wrap .m-12 svg { display:none!important; }
59
.wrap .m-12::before { content:"Loading..." }
60 61 62 63
.progress-bar { display:none!important; }
.meta-text { display:none!important; }
"""

64 65 66 67
# Using constants for these since the variation selector isn't visible.
# Important that they exactly match script.js for tooltip to work.
random_symbol = '\U0001f3b2\ufe0f'  # 🎲️
reuse_symbol = '\u267b\ufe0f'  # ♻️
68 69
art_symbol = '\U0001f3a8'  # 🎨
paste_symbol = '\u2199\ufe0f'  # ↙
M
Michoko 已提交
70
folder_symbol = '\uD83D\uDCC2'
71

72
def plaintext_to_html(text):
73
    text = "<p>" + "<br>\n".join([f"{html.escape(x)}" for x in text.split('\n')]) + "</p>"
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
    return text


def image_from_url_text(filedata):
    if type(filedata) == list:
        if len(filedata) == 0:
            return None

        filedata = filedata[0]

    if filedata.startswith("data:image/png;base64,"):
        filedata = filedata[len("data:image/png;base64,"):]

    filedata = base64.decodebytes(filedata.encode('utf-8'))
    image = Image.open(io.BytesIO(filedata))
    return image


def send_gradio_gallery_to_image(x):
    if len(x) == 0:
        return None

    return image_from_url_text(x[0])

J
jtkelm2 已提交
98

J
jtkelm2 已提交
99
def save_files(js_data, images, index):
100
    import csv    
101 102
    filenames = []

103 104 105 106 107 108 109
    #quick dictionary to class object conversion. Its neccesary due apply_filename_pattern requiring it
    class MyObject:
        def __init__(self, d=None):
            if d is not None:
                for key, value in d.items():
                    setattr(self, key, value)

110
    data = json.loads(js_data)
111

112 113 114 115 116 117 118 119 120
    p = MyObject(data)
    path = opts.outdir_save
    save_to_dirs = opts.save_to_dirs

    if save_to_dirs:
        dirname = apply_filename_pattern(opts.directories_filename_pattern or "[prompt_words]", p, p.seed, p.prompt)
        path = os.path.join(opts.outdir_save, dirname)

    os.makedirs(path, exist_ok=True)
121 122
  

123
    if index > -1 and opts.save_selected_only and (index >= data["index_of_first_image"]):  # ensures we are looking at a specific non-grid picture, and we have save_selected_only
124

J
jtkelm2 已提交
125
        images = [images[index]]
126 127 128
        infotexts = [data["infotexts"][index]]
    else:
        infotexts = data["infotexts"]
129

130
    with open(os.path.join(opts.outdir_save, "log.csv"), "a", encoding="utf8", newline='') as file:
131 132 133
        at_start = file.tell() == 0
        writer = csv.writer(file)
        if at_start:
134
            writer.writerow(["prompt", "seed", "width", "height", "sampler", "cfgs", "steps", "filename", "negative_prompt"])
135

136 137 138 139 140 141
        file_decoration = opts.samples_filename_pattern or "[seed]-[prompt_spaces]"
        if file_decoration != "":
            file_decoration = "-" + file_decoration.lower()
        file_decoration = apply_filename_pattern(file_decoration, p, p.seed, p.prompt)
        truncated = (file_decoration[:240] + '..') if len(file_decoration) > 240 else file_decoration
        filename_base = truncated
142
        extension = opts.samples_format.lower()
143 144

        basecount = get_next_sequence_number(path, "")
145
        for i, filedata in enumerate(images):
146
            file_number = f"{basecount+i:05}"
A
AUTOMATIC1111 已提交
147
            filename = file_number + filename_base + f".{extension}"
148
            filepath = os.path.join(path, filename)
149 150 151 152 153


            if filedata.startswith("data:image/png;base64,"):
                filedata = filedata[len("data:image/png;base64,"):]

154
            image = Image.open(io.BytesIO(base64.decodebytes(filedata.encode('utf-8'))))
155 156 157 158 159 160 161 162 163 164 165
            if opts.enable_pnginfo and extension == 'png':
                pnginfo = PngImagePlugin.PngInfo()
                pnginfo.add_text('parameters', infotexts[i])
                image.save(filepath, pnginfo=pnginfo)
            else:
                image.save(filepath, quality=opts.jpeg_quality)

            if opts.enable_pnginfo and extension in ("jpg", "jpeg", "webp"):
                piexif.insert(piexif.dump({"Exif": {
                    piexif.ExifIFD.UserComment: piexif.helper.UserComment.dump(infotexts[i], encoding="unicode")
                }}), filepath)
166 167 168

            filenames.append(filename)

169
        writer.writerow([data["prompt"], data["seed"], data["width"], data["height"], data["sampler"], data["cfg_scale"], data["steps"], filenames[0], data["negative_prompt"]])
170 171 172 173

    return '', '', plaintext_to_html(f"Saved: {filenames[0]}")


174 175
def wrap_gradio_call(func, extra_outputs=None):
    def f(*args, extra_outputs_array=extra_outputs, **kwargs):
E
EyeDeck 已提交
176 177
        run_memmon = opts.memmon_poll_rate > 0 and not shared.mem_mon.disabled
        if run_memmon:
178
            shared.mem_mon.monitor()
179 180 181 182 183 184 185 186 187
        t = time.perf_counter()

        try:
            res = list(func(*args, **kwargs))
        except Exception as e:
            print("Error completing request", file=sys.stderr)
            print("Arguments:", args, kwargs, file=sys.stderr)
            print(traceback.format_exc(), file=sys.stderr)

A
AUTOMATIC 已提交
188 189 190
            shared.state.job = ""
            shared.state.job_count = 0

191 192 193 194
            if extra_outputs_array is None:
                extra_outputs_array = [None, '']

            res = extra_outputs_array + [f"<div class='error'>{plaintext_to_html(type(e).__name__+': '+str(e))}</div>"]
195 196 197

        elapsed = time.perf_counter() - t

E
EyeDeck 已提交
198
        if run_memmon:
199 200 201 202 203 204 205
            mem_stats = {k: -(v//-(1024*1024)) for k, v in shared.mem_mon.stop().items()}
            active_peak = mem_stats['active_peak']
            reserved_peak = mem_stats['reserved_peak']
            sys_peak = mem_stats['system_peak']
            sys_total = mem_stats['total']
            sys_pct = round(sys_peak/max(sys_total, 1) * 100, 2)

E
EyeDeck 已提交
206
            vram_html = f"<p class='vram'>Torch active/reserved: {active_peak}/{reserved_peak} MiB, <wbr>Sys VRAM: {sys_peak}/{sys_total} MiB ({sys_pct}%)</p>"
207 208
        else:
            vram_html = ''
209

210
        # last item is always HTML
211
        res[-1] += f"<div class='performance'><p class='time'>Time taken: <wbr>{elapsed:.2f}s</p>{vram_html}</div>"
212 213

        shared.state.interrupted = False
214
        shared.state.job_count = 0
215 216 217 218 219 220

        return tuple(res)

    return f


221
def check_progress_call(id_part):
A
AUTOMATIC 已提交
222
    if shared.state.job_count == 0:
223
        return "", gr_show(False), gr_show(False), gr_show(False)
A
AUTOMATIC 已提交
224

A
AUTOMATIC 已提交
225 226 227 228
    progress = 0

    if shared.state.job_count > 0:
        progress += shared.state.job_no / shared.state.job_count
A
AUTOMATIC 已提交
229 230 231 232 233
    if shared.state.sampling_steps > 0:
        progress += 1 / shared.state.job_count * shared.state.sampling_step / shared.state.sampling_steps

    progress = min(progress, 1)

A
AUTOMATIC 已提交
234 235 236 237 238 239 240 241
    progressbar = ""
    if opts.show_progressbar:
        progressbar = f"""<div class='progressDiv'><div class='progress' style="width:{progress * 100}%">{str(int(progress*100))+"%" if progress > 0.01 else ""}</div></div>"""

    image = gr_show(False)
    preview_visibility = gr_show(False)

    if opts.show_progress_every_n_steps > 0:
242
        if shared.parallel_processing_allowed:
A
AUTOMATIC 已提交
243

244 245 246
            if shared.state.sampling_step - shared.state.current_image_sampling_step >= opts.show_progress_every_n_steps and shared.state.current_latent is not None:
                shared.state.current_image = modules.sd_samplers.sample_to_image(shared.state.current_latent)
                shared.state.current_image_sampling_step = shared.state.sampling_step
A
AUTOMATIC 已提交
247

A
AUTOMATIC 已提交
248 249
        image = shared.state.current_image

A
AUTOMATIC 已提交
250
        if image is None:
A
AUTOMATIC 已提交
251 252 253
            image = gr.update(value=None)
        else:
            preview_visibility = gr_show(True)
A
AUTOMATIC 已提交
254

255 256 257 258 259 260
    if shared.state.textinfo is not None:
        textinfo_result = gr.HTML.update(value=shared.state.textinfo, visible=True)
    else:
        textinfo_result = gr_show(False)

    return f"<span id='{id_part}_progress_span' style='display: none'>{time.time()}</span><p>{progressbar}</p>", preview_visibility, image, textinfo_result
A
AUTOMATIC 已提交
261 262


263
def check_progress_call_initial(id_part):
264
    shared.state.job_count = -1
265 266
    shared.state.current_latent = None
    shared.state.current_image = None
267
    shared.state.textinfo = None
268

269
    return check_progress_call(id_part)
270 271


A
AUTOMATIC 已提交
272 273 274 275 276 277 278
def roll_artist(prompt):
    allowed_cats = set([x for x in shared.artist_db.categories() if len(opts.random_artist_categories)==0 or x in opts.random_artist_categories])
    artist = random.choice([x for x in shared.artist_db.artists if x.category in allowed_cats])

    return prompt + ", " + artist.name if prompt != '' else artist.name


A
AUTOMATIC 已提交
279 280 281 282 283 284 285
def visit(x, func, path=""):
    if hasattr(x, 'children'):
        for c in x.children:
            visit(c, func, path)
    elif x.label is not None:
        func(path + "/" + str(x.label), x)

286

287 288
def add_style(name: str, prompt: str, negative_prompt: str):
    if name is None:
A
AUTOMATIC 已提交
289 290
        return [gr_show(), gr_show()]

291
    style = modules.styles.PromptStyle(name, prompt, negative_prompt)
A
AUTOMATIC 已提交
292
    shared.prompt_styles.styles[style.name] = style
293 294
    # Save all loaded prompt styles: this allows us to update the storage format in the future more easily, because we
    # reserialize all styles every time we save them
A
AUTOMATIC 已提交
295
    shared.prompt_styles.save_styles(shared.styles_filename)
A
AUTOMATIC 已提交
296

297
    return [gr.Dropdown.update(visible=True, choices=list(shared.prompt_styles.styles)) for _ in range(4)]
A
AUTOMATIC 已提交
298 299 300 301 302 303 304


def apply_styles(prompt, prompt_neg, style1_name, style2_name):
    prompt = shared.prompt_styles.apply_styles_to_prompt(prompt, [style1_name, style2_name])
    prompt_neg = shared.prompt_styles.apply_negative_styles_to_prompt(prompt_neg, [style1_name, style2_name])

    return [gr.Textbox.update(value=prompt), gr.Textbox.update(value=prompt_neg), gr.Dropdown.update(value="None"), gr.Dropdown.update(value="None")]
A
AUTOMATIC 已提交
305 306


A
AUTOMATIC 已提交
307 308 309 310 311
def interrogate(image):
    prompt = shared.interrogator.interrogate(image)

    return gr_show(True) if prompt is None else prompt

A
AUTOMATIC 已提交
312

313 314 315 316
def create_seed_inputs():
    with gr.Row():
        with gr.Box():
            with gr.Row(elem_id='seed_row'):
317
                seed = (gr.Textbox if cmd_opts.use_textbox_seed else gr.Number)(label='Seed', value=-1)
318
                seed.style(container=False)
319 320
                random_seed = gr.Button(random_symbol, elem_id='random_seed')
                reuse_seed = gr.Button(reuse_symbol, elem_id='reuse_seed')
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350

        with gr.Box(elem_id='subseed_show_box'):
            seed_checkbox = gr.Checkbox(label='Extra', elem_id='subseed_show', value=False)

    # Components to show/hide based on the 'Extra' checkbox
    seed_extras = []

    with gr.Row(visible=False) as seed_extra_row_1:
        seed_extras.append(seed_extra_row_1)
        with gr.Box():
            with gr.Row(elem_id='subseed_row'):
                subseed = gr.Number(label='Variation seed', value=-1)
                subseed.style(container=False)
                random_subseed = gr.Button(random_symbol, elem_id='random_subseed')
                reuse_subseed = gr.Button(reuse_symbol, elem_id='reuse_subseed')
        subseed_strength = gr.Slider(label='Variation strength', value=0.0, minimum=0, maximum=1, step=0.01)

    with gr.Row(visible=False) as seed_extra_row_2:
        seed_extras.append(seed_extra_row_2)
        seed_resize_from_w = gr.Slider(minimum=0, maximum=2048, step=64, label="Resize seed from width", value=0)
        seed_resize_from_h = gr.Slider(minimum=0, maximum=2048, step=64, label="Resize seed from height", value=0)

    random_seed.click(fn=lambda: -1, show_progress=False, inputs=[], outputs=[seed])
    random_subseed.click(fn=lambda: -1, show_progress=False, inputs=[], outputs=[subseed])

    def change_visibility(show):
        return {comp: gr_show(show) for comp in seed_extras}

    seed_checkbox.change(change_visibility, show_progress=False, inputs=[seed_checkbox], outputs=seed_extras)

351
    return seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox
352 353


354 355 356
def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info: gr.Textbox, dummy_component, is_subseed):
    """ Connects a 'reuse (sub)seed' button's click event so that it copies last used
        (sub)seed value from generation info the to the seed field. If copying subseed and subseed strength
357
        was 0, i.e. no variation seed was used, it copies the normal seed value instead."""
358 359 360
    def copy_seed(gen_info_string: str, index):
        res = -1

361 362
        try:
            gen_info = json.loads(gen_info_string)
363 364 365 366 367
            index -= gen_info.get('index_of_first_image', 0)

            if is_subseed and gen_info.get('subseed_strength', 0) > 0:
                all_subseeds = gen_info.get('all_subseeds', [-1])
                res = all_subseeds[index if 0 <= index < len(all_subseeds) else 0]
368
            else:
369 370 371
                all_seeds = gen_info.get('all_seeds', [-1])
                res = all_seeds[index if 0 <= index < len(all_seeds) else 0]

372 373 374 375
        except json.decoder.JSONDecodeError as e:
            if gen_info_string != '':
                print("Error parsing JSON generation info:", file=sys.stderr)
                print(gen_info_string, file=sys.stderr)
376 377

        return [res, gr_show(False)]
378 379 380

    reuse_seed.click(
        fn=copy_seed,
381
        _js="(x, y) => [x, selected_gallery_index()]",
382
        show_progress=False,
383 384
        inputs=[generation_info, dummy_component],
        outputs=[seed, dummy_component]
385 386
    )

387 388 389 390
def update_token_counter(text):
    tokens, token_count, max_length = model_hijack.tokenize(text)
    style_class = ' class="red"' if (token_count > max_length) else ""
    return f"<span {style_class}>{token_count}/{max_length}</span>"
A
AUTOMATIC 已提交
391

A
AUTOMATIC 已提交
392
def create_toprow(is_img2img):
393 394
    id_part = "img2img" if is_img2img else "txt2img"

A
AUTOMATIC 已提交
395 396 397
    with gr.Row(elem_id="toprow"):
        with gr.Column(scale=4):
            with gr.Row():
398
                with gr.Column(scale=80):
A
AUTOMATIC 已提交
399
                    with gr.Row():
400
                        prompt = gr.Textbox(label="Prompt", elem_id=f"{id_part}_prompt", show_label=False, placeholder="Prompt", lines=2)
A
AUTOMATIC 已提交
401

402 403 404
                with gr.Column(scale=1, elem_id="roll_col"):
                    roll = gr.Button(value=art_symbol, elem_id="roll", visible=len(shared.artist_db.artists) > 0)
                    paste = gr.Button(value=paste_symbol, elem_id="paste")
405
                    token_counter = gr.HTML(value="<span></span>", elem_id=f"{id_part}_token_counter")
406 407
                    hidden_button = gr.Button(visible=False, elem_id=f"{id_part}_token_button")
                    hidden_button.click(fn=update_token_counter, inputs=[prompt], outputs=[token_counter])
408 409 410

                with gr.Column(scale=10, elem_id="style_pos_col"):
                    prompt_style = gr.Dropdown(label="Style 1", elem_id=f"{id_part}_style_index", choices=[k for k, v in shared.prompt_styles.styles.items()], value=next(iter(shared.prompt_styles.styles.keys())), visible=len(shared.prompt_styles.styles) > 1)
A
AUTOMATIC 已提交
411 412 413 414 415 416

            with gr.Row():
                with gr.Column(scale=8):
                    negative_prompt = gr.Textbox(label="Negative prompt", elem_id="negative_prompt", show_label=False, placeholder="Negative prompt", lines=2)

                with gr.Column(scale=1, elem_id="style_neg_col"):
417
                    prompt_style2 = gr.Dropdown(label="Style 2", elem_id=f"{id_part}_style2_index", choices=[k for k, v in shared.prompt_styles.styles.items()], value=next(iter(shared.prompt_styles.styles.keys())), visible=len(shared.prompt_styles.styles) > 1)
A
AUTOMATIC 已提交
418 419 420

        with gr.Column(scale=1):
            with gr.Row():
421
                interrupt = gr.Button('Interrupt', elem_id=f"{id_part}_interrupt")
422
                submit = gr.Button('Generate', elem_id=f"{id_part}_generate", variant='primary')
423 424 425 426 427 428

                interrupt.click(
                    fn=lambda: shared.state.interrupt(),
                    inputs=[],
                    outputs=[],
                )
A
AUTOMATIC 已提交
429 430 431 432 433 434 435 436 437

            with gr.Row():
                if is_img2img:
                    interrogate = gr.Button('Interrogate', elem_id="interrogate")
                else:
                    interrogate = None
                prompt_style_apply = gr.Button('Apply style', elem_id="style_apply")
                save_style = gr.Button('Create style', elem_id="style_create")

438
    return prompt, roll, prompt_style, negative_prompt, prompt_style2, submit, interrogate, prompt_style_apply, save_style, paste
A
AUTOMATIC 已提交
439

440

441 442 443 444
def setup_progressbar(progressbar, preview, id_part, textinfo=None):
    if textinfo is None:
        textinfo = gr.HTML(visible=False)

445
    check_progress = gr.Button('Check progress', elem_id=f"{id_part}_check_progress", visible=False)
446
    check_progress.click(
447
        fn=lambda: check_progress_call(id_part),
448 449
        show_progress=False,
        inputs=[],
450
        outputs=[progressbar, preview, preview, textinfo],
451 452
    )

453
    check_progress_initial = gr.Button('Check progress (first)', elem_id=f"{id_part}_check_progress_initial", visible=False)
454
    check_progress_initial.click(
455
        fn=lambda: check_progress_call_initial(id_part),
456 457
        show_progress=False,
        inputs=[],
458
        outputs=[progressbar, preview, preview, textinfo],
459
    )
A
AUTOMATIC 已提交
460 461


462 463 464 465
def create_ui(wrap_gradio_gpu_call):
    import modules.img2img
    import modules.txt2img

466
    with gr.Blocks(analytics_enabled=False) as txt2img_interface:
467
        txt2img_prompt, roll, txt2img_prompt_style, txt2img_negative_prompt, txt2img_prompt_style2, submit, _, txt2img_prompt_style_apply, txt2img_save_style, paste = create_toprow(is_img2img=False)
468
        dummy_component = gr.Label(visible=False)
469

470 471 472
        with gr.Row(elem_id='txt2img_progress_row'):
            with gr.Column(scale=1):
                pass
473

474 475
            with gr.Column(scale=1):
                progressbar = gr.HTML(elem_id="txt2img_progressbar")
476
                txt2img_preview = gr.Image(elem_id='txt2img_preview', visible=False)
477
                setup_progressbar(progressbar, txt2img_preview, 'txt2img')
478

479 480 481 482 483
        with gr.Row().style(equal_height=False):
            with gr.Column(variant='panel'):
                steps = gr.Slider(minimum=1, maximum=150, step=1, label="Sampling Steps", value=20)
                sampler_index = gr.Radio(label='Sampling method', elem_id="txt2img_sampling", choices=[x.name for x in samplers], value=samplers[0].name, type="index")

484 485 486 487
                with gr.Group():
                    width = gr.Slider(minimum=64, maximum=2048, step=64, label="Width", value=512)
                    height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height", value=512)

488
                with gr.Row():
A
AUTOMATIC 已提交
489
                    restore_faces = gr.Checkbox(label='Restore faces', value=False, visible=len(shared.face_restorers) > 1)
490
                    tiling = gr.Checkbox(label='Tiling', value=False)
A
AUTOMATIC 已提交
491 492 493
                    enable_hr = gr.Checkbox(label='Highres. fix', value=False)

                with gr.Row(visible=False) as hr_options:
494
                    scale_latent = gr.Checkbox(label='Scale latent', value=False)
A
AUTOMATIC 已提交
495
                    denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising strength', value=0.7)
496 497 498 499 500

                with gr.Row():
                    batch_count = gr.Slider(minimum=1, maximum=cmd_opts.max_batch_count, step=1, label='Batch count', value=1)
                    batch_size = gr.Slider(minimum=1, maximum=8, step=1, label='Batch size', value=1)

501
                cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG Scale', value=7.0)
502

503
                seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox = create_seed_inputs()
504

A
AUTOMATIC 已提交
505
                with gr.Group():
A
AUTOMATIC 已提交
506
                    custom_inputs = modules.scripts.scripts_txt2img.setup_ui(is_img2img=False)
507 508

            with gr.Column(variant='panel'):
A
AUTOMATIC 已提交
509

510
                with gr.Group():
A
AUTOMATIC 已提交
511
                    txt2img_preview = gr.Image(elem_id='txt2img_preview', visible=False)
A
AUTOMATIC 已提交
512
                    txt2img_gallery = gr.Gallery(label='Output', show_label=False, elem_id='txt2img_gallery').style(grid=4)
513 514 515 516 517 518 519

                with gr.Group():
                    with gr.Row():
                        save = gr.Button('Save')
                        send_to_img2img = gr.Button('Send to img2img')
                        send_to_inpaint = gr.Button('Send to inpaint')
                        send_to_extras = gr.Button('Send to extras')
M
Michoko 已提交
520
                        button_id = "hidden_element" if shared.cmd_opts.hide_ui_dir_config else 'open_folder'
M
Michoko 已提交
521
                        open_txt2img_folder = gr.Button(folder_symbol, elem_id=button_id)
522 523 524 525 526

                with gr.Group():
                    html_info = gr.HTML()
                    generation_info = gr.Textbox(visible=False)

527 528
            connect_reuse_seed(seed, reuse_seed, generation_info, dummy_component, is_subseed=False)
            connect_reuse_seed(subseed, reuse_subseed, generation_info, dummy_component, is_subseed=True)
529

530
            txt2img_args = dict(
531
                fn=wrap_gradio_gpu_call(modules.txt2img.txt2img),
A
AUTOMATIC 已提交
532
                _js="submit",
533
                inputs=[
A
AUTOMATIC 已提交
534
                    txt2img_prompt,
535
                    txt2img_negative_prompt,
A
AUTOMATIC 已提交
536
                    txt2img_prompt_style,
A
AUTOMATIC 已提交
537
                    txt2img_prompt_style2,
538 539
                    steps,
                    sampler_index,
A
AUTOMATIC 已提交
540
                    restore_faces,
541
                    tiling,
542 543 544 545
                    batch_count,
                    batch_size,
                    cfg_scale,
                    seed,
546
                    subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox,
547 548
                    height,
                    width,
A
AUTOMATIC 已提交
549 550 551
                    enable_hr,
                    scale_latent,
                    denoising_strength,
A
AUTOMATIC 已提交
552
                ] + custom_inputs,
553 554 555 556
                outputs=[
                    txt2img_gallery,
                    generation_info,
                    html_info
557 558
                ],
                show_progress=False,
559 560
            )

A
AUTOMATIC 已提交
561
            txt2img_prompt.submit(**txt2img_args)
562 563
            submit.click(**txt2img_args)

A
AUTOMATIC 已提交
564 565 566 567 568 569
            enable_hr.change(
                fn=lambda x: gr_show(x),
                inputs=[enable_hr],
                outputs=[hr_options],
            )

570 571
            save.click(
                fn=wrap_gradio_call(save_files),
572
                _js="(x, y, z) => [x, y, selected_gallery_index()]",
573 574 575
                inputs=[
                    generation_info,
                    txt2img_gallery,
576
                    html_info,
577 578 579 580 581 582 583 584
                ],
                outputs=[
                    html_info,
                    html_info,
                    html_info,
                ]
            )

A
AUTOMATIC 已提交
585 586 587
            roll.click(
                fn=roll_artist,
                inputs=[
A
AUTOMATIC 已提交
588
                    txt2img_prompt,
A
AUTOMATIC 已提交
589 590
                ],
                outputs=[
A
AUTOMATIC 已提交
591
                    txt2img_prompt,
A
AUTOMATIC 已提交
592 593 594
                ]
            )

595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
            txt2img_paste_fields = [
                (txt2img_prompt, "Prompt"),
                (txt2img_negative_prompt, "Negative prompt"),
                (steps, "Steps"),
                (sampler_index, "Sampler"),
                (restore_faces, "Face restoration"),
                (cfg_scale, "CFG scale"),
                (seed, "Seed"),
                (width, "Size-1"),
                (height, "Size-2"),
                (batch_size, "Batch size"),
                (subseed, "Variation seed"),
                (subseed_strength, "Variation seed strength"),
                (seed_resize_from_w, "Seed resize from-1"),
                (seed_resize_from_h, "Seed resize from-2"),
                (denoising_strength, "Denoising strength"),
                (enable_hr, lambda d: "Denoising strength" in d),
                (hr_options, lambda d: gr.Row.update(visible="Denoising strength" in d)),
            ]
614 615
            modules.generation_parameters_copypaste.connect_paste(paste, txt2img_paste_fields, txt2img_prompt)

616
    with gr.Blocks(analytics_enabled=False) as img2img_interface:
617
        img2img_prompt, roll, img2img_prompt_style, img2img_negative_prompt, img2img_prompt_style2, submit, img2img_interrogate, img2img_prompt_style_apply, img2img_save_style, paste = create_toprow(is_img2img=True)
618

619 620 621
        with gr.Row(elem_id='img2img_progress_row'):
            with gr.Column(scale=1):
                pass
622

623 624
            with gr.Column(scale=1):
                progressbar = gr.HTML(elem_id="img2img_progressbar")
625
                img2img_preview = gr.Image(elem_id='img2img_preview', visible=False)
626
                setup_progressbar(progressbar, img2img_preview, 'img2img')
627

628 629
        with gr.Row().style(equal_height=False):
            with gr.Column(variant='panel'):
630

631
                with gr.Tabs(elem_id="mode_img2img") as tabs_img2img_mode:
632
                    with gr.TabItem('img2img', id='img2img'):
633
                        init_img = gr.Image(label="Image for img2img", show_label=False, source="upload", interactive=True, type="pil")
634

635
                    with gr.TabItem('Inpaint', id='inpaint'):
636 637
                        init_img_with_mask = gr.Image(label="Image for inpainting with mask",  show_label=False, elem_id="img2maskimg", source="upload", interactive=True, type="pil", tool="sketch", image_mode="RGBA")

638 639
                        init_img_inpaint = gr.Image(label="Image for img2img", show_label=False, source="upload", interactive=True, type="pil", visible=False, elem_id="img_inpaint_base")
                        init_mask_inpaint = gr.Image(label="Mask", source="upload", interactive=True, type="pil", visible=False, elem_id="img_inpaint_mask")
640 641 642 643

                        mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=4)

                        with gr.Row():
644
                            mask_mode = gr.Radio(label="Mask mode", show_label=False, choices=["Draw mask", "Upload mask"], type="index", value="Draw mask", elem_id="mask_mode")
645 646
                            inpainting_mask_invert = gr.Radio(label='Masking mode', show_label=False, choices=['Inpaint masked', 'Inpaint not masked'], value='Inpaint masked', type="index")

647
                        inpainting_fill = gr.Radio(label='Masked content', choices=['fill', 'original', 'latent noise', 'latent nothing'], value='original', type="index")
648 649 650 651 652

                        with gr.Row():
                            inpaint_full_res = gr.Checkbox(label='Inpaint at full resolution', value=False)
                            inpaint_full_res_padding = gr.Slider(label='Inpaint at full resolution padding, pixels', minimum=0, maximum=256, step=4, value=32)

653
                    with gr.TabItem('Batch img2img', id='batch'):
654 655 656 657
                        hidden = '<br>Disabled when launched with --hide-ui-dir-config.' if shared.cmd_opts.hide_ui_dir_config else ''
                        gr.HTML(f"<p class=\"text-gray-500\">Process images in a directory on the same machine where the server is running.{hidden}</p>")
                        img2img_batch_input_dir = gr.Textbox(label="Input directory", **shared.hide_dirs)
                        img2img_batch_output_dir = gr.Textbox(label="Output directory", **shared.hide_dirs)
658 659

                with gr.Row():
660 661 662 663
                    resize_mode = gr.Radio(label="Resize mode", elem_id="resize_mode", show_label=False, choices=["Just resize", "Crop and resize", "Resize and fill"], type="index", value="Just resize")

                steps = gr.Slider(minimum=1, maximum=150, step=1, label="Sampling Steps", value=20)
                sampler_index = gr.Radio(label='Sampling method', choices=[x.name for x in samplers_for_img2img], value=samplers_for_img2img[0].name, type="index")
A
AUTOMATIC 已提交
664

665 666 667 668
                with gr.Group():
                    width = gr.Slider(minimum=64, maximum=2048, step=64, label="Width", value=512)
                    height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height", value=512)

A
AUTOMATIC 已提交
669
                with gr.Row():
A
AUTOMATIC 已提交
670
                    restore_faces = gr.Checkbox(label='Restore faces', value=False, visible=len(shared.face_restorers) > 1)
671
                    tiling = gr.Checkbox(label='Tiling', value=False)
672 673 674 675 676 677

                with gr.Row():
                    batch_count = gr.Slider(minimum=1, maximum=cmd_opts.max_batch_count, step=1, label='Batch count', value=1)
                    batch_size = gr.Slider(minimum=1, maximum=8, step=1, label='Batch size', value=1)

                with gr.Group():
678
                    cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG Scale', value=7.0)
679
                    denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising strength', value=0.75)
680

681
                seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox = create_seed_inputs()
682

A
AUTOMATIC 已提交
683
                with gr.Group():
A
AUTOMATIC 已提交
684
                    custom_inputs = modules.scripts.scripts_img2img.setup_ui(is_img2img=True)
A
AUTOMATIC 已提交
685

686
            with gr.Column(variant='panel'):
A
AUTOMATIC 已提交
687

688
                with gr.Group():
A
AUTOMATIC 已提交
689
                    img2img_preview = gr.Image(elem_id='img2img_preview', visible=False)
A
AUTOMATIC 已提交
690
                    img2img_gallery = gr.Gallery(label='Output', show_label=False, elem_id='img2img_gallery').style(grid=4)
691 692 693 694

                with gr.Group():
                    with gr.Row():
                        save = gr.Button('Save')
A
AUTOMATIC 已提交
695 696
                        img2img_send_to_img2img = gr.Button('Send to img2img')
                        img2img_send_to_inpaint = gr.Button('Send to inpaint')
697
                        img2img_send_to_extras = gr.Button('Send to extras')
M
Michoko 已提交
698
                        button_id = "hidden_element" if shared.cmd_opts.hide_ui_dir_config else 'open_folder'
M
Michoko 已提交
699
                        open_img2img_folder = gr.Button(folder_symbol, elem_id=button_id)
700 701 702 703 704

                with gr.Group():
                    html_info = gr.HTML()
                    generation_info = gr.Textbox(visible=False)

705 706
            connect_reuse_seed(seed, reuse_seed, generation_info, dummy_component, is_subseed=False)
            connect_reuse_seed(subseed, reuse_subseed, generation_info, dummy_component, is_subseed=True)
707

708
            mask_mode.change(
709
                lambda mode, img: {
710
                    init_img_with_mask: gr_show(mode == 0),
711 712
                    init_img_inpaint: gr_show(mode == 1),
                    init_mask_inpaint: gr_show(mode == 1),
713
                },
714
                inputs=[mask_mode, init_img_with_mask],
715 716
                outputs=[
                    init_img_with_mask,
717 718
                    init_img_inpaint,
                    init_mask_inpaint,
719 720 721
                ],
            )

722
            img2img_args = dict(
723
                fn=wrap_gradio_gpu_call(modules.img2img.img2img),
724
                _js="submit_img2img",
725
                inputs=[
726
                    dummy_component,
A
AUTOMATIC 已提交
727
                    img2img_prompt,
728
                    img2img_negative_prompt,
A
AUTOMATIC 已提交
729
                    img2img_prompt_style,
A
AUTOMATIC 已提交
730
                    img2img_prompt_style2,
731 732
                    init_img,
                    init_img_with_mask,
733 734
                    init_img_inpaint,
                    init_mask_inpaint,
735
                    mask_mode,
736 737 738 739
                    steps,
                    sampler_index,
                    mask_blur,
                    inpainting_fill,
A
AUTOMATIC 已提交
740
                    restore_faces,
741
                    tiling,
742 743 744 745 746
                    batch_count,
                    batch_size,
                    cfg_scale,
                    denoising_strength,
                    seed,
747
                    subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox,
748 749 750 751
                    height,
                    width,
                    resize_mode,
                    inpaint_full_res,
752
                    inpaint_full_res_padding,
A
AUTOMATIC 已提交
753
                    inpainting_mask_invert,
754 755
                    img2img_batch_input_dir,
                    img2img_batch_output_dir,
A
AUTOMATIC 已提交
756
                ] + custom_inputs,
757 758 759 760
                outputs=[
                    img2img_gallery,
                    generation_info,
                    html_info
761 762
                ],
                show_progress=False,
763 764
            )

A
AUTOMATIC 已提交
765
            img2img_prompt.submit(**img2img_args)
766 767
            submit.click(**img2img_args)

A
AUTOMATIC 已提交
768 769 770 771 772 773
            img2img_interrogate.click(
                fn=interrogate,
                inputs=[init_img],
                outputs=[img2img_prompt],
            )

774 775
            save.click(
                fn=wrap_gradio_call(save_files),
776
                _js="(x, y, z) => [x, y, selected_gallery_index()]",
777 778 779
                inputs=[
                    generation_info,
                    img2img_gallery,
J
jtkelm2 已提交
780
                    html_info
781 782 783 784 785 786 787 788
                ],
                outputs=[
                    html_info,
                    html_info,
                    html_info,
                ]
            )

A
AUTOMATIC 已提交
789 790 791 792 793 794 795 796 797 798 799 800 801 802
            roll.click(
                fn=roll_artist,
                inputs=[
                    img2img_prompt,
                ],
                outputs=[
                    img2img_prompt,
                ]
            )

            prompts = [(txt2img_prompt, txt2img_negative_prompt), (img2img_prompt, img2img_negative_prompt)]
            style_dropdowns = [(txt2img_prompt_style, txt2img_prompt_style2), (img2img_prompt_style, img2img_prompt_style2)]

            for button, (prompt, negative_prompt) in zip([txt2img_save_style, img2img_save_style], prompts):
A
AUTOMATIC 已提交
803 804 805
                button.click(
                    fn=add_style,
                    _js="ask_for_style_name",
806 807 808
                    # Have to pass empty dummy component here, because the JavaScript and Python function have to accept
                    # the same number of parameters, but we only know the style-name after the JavaScript prompt
                    inputs=[dummy_component, prompt, negative_prompt],
A
AUTOMATIC 已提交
809 810 811 812 813 814 815 816
                    outputs=[txt2img_prompt_style, img2img_prompt_style, txt2img_prompt_style2, img2img_prompt_style2],
                )

            for button, (prompt, negative_prompt), (style1, style2) in zip([txt2img_prompt_style_apply, img2img_prompt_style_apply], prompts, style_dropdowns):
                button.click(
                    fn=apply_styles,
                    inputs=[prompt, negative_prompt, style1, style2],
                    outputs=[prompt, negative_prompt, style1, style2],
A
AUTOMATIC 已提交
817 818
                )

819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835
            img2img_paste_fields = [
                (img2img_prompt, "Prompt"),
                (img2img_negative_prompt, "Negative prompt"),
                (steps, "Steps"),
                (sampler_index, "Sampler"),
                (restore_faces, "Face restoration"),
                (cfg_scale, "CFG scale"),
                (seed, "Seed"),
                (width, "Size-1"),
                (height, "Size-2"),
                (batch_size, "Batch size"),
                (subseed, "Variation seed"),
                (subseed_strength, "Variation seed strength"),
                (seed_resize_from_w, "Seed resize from-1"),
                (seed_resize_from_h, "Seed resize from-2"),
                (denoising_strength, "Denoising strength"),
            ]
836 837
            modules.generation_parameters_copypaste.connect_paste(paste, img2img_paste_fields, img2img_prompt)

838 839 840
    with gr.Blocks(analytics_enabled=False) as extras_interface:
        with gr.Row().style(equal_height=False):
            with gr.Column(variant='panel'):
841
                with gr.Tabs(elem_id="mode_extras"):
A
ArrowM 已提交
842
                    with gr.TabItem('Single Image'):
843
                        extras_image = gr.Image(label="Source", source="upload", interactive=True, type="pil")
A
ArrowM 已提交
844 845

                    with gr.TabItem('Batch Process'):
846
                        image_batch = gr.File(label="Batch Process", file_count="multiple", interactive=True, type="file")
A
AUTOMATIC 已提交
847 848 849 850 851 852 853 854 855 856 857

                upscaling_resize = gr.Slider(minimum=1.0, maximum=4.0, step=0.05, label="Resize", value=2)

                with gr.Group():
                    extras_upscaler_1 = gr.Radio(label='Upscaler 1', choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name, type="index")

                with gr.Group():
                    extras_upscaler_2 = gr.Radio(label='Upscaler 2', choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name, type="index")
                    extras_upscaler_2_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Upscaler 2 visibility", value=1)

                with gr.Group():
858 859 860 861
                    gfpgan_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="GFPGAN visibility", value=0, interactive=modules.gfpgan_model.have_gfpgan)

                with gr.Group():
                    codeformer_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="CodeFormer visibility", value=0, interactive=modules.codeformer_model.have_codeformer)
862
                    codeformer_weight = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="CodeFormer weight (0 = maximum effect, 1 = minimum effect)", value=0, interactive=modules.codeformer_model.have_codeformer)
863 864 865 866

                submit = gr.Button('Generate', elem_id="extras_generate", variant='primary')

            with gr.Column(variant='panel'):
A
AUTOMATIC 已提交
867
                result_images = gr.Gallery(label="Result", show_label=False)
868 869
                html_info_x = gr.HTML()
                html_info = gr.HTML()
S
Seki 已提交
870
                extras_send_to_img2img = gr.Button('Send to img2img')
S
Seki 已提交
871
                extras_send_to_inpaint = gr.Button('Send to inpaint')
M
Michoko 已提交
872
                button_id = "hidden_element" if shared.cmd_opts.hide_ui_dir_config else ''
M
Michoko 已提交
873
                open_extras_folder = gr.Button('Open output directory', elem_id=button_id)
874

875
        submit.click(
876
            fn=wrap_gradio_gpu_call(modules.extras.run_extras),
877
            _js="get_extras_tab_index",
878
            inputs=[
879
                dummy_component,
880
                extras_image,
A
ArrowM 已提交
881
                image_batch,
882 883 884
                gfpgan_visibility,
                codeformer_visibility,
                codeformer_weight,
A
AUTOMATIC 已提交
885 886 887 888
                upscaling_resize,
                extras_upscaler_1,
                extras_upscaler_2,
                extras_upscaler_2_visibility,
889 890
            ],
            outputs=[
A
ArrowM 已提交
891
                result_images,
892 893 894 895
                html_info_x,
                html_info,
            ]
        )
S
Seki 已提交
896 897 898 899 900 901 902
     
        extras_send_to_img2img.click(
            fn=lambda x: image_from_url_text(x),
            _js="extract_image_from_gallery_img2img",
            inputs=[result_images],
            outputs=[init_img],
        )
S
Seki 已提交
903 904 905 906 907 908 909
        
        extras_send_to_inpaint.click(
            fn=lambda x: image_from_url_text(x),
            _js="extract_image_from_gallery_img2img",
            inputs=[result_images],
            outputs=[init_img_with_mask],
        )
910

911 912 913 914 915 916 917 918 919 920 921 922 923 924 925
    with gr.Blocks(analytics_enabled=False) as pnginfo_interface:
        with gr.Row().style(equal_height=False):
            with gr.Column(variant='panel'):
                image = gr.Image(elem_id="pnginfo_image", label="Source", source="upload", interactive=True, type="pil")

            with gr.Column(variant='panel'):
                html = gr.HTML()
                generation_info = gr.Textbox(visible=False)
                html2 = gr.HTML()

                with gr.Row():
                    pnginfo_send_to_txt2img = gr.Button('Send to txt2img')
                    pnginfo_send_to_img2img = gr.Button('Send to img2img')

        image.change(
926
            fn=wrap_gradio_call(modules.extras.run_pnginfo),
927 928 929
            inputs=[image],
            outputs=[html, generation_info, html2],
        )
930

931 932 933
    with gr.Blocks() as modelmerger_interface:
        with gr.Row().style(equal_height=False):
            with gr.Column(variant='panel'):
934
                gr.HTML(value="<p>A merger of the two checkpoints will be generated in your <b>checkpoint</b> directory.</p>")
935

936
                with gr.Row():
937 938
                    primary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_primary_model_name", label="Primary Model Name")
                    secondary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_secondary_model_name", label="Secondary Model Name")
S
safentisAuth 已提交
939
                custom_name = gr.Textbox(label="Custom Name (Optional)")
940
                interp_amount = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Interpolation Amount', value=0.3)
941
                interp_method = gr.Radio(choices=["Weighted Sum", "Sigmoid", "Inverse Sigmoid"], value="Weighted Sum", label="Interpolation Method")
942 943
                save_as_half = gr.Checkbox(value=False, label="Safe as float16")
                modelmerger_merge = gr.Button(elem_id="modelmerger_merge", label="Merge", variant='primary')
944

945
            with gr.Column(variant='panel'):
946
                submit_result = gr.Textbox(elem_id="modelmerger_result", show_label=False)
947

948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
    sd_hijack.model_hijack.embedding_db.load_textual_inversion_embeddings()

    with gr.Blocks() as textual_inversion_interface:
        with gr.Row().style(equal_height=False):
            with gr.Column():
                with gr.Group():
                    gr.HTML(value="<p style='margin-bottom: 0.7em'>Create a new embedding</p>")

                    new_embedding_name = gr.Textbox(label="Name")
                    nvpt = gr.Slider(label="Number of vectors per token", minimum=1, maximum=75, step=1, value=1)

                    with gr.Row():
                        with gr.Column(scale=3):
                            gr.HTML(value="")

                        with gr.Column():
                            create_embedding = gr.Button(value="Create", variant='primary')

                with gr.Group():
                    gr.HTML(value="<p style='margin-bottom: 0.7em'>Train an embedding; must specify a directory with a set of 512x512 images</p>")
                    train_embedding_name = gr.Dropdown(label='Embedding', choices=sorted(sd_hijack.model_hijack.embedding_db.word_embeddings.keys()))
                    learn_rate = gr.Number(label='Learning rate', value=5.0e-03)
                    dataset_directory = gr.Textbox(label='Dataset directory', placeholder="Path to directory with input images")
                    log_directory = gr.Textbox(label='Log directory', placeholder="Path to directory where to write outputs", value="textual_inversion")
                    template_file = gr.Textbox(label='Prompt template file', value=os.path.join(script_path, "textual_inversion_templates", "style_filewords.txt"))
                    steps = gr.Number(label='Max steps', value=100000, precision=0)
                    create_image_every = gr.Number(label='Save an image to log directory every N steps, 0 to disable', value=1000, precision=0)
                    save_embedding_every = gr.Number(label='Save a copy of embedding to log directory every N steps, 0 to disable', value=1000, precision=0)

                    with gr.Row():
                        with gr.Column(scale=2):
                            gr.HTML(value="")

                        with gr.Column():
                            with gr.Row():
                                interrupt_training = gr.Button(value="Interrupt")
                                train_embedding = gr.Button(value="Train", variant='primary')

            with gr.Column():
                progressbar = gr.HTML(elem_id="ti_progressbar")
                ti_output = gr.Text(elem_id="ti_output", value="", show_label=False)

                ti_gallery = gr.Gallery(label='Output', show_label=False, elem_id='ti_gallery').style(grid=4)
                ti_preview = gr.Image(elem_id='ti_preview', visible=False)
                ti_progress = gr.HTML(elem_id="ti_progress", value="")
                ti_outcome = gr.HTML(elem_id="ti_error", value="")
                setup_progressbar(progressbar, ti_preview, 'ti', textinfo=ti_progress)

        create_embedding.click(
            fn=modules.textual_inversion.ui.create_embedding,
            inputs=[
                new_embedding_name,
                nvpt,
            ],
            outputs=[
                train_embedding_name,
                ti_output,
                ti_outcome,
            ]
        )

        train_embedding.click(
            fn=wrap_gradio_gpu_call(modules.textual_inversion.ui.train_embedding, extra_outputs=[gr.update()]),
            _js="start_training_textual_inversion",
            inputs=[
                train_embedding_name,
                learn_rate,
                dataset_directory,
                log_directory,
                steps,
                create_image_every,
                save_embedding_every,
                template_file,
            ],
            outputs=[
                ti_output,
                ti_outcome,
            ]
        )

        interrupt_training.click(
            fn=lambda: shared.state.interrupt(),
            inputs=[],
            outputs=[],
        )

1034 1035 1036 1037 1038 1039 1040
    def create_setting_component(key):
        def fun():
            return opts.data[key] if key in opts.data else opts.data_labels[key].default

        info = opts.data_labels[key]
        t = type(info.default)

1041 1042
        args = info.component_args() if callable(info.component_args) else info.component_args

1043
        if info.component is not None:
1044
            comp = info.component
1045
        elif t == str:
1046
            comp = gr.Textbox
1047
        elif t == int:
1048
            comp = gr.Number
1049
        elif t == bool:
1050
            comp = gr.Checkbox
1051 1052 1053
        else:
            raise Exception(f'bad options item type: {str(t)} for key {key}')

1054
        return comp(label=info.label, value=fun, **(args or {}))
1055

A
AUTOMATIC 已提交
1056
    components = []
1057
    component_dict = {}
A
AUTOMATIC 已提交
1058

M
Michoko 已提交
1059
    def open_folder(f):
M
Michoko 已提交
1060 1061 1062 1063 1064 1065 1066 1067
        if not shared.cmd_opts.hide_ui_dir_config:
            path = os.path.normpath(f)
            if platform.system() == "Windows":
                os.startfile(path)
            elif platform.system() == "Darwin":
                sp.Popen(["open", path])
            else:
                sp.Popen(["xdg-open", path])
A
AUTOMATIC 已提交
1068

1069
    def run_settings(*args):
1070 1071 1072 1073 1074
        changed = 0

        for key, value, comp in zip(opts.data_labels.keys(), args, components):
            if not opts.same_type(value, opts.data_labels[key].default):
                return f"Bad value for setting {key}: {value}; expecting {type(opts.data_labels[key].default).__name__}"
1075

A
AUTOMATIC 已提交
1076
        for key, value, comp in zip(opts.data_labels.keys(), args, components):
1077 1078 1079 1080
            comp_args = opts.data_labels[key].component_args
            if comp_args and isinstance(comp_args, dict) and comp_args.get('visible') is False:
                continue

1081
            oldval = opts.data.get(key, None)
1082
            opts.data[key] = value
1083

1084 1085 1086
            if oldval != value:
                if opts.data_labels[key].onchange is not None:
                    opts.data_labels[key].onchange()
1087

1088
                changed += 1
1089 1090 1091

        opts.save(shared.config_filename)

1092
        return f'{changed} settings changed.', opts.dumpjson()
1093

A
AUTOMATIC 已提交
1094
    with gr.Blocks(analytics_enabled=False) as settings_interface:
1095
        settings_submit = gr.Button(value="Apply settings", variant='primary')
A
AUTOMATIC 已提交
1096 1097
        result = gr.HTML()

1098 1099
        settings_cols = 3
        items_per_col = int(len(opts.data_labels) * 0.9 / settings_cols)
A
AUTOMATIC 已提交
1100

1101 1102 1103 1104 1105 1106
        cols_displayed = 0
        items_displayed = 0
        previous_section = None
        column = None
        with gr.Row(elem_id="settings").style(equal_height=False):
            for i, (k, item) in enumerate(opts.data_labels.items()):
D
DepFA 已提交
1107

1108 1109 1110 1111
                if previous_section != item.section:
                    if cols_displayed < settings_cols and (items_displayed >= items_per_col or previous_section is None):
                        if column is not None:
                            column.__exit__()
D
DepFA 已提交
1112

1113 1114
                        column = gr.Column(variant='panel')
                        column.__enter__()
A
AUTOMATIC 已提交
1115

1116 1117 1118 1119 1120 1121 1122
                        items_displayed = 0
                        cols_displayed += 1

                    previous_section = item.section

                    gr.HTML(elem_id="settings_header_text_{}".format(item.section[0]), value='<h1 class="gr-button-lg">{}</h1>'.format(item.section[1]))

1123 1124 1125
                component = create_setting_component(k)
                component_dict[k] = component
                components.append(component)
1126
                items_displayed += 1
1127

1128
        request_notifications = gr.Button(value='Request browser notifications', elem_id="request_notifications")
1129 1130 1131 1132
        request_notifications.click(
            fn=lambda: None,
            inputs=[],
            outputs=[],
1133
            _js='function(){}'
1134 1135
        )

1136 1137 1138
        if column is not None:
            column.__exit__()

1139
    interfaces = [
A
AUTOMATIC 已提交
1140 1141 1142 1143
        (txt2img_interface, "txt2img", "txt2img"),
        (img2img_interface, "img2img", "img2img"),
        (extras_interface, "Extras", "extras"),
        (pnginfo_interface, "PNG Info", "pnginfo"),
1144
        (modelmerger_interface, "Checkpoint Merger", "modelmerger"),
1145
        (textual_inversion_interface, "Textual inversion", "ti"),
A
AUTOMATIC 已提交
1146
        (settings_interface, "Settings", "settings"),
1147 1148 1149 1150 1151
    ]

    with open(os.path.join(script_path, "style.css"), "r", encoding="utf8") as file:
        css = file.read()

A
typo  
AUTOMATIC 已提交
1152
    if os.path.exists(os.path.join(script_path, "user.css")):
A
AUTOMATIC 已提交
1153 1154 1155 1156
        with open(os.path.join(script_path, "user.css"), "r", encoding="utf8") as file:
            usercss = file.read()
            css += usercss

1157 1158 1159
    if not cmd_opts.no_progressbar_hiding:
        css += css_hide_progressbar

A
AUTOMATIC 已提交
1160
    with gr.Blocks(css=css, analytics_enabled=False, title="Stable Diffusion") as demo:
1161

A
AUTOMATIC 已提交
1162 1163 1164 1165
        with gr.Tabs() as tabs:
            for interface, label, ifid in interfaces:
                with gr.TabItem(label, id=ifid):
                    interface.render()
1166 1167 1168
        
        if os.path.exists(os.path.join(script_path, "notification.mp3")):
            audio_notification = gr.Audio(interactive=False, value=os.path.join(script_path, "notification.mp3"), elem_id="audio_notification", visible=False)
A
AUTOMATIC 已提交
1169

1170
        text_settings = gr.Textbox(elem_id="settings_json", value=lambda: opts.dumpjson(), visible=False)
1171
        settings_submit.click(
1172 1173 1174
            fn=run_settings,
            inputs=components,
            outputs=[result, text_settings],
1175
        )
S
safentisAuth 已提交
1176 1177 1178
        
        def modelmerger(*args):
            try:
1179
                results = modules.extras.run_modelmerger(*args)
S
safentisAuth 已提交
1180 1181 1182
            except Exception as e:
                print("Error loading/saving model file:", file=sys.stderr)
                print(traceback.format_exc(), file=sys.stderr)
1183
                modules.sd_models.list_models()  # to remove the potentially missing models from the list
S
safentisAuth 已提交
1184 1185
                return ["Error loading/saving model file. It doesn't exist or the name contains illegal characters"] + [gr.Dropdown.update(choices=modules.sd_models.checkpoint_tiles()) for _ in range(3)]
            return results
1186

1187
        modelmerger_merge.click(
S
safentisAuth 已提交
1188
            fn=modelmerger,
1189 1190 1191 1192 1193 1194
            inputs=[
                primary_model_name,
                secondary_model_name,
                interp_method,
                interp_amount,
                save_as_half,
S
safentisAuth 已提交
1195
                custom_name,
1196 1197 1198 1199 1200 1201 1202 1203
            ],
            outputs=[
                submit_result,
                primary_model_name,
                secondary_model_name,
                component_dict['sd_model_checkpoint'],
            ]
        )
1204 1205 1206
        paste_field_names = ['Prompt', 'Negative prompt', 'Steps', 'Face restoration', 'Seed', 'Size-1', 'Size-2']
        txt2img_fields = [field for field,name in txt2img_paste_fields if name in paste_field_names]
        img2img_fields = [field for field,name in img2img_paste_fields if name in paste_field_names]
A
AUTOMATIC 已提交
1207
        send_to_img2img.click(
1208 1209 1210 1211
            fn=lambda img, *args: (image_from_url_text(img),*args),
            _js="(gallery, ...args) => [extract_image_from_gallery_img2img(gallery), ...args]",
            inputs=[txt2img_gallery] + txt2img_fields,
            outputs=[init_img] + img2img_fields,
A
AUTOMATIC 已提交
1212 1213 1214
        )

        send_to_inpaint.click(
1215 1216 1217 1218
            fn=lambda x, *args: (image_from_url_text(x), *args),
            _js="(gallery, ...args) => [extract_image_from_gallery_inpaint(gallery), ...args]",
            inputs=[txt2img_gallery] + txt2img_fields,
            outputs=[init_img_with_mask] + img2img_fields,
A
AUTOMATIC 已提交
1219 1220 1221 1222
        )

        img2img_send_to_img2img.click(
            fn=lambda x: image_from_url_text(x),
1223
            _js="extract_image_from_gallery_img2img",
A
AUTOMATIC 已提交
1224 1225 1226 1227 1228 1229
            inputs=[img2img_gallery],
            outputs=[init_img],
        )

        img2img_send_to_inpaint.click(
            fn=lambda x: image_from_url_text(x),
1230
            _js="extract_image_from_gallery_inpaint",
A
AUTOMATIC 已提交
1231 1232 1233 1234 1235 1236
            inputs=[img2img_gallery],
            outputs=[init_img_with_mask],
        )

        send_to_extras.click(
            fn=lambda x: image_from_url_text(x),
S
Seki 已提交
1237
            _js="extract_image_from_gallery_extras",
A
AUTOMATIC 已提交
1238
            inputs=[txt2img_gallery],
1239
            outputs=[extras_image],
A
AUTOMATIC 已提交
1240 1241
        )

M
Michoko 已提交
1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259
        open_txt2img_folder.click(
            fn=lambda: open_folder(opts.outdir_samples or opts.outdir_txt2img_samples),
            inputs=[],
            outputs=[],
        )

        open_img2img_folder.click(
            fn=lambda: open_folder(opts.outdir_samples or opts.outdir_img2img_samples),
            inputs=[],
            outputs=[],
        )

        open_extras_folder.click(
            fn=lambda: open_folder(opts.outdir_samples or opts.outdir_extras_samples),
            inputs=[],
            outputs=[],
        )

A
AUTOMATIC 已提交
1260 1261
        img2img_send_to_extras.click(
            fn=lambda x: image_from_url_text(x),
S
Seki 已提交
1262
            _js="extract_image_from_gallery_extras",
A
AUTOMATIC 已提交
1263
            inputs=[img2img_gallery],
1264
            outputs=[extras_image],
A
AUTOMATIC 已提交
1265
        )
1266

1267 1268 1269
        modules.generation_parameters_copypaste.connect_paste(pnginfo_send_to_txt2img, txt2img_paste_fields, generation_info, 'switch_to_txt2img')
        modules.generation_parameters_copypaste.connect_paste(pnginfo_send_to_img2img, img2img_paste_fields, generation_info, 'switch_to_img2img_img2img')

1270
    ui_config_file = cmd_opts.ui_config_file
A
AUTOMATIC 已提交
1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284
    ui_settings = {}
    settings_count = len(ui_settings)
    error_loading = False

    try:
        if os.path.exists(ui_config_file):
            with open(ui_config_file, "r", encoding="utf8") as file:
                ui_settings = json.load(file)
    except Exception:
        error_loading = True
        print("Error loading settings:", file=sys.stderr)
        print(traceback.format_exc(), file=sys.stderr)

    def loadsave(path, x):
1285
        def apply_field(obj, field, condition=None):
A
AUTOMATIC 已提交
1286
            key = path + "/" + field
1287 1288 1289

            if getattr(obj,'custom_script_source',None) is not None:
              key = 'customscript/' + obj.custom_script_source + '/' + key
D
DepFA 已提交
1290
            
A
AUTOMATIC 已提交
1291 1292
            if getattr(obj, 'do_not_save_to_config', False):
                return
D
DepFA 已提交
1293
            
A
AUTOMATIC 已提交
1294 1295 1296
            saved_value = ui_settings.get(key, None)
            if saved_value is None:
                ui_settings[key] = getattr(obj, field)
1297
            elif condition is None or condition(saved_value):
A
AUTOMATIC 已提交
1298 1299
                setattr(obj, field, saved_value)

A
AUTOMATIC 已提交
1300 1301 1302
        if type(x) in [gr.Slider, gr.Radio, gr.Checkbox, gr.Textbox, gr.Number] and x.visible:
            apply_field(x, 'visible')

A
AUTOMATIC 已提交
1303 1304 1305 1306 1307 1308 1309
        if type(x) == gr.Slider:
            apply_field(x, 'value')
            apply_field(x, 'minimum')
            apply_field(x, 'maximum')
            apply_field(x, 'step')

        if type(x) == gr.Radio:
1310
            apply_field(x, 'value', lambda val: val in x.choices)
A
AUTOMATIC 已提交
1311

D
DepFA 已提交
1312
        if type(x) == gr.Checkbox:
D
DepFA 已提交
1313
            apply_field(x, 'value')
D
DepFA 已提交
1314 1315

        if type(x) == gr.Textbox:
D
DepFA 已提交
1316
            apply_field(x, 'value')
D
DepFA 已提交
1317 1318
        
        if type(x) == gr.Number:
D
DepFA 已提交
1319
            apply_field(x, 'value')
D
DepFA 已提交
1320
        
A
AUTOMATIC 已提交
1321 1322
    visit(txt2img_interface, loadsave, "txt2img")
    visit(img2img_interface, loadsave, "img2img")
1323
    visit(extras_interface, loadsave, "extras")
A
AUTOMATIC 已提交
1324 1325 1326 1327 1328

    if not error_loading and (not os.path.exists(ui_config_file) or settings_count != len(ui_settings)):
        with open(ui_config_file, "w", encoding="utf8") as file:
            json.dump(ui_settings, file, indent=4)

1329 1330 1331
    return demo


A
AUTOMATIC 已提交
1332
with open(os.path.join(script_path, "script.js"), "r", encoding="utf8") as jsfile:
A
AUTOMATIC 已提交
1333 1334 1335
    javascript = f'<script>{jsfile.read()}</script>'

jsdir = os.path.join(script_path, "javascript")
1336
for filename in sorted(os.listdir(jsdir)):
A
AUTOMATIC 已提交
1337 1338
    with open(os.path.join(jsdir, filename), "r", encoding="utf8") as jsfile:
        javascript += f"\n<script>{jsfile.read()}</script>"
1339 1340


A
AUTOMATIC 已提交
1341 1342
def template_response(*args, **kwargs):
    res = gradio_routes_templates_response(*args, **kwargs)
A
AUTOMATIC 已提交
1343
    res.body = res.body.replace(b'</head>', f'{javascript}</head>'.encode("utf8"))
A
AUTOMATIC 已提交
1344 1345
    res.init_headers()
    return res
1346 1347


A
AUTOMATIC 已提交
1348 1349
gradio_routes_templates_response = gradio.routes.templates.TemplateResponse
gradio.routes.templates.TemplateResponse = template_response