ui.py 57 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322
  1. import base64
  2. import html
  3. import io
  4. import json
  5. import math
  6. import mimetypes
  7. import os
  8. import random
  9. import sys
  10. import time
  11. import traceback
  12. import platform
  13. import subprocess as sp
  14. import numpy as np
  15. import torch
  16. from PIL import Image, PngImagePlugin
  17. import piexif
  18. import gradio as gr
  19. import gradio.utils
  20. import gradio.routes
  21. from modules import sd_hijack
  22. from modules.paths import script_path
  23. from modules.shared import opts, cmd_opts
  24. import modules.shared as shared
  25. from modules.sd_samplers import samplers, samplers_for_img2img
  26. from modules.sd_hijack import model_hijack
  27. import modules.ldsr_model
  28. import modules.scripts
  29. import modules.gfpgan_model
  30. import modules.codeformer_model
  31. import modules.styles
  32. import modules.generation_parameters_copypaste
  33. import modules.textual_inversion.ui
  34. # 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
  35. mimetypes.init()
  36. mimetypes.add_type('application/javascript', '.js')
  37. if not cmd_opts.share and not cmd_opts.listen:
  38. # fix gradio phoning home
  39. gradio.utils.version_check = lambda: None
  40. gradio.utils.get_local_ip_address = lambda: '127.0.0.1'
  41. def gr_show(visible=True):
  42. return {"visible": visible, "__type__": "update"}
  43. sample_img2img = "assets/stable-samples/img2img/sketch-mountains-input.jpg"
  44. sample_img2img = sample_img2img if os.path.exists(sample_img2img) else None
  45. css_hide_progressbar = """
  46. .wrap .m-12 svg { display:none!important; }
  47. .wrap .m-12::before { content:"Loading..." }
  48. .progress-bar { display:none!important; }
  49. .meta-text { display:none!important; }
  50. """
  51. # Using constants for these since the variation selector isn't visible.
  52. # Important that they exactly match script.js for tooltip to work.
  53. random_symbol = '\U0001f3b2\ufe0f' # 🎲️
  54. reuse_symbol = '\u267b\ufe0f' # ♻️
  55. art_symbol = '\U0001f3a8' # 🎨
  56. paste_symbol = '\u2199\ufe0f' # ↙
  57. folder_symbol = '\uD83D\uDCC2'
  58. def plaintext_to_html(text):
  59. text = "<p>" + "<br>\n".join([f"{html.escape(x)}" for x in text.split('\n')]) + "</p>"
  60. return text
  61. def image_from_url_text(filedata):
  62. if type(filedata) == list:
  63. if len(filedata) == 0:
  64. return None
  65. filedata = filedata[0]
  66. if filedata.startswith("data:image/png;base64,"):
  67. filedata = filedata[len("data:image/png;base64,"):]
  68. filedata = base64.decodebytes(filedata.encode('utf-8'))
  69. image = Image.open(io.BytesIO(filedata))
  70. return image
  71. def send_gradio_gallery_to_image(x):
  72. if len(x) == 0:
  73. return None
  74. return image_from_url_text(x[0])
  75. def save_files(js_data, images, index):
  76. import csv
  77. os.makedirs(opts.outdir_save, exist_ok=True)
  78. filenames = []
  79. data = json.loads(js_data)
  80. 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
  81. images = [images[index]]
  82. infotexts = [data["infotexts"][index]]
  83. else:
  84. infotexts = data["infotexts"]
  85. with open(os.path.join(opts.outdir_save, "log.csv"), "a", encoding="utf8", newline='') as file:
  86. at_start = file.tell() == 0
  87. writer = csv.writer(file)
  88. if at_start:
  89. writer.writerow(["prompt", "seed", "width", "height", "sampler", "cfgs", "steps", "filename", "negative_prompt"])
  90. filename_base = str(int(time.time() * 1000))
  91. extension = opts.samples_format.lower()
  92. for i, filedata in enumerate(images):
  93. filename = filename_base + ("" if len(images) == 1 else "-" + str(i + 1)) + f".{extension}"
  94. filepath = os.path.join(opts.outdir_save, filename)
  95. if filedata.startswith("data:image/png;base64,"):
  96. filedata = filedata[len("data:image/png;base64,"):]
  97. image = Image.open(io.BytesIO(base64.decodebytes(filedata.encode('utf-8'))))
  98. if opts.enable_pnginfo and extension == 'png':
  99. pnginfo = PngImagePlugin.PngInfo()
  100. pnginfo.add_text('parameters', infotexts[i])
  101. image.save(filepath, pnginfo=pnginfo)
  102. else:
  103. image.save(filepath, quality=opts.jpeg_quality)
  104. if opts.enable_pnginfo and extension in ("jpg", "jpeg", "webp"):
  105. piexif.insert(piexif.dump({"Exif": {
  106. piexif.ExifIFD.UserComment: piexif.helper.UserComment.dump(infotexts[i], encoding="unicode")
  107. }}), filepath)
  108. filenames.append(filename)
  109. writer.writerow([data["prompt"], data["seed"], data["width"], data["height"], data["sampler"], data["cfg_scale"], data["steps"], filenames[0], data["negative_prompt"]])
  110. return '', '', plaintext_to_html(f"Saved: {filenames[0]}")
  111. def wrap_gradio_call(func, extra_outputs=None):
  112. def f(*args, extra_outputs_array=extra_outputs, **kwargs):
  113. run_memmon = opts.memmon_poll_rate > 0 and not shared.mem_mon.disabled
  114. if run_memmon:
  115. shared.mem_mon.monitor()
  116. t = time.perf_counter()
  117. try:
  118. res = list(func(*args, **kwargs))
  119. except Exception as e:
  120. print("Error completing request", file=sys.stderr)
  121. print("Arguments:", args, kwargs, file=sys.stderr)
  122. print(traceback.format_exc(), file=sys.stderr)
  123. shared.state.job = ""
  124. shared.state.job_count = 0
  125. if extra_outputs_array is None:
  126. extra_outputs_array = [None, '']
  127. res = extra_outputs_array + [f"<div class='error'>{plaintext_to_html(type(e).__name__+': '+str(e))}</div>"]
  128. elapsed = time.perf_counter() - t
  129. if run_memmon:
  130. mem_stats = {k: -(v//-(1024*1024)) for k, v in shared.mem_mon.stop().items()}
  131. active_peak = mem_stats['active_peak']
  132. reserved_peak = mem_stats['reserved_peak']
  133. sys_peak = mem_stats['system_peak']
  134. sys_total = mem_stats['total']
  135. sys_pct = round(sys_peak/max(sys_total, 1) * 100, 2)
  136. 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>"
  137. else:
  138. vram_html = ''
  139. # last item is always HTML
  140. res[-1] += f"<div class='performance'><p class='time'>Time taken: <wbr>{elapsed:.2f}s</p>{vram_html}</div>"
  141. shared.state.interrupted = False
  142. shared.state.job_count = 0
  143. return tuple(res)
  144. return f
  145. def check_progress_call(id_part):
  146. if shared.state.job_count == 0:
  147. return "", gr_show(False), gr_show(False), gr_show(False)
  148. progress = 0
  149. if shared.state.job_count > 0:
  150. progress += shared.state.job_no / shared.state.job_count
  151. if shared.state.sampling_steps > 0:
  152. progress += 1 / shared.state.job_count * shared.state.sampling_step / shared.state.sampling_steps
  153. progress = min(progress, 1)
  154. progressbar = ""
  155. if opts.show_progressbar:
  156. progressbar = f"""<div class='progressDiv'><div class='progress' style="width:{progress * 100}%">{str(int(progress*100))+"%" if progress > 0.01 else ""}</div></div>"""
  157. image = gr_show(False)
  158. preview_visibility = gr_show(False)
  159. if opts.show_progress_every_n_steps > 0:
  160. if shared.parallel_processing_allowed:
  161. 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:
  162. shared.state.current_image = modules.sd_samplers.sample_to_image(shared.state.current_latent)
  163. shared.state.current_image_sampling_step = shared.state.sampling_step
  164. image = shared.state.current_image
  165. if image is None:
  166. image = gr.update(value=None)
  167. else:
  168. preview_visibility = gr_show(True)
  169. if shared.state.textinfo is not None:
  170. textinfo_result = gr.HTML.update(value=shared.state.textinfo, visible=True)
  171. else:
  172. textinfo_result = gr_show(False)
  173. return f"<span id='{id_part}_progress_span' style='display: none'>{time.time()}</span><p>{progressbar}</p>", preview_visibility, image, textinfo_result
  174. def check_progress_call_initial(id_part):
  175. shared.state.job_count = -1
  176. shared.state.current_latent = None
  177. shared.state.current_image = None
  178. shared.state.textinfo = None
  179. return check_progress_call(id_part)
  180. def roll_artist(prompt):
  181. 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])
  182. artist = random.choice([x for x in shared.artist_db.artists if x.category in allowed_cats])
  183. return prompt + ", " + artist.name if prompt != '' else artist.name
  184. def visit(x, func, path=""):
  185. if hasattr(x, 'children'):
  186. for c in x.children:
  187. visit(c, func, path)
  188. elif x.label is not None:
  189. func(path + "/" + str(x.label), x)
  190. def add_style(name: str, prompt: str, negative_prompt: str):
  191. if name is None:
  192. return [gr_show(), gr_show()]
  193. style = modules.styles.PromptStyle(name, prompt, negative_prompt)
  194. shared.prompt_styles.styles[style.name] = style
  195. # Save all loaded prompt styles: this allows us to update the storage format in the future more easily, because we
  196. # reserialize all styles every time we save them
  197. shared.prompt_styles.save_styles(shared.styles_filename)
  198. return [gr.Dropdown.update(visible=True, choices=list(shared.prompt_styles.styles)) for _ in range(4)]
  199. def apply_styles(prompt, prompt_neg, style1_name, style2_name):
  200. prompt = shared.prompt_styles.apply_styles_to_prompt(prompt, [style1_name, style2_name])
  201. prompt_neg = shared.prompt_styles.apply_negative_styles_to_prompt(prompt_neg, [style1_name, style2_name])
  202. return [gr.Textbox.update(value=prompt), gr.Textbox.update(value=prompt_neg), gr.Dropdown.update(value="None"), gr.Dropdown.update(value="None")]
  203. def interrogate(image):
  204. prompt = shared.interrogator.interrogate(image)
  205. return gr_show(True) if prompt is None else prompt
  206. def create_seed_inputs():
  207. with gr.Row():
  208. with gr.Box():
  209. with gr.Row(elem_id='seed_row'):
  210. seed = (gr.Textbox if cmd_opts.use_textbox_seed else gr.Number)(label='Seed', value=-1)
  211. seed.style(container=False)
  212. random_seed = gr.Button(random_symbol, elem_id='random_seed')
  213. reuse_seed = gr.Button(reuse_symbol, elem_id='reuse_seed')
  214. with gr.Box(elem_id='subseed_show_box'):
  215. seed_checkbox = gr.Checkbox(label='Extra', elem_id='subseed_show', value=False)
  216. # Components to show/hide based on the 'Extra' checkbox
  217. seed_extras = []
  218. with gr.Row(visible=False) as seed_extra_row_1:
  219. seed_extras.append(seed_extra_row_1)
  220. with gr.Box():
  221. with gr.Row(elem_id='subseed_row'):
  222. subseed = gr.Number(label='Variation seed', value=-1)
  223. subseed.style(container=False)
  224. random_subseed = gr.Button(random_symbol, elem_id='random_subseed')
  225. reuse_subseed = gr.Button(reuse_symbol, elem_id='reuse_subseed')
  226. subseed_strength = gr.Slider(label='Variation strength', value=0.0, minimum=0, maximum=1, step=0.01)
  227. with gr.Row(visible=False) as seed_extra_row_2:
  228. seed_extras.append(seed_extra_row_2)
  229. seed_resize_from_w = gr.Slider(minimum=0, maximum=2048, step=64, label="Resize seed from width", value=0)
  230. seed_resize_from_h = gr.Slider(minimum=0, maximum=2048, step=64, label="Resize seed from height", value=0)
  231. random_seed.click(fn=lambda: -1, show_progress=False, inputs=[], outputs=[seed])
  232. random_subseed.click(fn=lambda: -1, show_progress=False, inputs=[], outputs=[subseed])
  233. def change_visibility(show):
  234. return {comp: gr_show(show) for comp in seed_extras}
  235. seed_checkbox.change(change_visibility, show_progress=False, inputs=[seed_checkbox], outputs=seed_extras)
  236. return seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox
  237. def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info: gr.Textbox, dummy_component, is_subseed):
  238. """ Connects a 'reuse (sub)seed' button's click event so that it copies last used
  239. (sub)seed value from generation info the to the seed field. If copying subseed and subseed strength
  240. was 0, i.e. no variation seed was used, it copies the normal seed value instead."""
  241. def copy_seed(gen_info_string: str, index):
  242. res = -1
  243. try:
  244. gen_info = json.loads(gen_info_string)
  245. index -= gen_info.get('index_of_first_image', 0)
  246. if is_subseed and gen_info.get('subseed_strength', 0) > 0:
  247. all_subseeds = gen_info.get('all_subseeds', [-1])
  248. res = all_subseeds[index if 0 <= index < len(all_subseeds) else 0]
  249. else:
  250. all_seeds = gen_info.get('all_seeds', [-1])
  251. res = all_seeds[index if 0 <= index < len(all_seeds) else 0]
  252. except json.decoder.JSONDecodeError as e:
  253. if gen_info_string != '':
  254. print("Error parsing JSON generation info:", file=sys.stderr)
  255. print(gen_info_string, file=sys.stderr)
  256. return [res, gr_show(False)]
  257. reuse_seed.click(
  258. fn=copy_seed,
  259. _js="(x, y) => [x, selected_gallery_index()]",
  260. show_progress=False,
  261. inputs=[generation_info, dummy_component],
  262. outputs=[seed, dummy_component]
  263. )
  264. def update_token_counter(text):
  265. tokens, token_count, max_length = model_hijack.tokenize(text)
  266. style_class = ' class="red"' if (token_count > max_length) else ""
  267. return f"<span {style_class}>{token_count}/{max_length}</span>"
  268. def create_toprow(is_img2img):
  269. id_part = "img2img" if is_img2img else "txt2img"
  270. with gr.Row(elem_id="toprow"):
  271. with gr.Column(scale=4):
  272. with gr.Row():
  273. with gr.Column(scale=80):
  274. with gr.Row():
  275. prompt = gr.Textbox(label="Prompt", elem_id=f"{id_part}_prompt", show_label=False, placeholder="Prompt", lines=2)
  276. with gr.Column(scale=1, elem_id="roll_col"):
  277. roll = gr.Button(value=art_symbol, elem_id="roll", visible=len(shared.artist_db.artists) > 0)
  278. paste = gr.Button(value=paste_symbol, elem_id="paste")
  279. token_counter = gr.HTML(value="<span></span>", elem_id=f"{id_part}_token_counter")
  280. hidden_button = gr.Button(visible=False, elem_id=f"{id_part}_token_button")
  281. hidden_button.click(fn=update_token_counter, inputs=[prompt], outputs=[token_counter])
  282. with gr.Column(scale=10, elem_id="style_pos_col"):
  283. 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)
  284. with gr.Row():
  285. with gr.Column(scale=8):
  286. negative_prompt = gr.Textbox(label="Negative prompt", elem_id="negative_prompt", show_label=False, placeholder="Negative prompt", lines=2)
  287. with gr.Column(scale=1, elem_id="style_neg_col"):
  288. 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)
  289. with gr.Column(scale=1):
  290. with gr.Row():
  291. interrupt = gr.Button('Interrupt', elem_id=f"{id_part}_interrupt")
  292. submit = gr.Button('Generate', elem_id=f"{id_part}_generate", variant='primary')
  293. interrupt.click(
  294. fn=lambda: shared.state.interrupt(),
  295. inputs=[],
  296. outputs=[],
  297. )
  298. with gr.Row():
  299. if is_img2img:
  300. interrogate = gr.Button('Interrogate', elem_id="interrogate")
  301. else:
  302. interrogate = None
  303. prompt_style_apply = gr.Button('Apply style', elem_id="style_apply")
  304. save_style = gr.Button('Create style', elem_id="style_create")
  305. return prompt, roll, prompt_style, negative_prompt, prompt_style2, submit, interrogate, prompt_style_apply, save_style, paste
  306. def setup_progressbar(progressbar, preview, id_part, textinfo=None):
  307. if textinfo is None:
  308. textinfo = gr.HTML(visible=False)
  309. check_progress = gr.Button('Check progress', elem_id=f"{id_part}_check_progress", visible=False)
  310. check_progress.click(
  311. fn=lambda: check_progress_call(id_part),
  312. show_progress=False,
  313. inputs=[],
  314. outputs=[progressbar, preview, preview, textinfo],
  315. )
  316. check_progress_initial = gr.Button('Check progress (first)', elem_id=f"{id_part}_check_progress_initial", visible=False)
  317. check_progress_initial.click(
  318. fn=lambda: check_progress_call_initial(id_part),
  319. show_progress=False,
  320. inputs=[],
  321. outputs=[progressbar, preview, preview, textinfo],
  322. )
  323. def create_ui(wrap_gradio_gpu_call):
  324. import modules.img2img
  325. import modules.txt2img
  326. with gr.Blocks(analytics_enabled=False) as txt2img_interface:
  327. 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)
  328. dummy_component = gr.Label(visible=False)
  329. with gr.Row(elem_id='txt2img_progress_row'):
  330. with gr.Column(scale=1):
  331. pass
  332. with gr.Column(scale=1):
  333. progressbar = gr.HTML(elem_id="txt2img_progressbar")
  334. txt2img_preview = gr.Image(elem_id='txt2img_preview', visible=False)
  335. setup_progressbar(progressbar, txt2img_preview, 'txt2img')
  336. with gr.Row().style(equal_height=False):
  337. with gr.Column(variant='panel'):
  338. steps = gr.Slider(minimum=1, maximum=150, step=1, label="Sampling Steps", value=20)
  339. sampler_index = gr.Radio(label='Sampling method', elem_id="txt2img_sampling", choices=[x.name for x in samplers], value=samplers[0].name, type="index")
  340. with gr.Group():
  341. width = gr.Slider(minimum=64, maximum=2048, step=64, label="Width", value=512)
  342. height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height", value=512)
  343. with gr.Row():
  344. restore_faces = gr.Checkbox(label='Restore faces', value=False, visible=len(shared.face_restorers) > 1)
  345. tiling = gr.Checkbox(label='Tiling', value=False)
  346. enable_hr = gr.Checkbox(label='Highres. fix', value=False)
  347. with gr.Row(visible=False) as hr_options:
  348. scale_latent = gr.Checkbox(label='Scale latent', value=False)
  349. denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising strength', value=0.7)
  350. with gr.Row():
  351. batch_count = gr.Slider(minimum=1, maximum=cmd_opts.max_batch_count, step=1, label='Batch count', value=1)
  352. batch_size = gr.Slider(minimum=1, maximum=8, step=1, label='Batch size', value=1)
  353. cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG Scale', value=7.0)
  354. seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox = create_seed_inputs()
  355. with gr.Group():
  356. custom_inputs = modules.scripts.scripts_txt2img.setup_ui(is_img2img=False)
  357. with gr.Column(variant='panel'):
  358. with gr.Group():
  359. txt2img_preview = gr.Image(elem_id='txt2img_preview', visible=False)
  360. txt2img_gallery = gr.Gallery(label='Output', show_label=False, elem_id='txt2img_gallery').style(grid=4)
  361. with gr.Group():
  362. with gr.Row():
  363. save = gr.Button('Save')
  364. send_to_img2img = gr.Button('Send to img2img')
  365. send_to_inpaint = gr.Button('Send to inpaint')
  366. send_to_extras = gr.Button('Send to extras')
  367. button_id = "hidden_element" if shared.cmd_opts.hide_ui_dir_config else 'open_folder'
  368. open_txt2img_folder = gr.Button(folder_symbol, elem_id=button_id)
  369. with gr.Group():
  370. html_info = gr.HTML()
  371. generation_info = gr.Textbox(visible=False)
  372. connect_reuse_seed(seed, reuse_seed, generation_info, dummy_component, is_subseed=False)
  373. connect_reuse_seed(subseed, reuse_subseed, generation_info, dummy_component, is_subseed=True)
  374. txt2img_args = dict(
  375. fn=wrap_gradio_gpu_call(modules.txt2img.txt2img),
  376. _js="submit",
  377. inputs=[
  378. txt2img_prompt,
  379. txt2img_negative_prompt,
  380. txt2img_prompt_style,
  381. txt2img_prompt_style2,
  382. steps,
  383. sampler_index,
  384. restore_faces,
  385. tiling,
  386. batch_count,
  387. batch_size,
  388. cfg_scale,
  389. seed,
  390. subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox,
  391. height,
  392. width,
  393. enable_hr,
  394. scale_latent,
  395. denoising_strength,
  396. ] + custom_inputs,
  397. outputs=[
  398. txt2img_gallery,
  399. generation_info,
  400. html_info
  401. ],
  402. show_progress=False,
  403. )
  404. txt2img_prompt.submit(**txt2img_args)
  405. submit.click(**txt2img_args)
  406. enable_hr.change(
  407. fn=lambda x: gr_show(x),
  408. inputs=[enable_hr],
  409. outputs=[hr_options],
  410. )
  411. save.click(
  412. fn=wrap_gradio_call(save_files),
  413. _js="(x, y, z) => [x, y, selected_gallery_index()]",
  414. inputs=[
  415. generation_info,
  416. txt2img_gallery,
  417. html_info,
  418. ],
  419. outputs=[
  420. html_info,
  421. html_info,
  422. html_info,
  423. ]
  424. )
  425. roll.click(
  426. fn=roll_artist,
  427. inputs=[
  428. txt2img_prompt,
  429. ],
  430. outputs=[
  431. txt2img_prompt,
  432. ]
  433. )
  434. txt2img_paste_fields = [
  435. (txt2img_prompt, "Prompt"),
  436. (txt2img_negative_prompt, "Negative prompt"),
  437. (steps, "Steps"),
  438. (sampler_index, "Sampler"),
  439. (restore_faces, "Face restoration"),
  440. (cfg_scale, "CFG scale"),
  441. (seed, "Seed"),
  442. (width, "Size-1"),
  443. (height, "Size-2"),
  444. (batch_size, "Batch size"),
  445. (subseed, "Variation seed"),
  446. (subseed_strength, "Variation seed strength"),
  447. (seed_resize_from_w, "Seed resize from-1"),
  448. (seed_resize_from_h, "Seed resize from-2"),
  449. (denoising_strength, "Denoising strength"),
  450. (enable_hr, lambda d: "Denoising strength" in d),
  451. (hr_options, lambda d: gr.Row.update(visible="Denoising strength" in d)),
  452. ]
  453. modules.generation_parameters_copypaste.connect_paste(paste, txt2img_paste_fields, txt2img_prompt)
  454. with gr.Blocks(analytics_enabled=False) as img2img_interface:
  455. 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)
  456. with gr.Row(elem_id='img2img_progress_row'):
  457. with gr.Column(scale=1):
  458. pass
  459. with gr.Column(scale=1):
  460. progressbar = gr.HTML(elem_id="img2img_progressbar")
  461. img2img_preview = gr.Image(elem_id='img2img_preview', visible=False)
  462. setup_progressbar(progressbar, img2img_preview, 'img2img')
  463. with gr.Row().style(equal_height=False):
  464. with gr.Column(variant='panel'):
  465. with gr.Tabs(elem_id="mode_img2img") as tabs_img2img_mode:
  466. with gr.TabItem('img2img', id='img2img'):
  467. init_img = gr.Image(label="Image for img2img", show_label=False, source="upload", interactive=True, type="pil")
  468. with gr.TabItem('Inpaint', id='inpaint'):
  469. 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")
  470. 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")
  471. init_mask_inpaint = gr.Image(label="Mask", source="upload", interactive=True, type="pil", visible=False, elem_id="img_inpaint_mask")
  472. mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=4)
  473. with gr.Row():
  474. mask_mode = gr.Radio(label="Mask mode", show_label=False, choices=["Draw mask", "Upload mask"], type="index", value="Draw mask", elem_id="mask_mode")
  475. inpainting_mask_invert = gr.Radio(label='Masking mode', show_label=False, choices=['Inpaint masked', 'Inpaint not masked'], value='Inpaint masked', type="index")
  476. inpainting_fill = gr.Radio(label='Masked content', choices=['fill', 'original', 'latent noise', 'latent nothing'], value='original', type="index")
  477. with gr.Row():
  478. inpaint_full_res = gr.Checkbox(label='Inpaint at full resolution', value=False)
  479. inpaint_full_res_padding = gr.Slider(label='Inpaint at full resolution padding, pixels', minimum=0, maximum=256, step=4, value=32)
  480. with gr.TabItem('Batch img2img', id='batch'):
  481. hidden = '<br>Disabled when launched with --hide-ui-dir-config.' if shared.cmd_opts.hide_ui_dir_config else ''
  482. gr.HTML(f"<p class=\"text-gray-500\">Process images in a directory on the same machine where the server is running.{hidden}</p>")
  483. img2img_batch_input_dir = gr.Textbox(label="Input directory", **shared.hide_dirs)
  484. img2img_batch_output_dir = gr.Textbox(label="Output directory", **shared.hide_dirs)
  485. with gr.Row():
  486. 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")
  487. steps = gr.Slider(minimum=1, maximum=150, step=1, label="Sampling Steps", value=20)
  488. sampler_index = gr.Radio(label='Sampling method', choices=[x.name for x in samplers_for_img2img], value=samplers_for_img2img[0].name, type="index")
  489. with gr.Group():
  490. width = gr.Slider(minimum=64, maximum=2048, step=64, label="Width", value=512)
  491. height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height", value=512)
  492. with gr.Row():
  493. restore_faces = gr.Checkbox(label='Restore faces', value=False, visible=len(shared.face_restorers) > 1)
  494. tiling = gr.Checkbox(label='Tiling', value=False)
  495. with gr.Row():
  496. batch_count = gr.Slider(minimum=1, maximum=cmd_opts.max_batch_count, step=1, label='Batch count', value=1)
  497. batch_size = gr.Slider(minimum=1, maximum=8, step=1, label='Batch size', value=1)
  498. with gr.Group():
  499. cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG Scale', value=7.0)
  500. denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising strength', value=0.75)
  501. seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox = create_seed_inputs()
  502. with gr.Group():
  503. custom_inputs = modules.scripts.scripts_img2img.setup_ui(is_img2img=True)
  504. with gr.Column(variant='panel'):
  505. with gr.Group():
  506. img2img_preview = gr.Image(elem_id='img2img_preview', visible=False)
  507. img2img_gallery = gr.Gallery(label='Output', show_label=False, elem_id='img2img_gallery').style(grid=4)
  508. with gr.Group():
  509. with gr.Row():
  510. save = gr.Button('Save')
  511. img2img_send_to_img2img = gr.Button('Send to img2img')
  512. img2img_send_to_inpaint = gr.Button('Send to inpaint')
  513. img2img_send_to_extras = gr.Button('Send to extras')
  514. button_id = "hidden_element" if shared.cmd_opts.hide_ui_dir_config else 'open_folder'
  515. open_img2img_folder = gr.Button(folder_symbol, elem_id=button_id)
  516. with gr.Group():
  517. html_info = gr.HTML()
  518. generation_info = gr.Textbox(visible=False)
  519. connect_reuse_seed(seed, reuse_seed, generation_info, dummy_component, is_subseed=False)
  520. connect_reuse_seed(subseed, reuse_subseed, generation_info, dummy_component, is_subseed=True)
  521. mask_mode.change(
  522. lambda mode, img: {
  523. init_img_with_mask: gr_show(mode == 0),
  524. init_img_inpaint: gr_show(mode == 1),
  525. init_mask_inpaint: gr_show(mode == 1),
  526. },
  527. inputs=[mask_mode, init_img_with_mask],
  528. outputs=[
  529. init_img_with_mask,
  530. init_img_inpaint,
  531. init_mask_inpaint,
  532. ],
  533. )
  534. img2img_args = dict(
  535. fn=wrap_gradio_gpu_call(modules.img2img.img2img),
  536. _js="submit_img2img",
  537. inputs=[
  538. dummy_component,
  539. img2img_prompt,
  540. img2img_negative_prompt,
  541. img2img_prompt_style,
  542. img2img_prompt_style2,
  543. init_img,
  544. init_img_with_mask,
  545. init_img_inpaint,
  546. init_mask_inpaint,
  547. mask_mode,
  548. steps,
  549. sampler_index,
  550. mask_blur,
  551. inpainting_fill,
  552. restore_faces,
  553. tiling,
  554. batch_count,
  555. batch_size,
  556. cfg_scale,
  557. denoising_strength,
  558. seed,
  559. subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox,
  560. height,
  561. width,
  562. resize_mode,
  563. inpaint_full_res,
  564. inpaint_full_res_padding,
  565. inpainting_mask_invert,
  566. img2img_batch_input_dir,
  567. img2img_batch_output_dir,
  568. ] + custom_inputs,
  569. outputs=[
  570. img2img_gallery,
  571. generation_info,
  572. html_info
  573. ],
  574. show_progress=False,
  575. )
  576. img2img_prompt.submit(**img2img_args)
  577. submit.click(**img2img_args)
  578. img2img_interrogate.click(
  579. fn=interrogate,
  580. inputs=[init_img],
  581. outputs=[img2img_prompt],
  582. )
  583. save.click(
  584. fn=wrap_gradio_call(save_files),
  585. _js="(x, y, z) => [x, y, selected_gallery_index()]",
  586. inputs=[
  587. generation_info,
  588. img2img_gallery,
  589. html_info
  590. ],
  591. outputs=[
  592. html_info,
  593. html_info,
  594. html_info,
  595. ]
  596. )
  597. roll.click(
  598. fn=roll_artist,
  599. inputs=[
  600. img2img_prompt,
  601. ],
  602. outputs=[
  603. img2img_prompt,
  604. ]
  605. )
  606. prompts = [(txt2img_prompt, txt2img_negative_prompt), (img2img_prompt, img2img_negative_prompt)]
  607. style_dropdowns = [(txt2img_prompt_style, txt2img_prompt_style2), (img2img_prompt_style, img2img_prompt_style2)]
  608. for button, (prompt, negative_prompt) in zip([txt2img_save_style, img2img_save_style], prompts):
  609. button.click(
  610. fn=add_style,
  611. _js="ask_for_style_name",
  612. # Have to pass empty dummy component here, because the JavaScript and Python function have to accept
  613. # the same number of parameters, but we only know the style-name after the JavaScript prompt
  614. inputs=[dummy_component, prompt, negative_prompt],
  615. outputs=[txt2img_prompt_style, img2img_prompt_style, txt2img_prompt_style2, img2img_prompt_style2],
  616. )
  617. for button, (prompt, negative_prompt), (style1, style2) in zip([txt2img_prompt_style_apply, img2img_prompt_style_apply], prompts, style_dropdowns):
  618. button.click(
  619. fn=apply_styles,
  620. inputs=[prompt, negative_prompt, style1, style2],
  621. outputs=[prompt, negative_prompt, style1, style2],
  622. )
  623. img2img_paste_fields = [
  624. (img2img_prompt, "Prompt"),
  625. (img2img_negative_prompt, "Negative prompt"),
  626. (steps, "Steps"),
  627. (sampler_index, "Sampler"),
  628. (restore_faces, "Face restoration"),
  629. (cfg_scale, "CFG scale"),
  630. (seed, "Seed"),
  631. (width, "Size-1"),
  632. (height, "Size-2"),
  633. (batch_size, "Batch size"),
  634. (subseed, "Variation seed"),
  635. (subseed_strength, "Variation seed strength"),
  636. (seed_resize_from_w, "Seed resize from-1"),
  637. (seed_resize_from_h, "Seed resize from-2"),
  638. (denoising_strength, "Denoising strength"),
  639. ]
  640. modules.generation_parameters_copypaste.connect_paste(paste, img2img_paste_fields, img2img_prompt)
  641. with gr.Blocks(analytics_enabled=False) as extras_interface:
  642. with gr.Row().style(equal_height=False):
  643. with gr.Column(variant='panel'):
  644. with gr.Tabs(elem_id="mode_extras"):
  645. with gr.TabItem('Single Image'):
  646. extras_image = gr.Image(label="Source", source="upload", interactive=True, type="pil")
  647. with gr.TabItem('Batch Process'):
  648. image_batch = gr.File(label="Batch Process", file_count="multiple", interactive=True, type="file")
  649. upscaling_resize = gr.Slider(minimum=1.0, maximum=4.0, step=0.05, label="Resize", value=2)
  650. with gr.Group():
  651. 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")
  652. with gr.Group():
  653. 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")
  654. extras_upscaler_2_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Upscaler 2 visibility", value=1)
  655. with gr.Group():
  656. gfpgan_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="GFPGAN visibility", value=0, interactive=modules.gfpgan_model.have_gfpgan)
  657. with gr.Group():
  658. codeformer_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="CodeFormer visibility", value=0, interactive=modules.codeformer_model.have_codeformer)
  659. 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)
  660. submit = gr.Button('Generate', elem_id="extras_generate", variant='primary')
  661. with gr.Column(variant='panel'):
  662. result_images = gr.Gallery(label="Result", show_label=False)
  663. html_info_x = gr.HTML()
  664. html_info = gr.HTML()
  665. extras_send_to_img2img = gr.Button('Send to img2img')
  666. extras_send_to_inpaint = gr.Button('Send to inpaint')
  667. button_id = "hidden_element" if shared.cmd_opts.hide_ui_dir_config else ''
  668. open_extras_folder = gr.Button('Open output directory', elem_id=button_id)
  669. submit.click(
  670. fn=wrap_gradio_gpu_call(modules.extras.run_extras),
  671. _js="get_extras_tab_index",
  672. inputs=[
  673. dummy_component,
  674. extras_image,
  675. image_batch,
  676. gfpgan_visibility,
  677. codeformer_visibility,
  678. codeformer_weight,
  679. upscaling_resize,
  680. extras_upscaler_1,
  681. extras_upscaler_2,
  682. extras_upscaler_2_visibility,
  683. ],
  684. outputs=[
  685. result_images,
  686. html_info_x,
  687. html_info,
  688. ]
  689. )
  690. extras_send_to_img2img.click(
  691. fn=lambda x: image_from_url_text(x),
  692. _js="extract_image_from_gallery_img2img",
  693. inputs=[result_images],
  694. outputs=[init_img],
  695. )
  696. extras_send_to_inpaint.click(
  697. fn=lambda x: image_from_url_text(x),
  698. _js="extract_image_from_gallery_img2img",
  699. inputs=[result_images],
  700. outputs=[init_img_with_mask],
  701. )
  702. with gr.Blocks(analytics_enabled=False) as pnginfo_interface:
  703. with gr.Row().style(equal_height=False):
  704. with gr.Column(variant='panel'):
  705. image = gr.Image(elem_id="pnginfo_image", label="Source", source="upload", interactive=True, type="pil")
  706. with gr.Column(variant='panel'):
  707. html = gr.HTML()
  708. generation_info = gr.Textbox(visible=False)
  709. html2 = gr.HTML()
  710. with gr.Row():
  711. pnginfo_send_to_txt2img = gr.Button('Send to txt2img')
  712. pnginfo_send_to_img2img = gr.Button('Send to img2img')
  713. image.change(
  714. fn=wrap_gradio_call(modules.extras.run_pnginfo),
  715. inputs=[image],
  716. outputs=[html, generation_info, html2],
  717. )
  718. with gr.Blocks() as modelmerger_interface:
  719. with gr.Row().style(equal_height=False):
  720. with gr.Column(variant='panel'):
  721. gr.HTML(value="<p>A merger of the two checkpoints will be generated in your <b>checkpoint</b> directory.</p>")
  722. with gr.Row():
  723. primary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_primary_model_name", label="Primary Model Name")
  724. secondary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_secondary_model_name", label="Secondary Model Name")
  725. custom_name = gr.Textbox(label="Custom Name (Optional)")
  726. interp_amount = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Interpolation Amount', value=0.3)
  727. interp_method = gr.Radio(choices=["Weighted Sum", "Sigmoid", "Inverse Sigmoid"], value="Weighted Sum", label="Interpolation Method")
  728. save_as_half = gr.Checkbox(value=False, label="Safe as float16")
  729. modelmerger_merge = gr.Button(elem_id="modelmerger_merge", label="Merge", variant='primary')
  730. with gr.Column(variant='panel'):
  731. submit_result = gr.Textbox(elem_id="modelmerger_result", show_label=False)
  732. sd_hijack.model_hijack.embedding_db.load_textual_inversion_embeddings()
  733. with gr.Blocks() as textual_inversion_interface:
  734. with gr.Row().style(equal_height=False):
  735. with gr.Column():
  736. with gr.Group():
  737. gr.HTML(value="<p style='margin-bottom: 0.7em'>Create a new embedding</p>")
  738. new_embedding_name = gr.Textbox(label="Name")
  739. nvpt = gr.Slider(label="Number of vectors per token", minimum=1, maximum=75, step=1, value=1)
  740. with gr.Row():
  741. with gr.Column(scale=3):
  742. gr.HTML(value="")
  743. with gr.Column():
  744. create_embedding = gr.Button(value="Create", variant='primary')
  745. with gr.Group():
  746. gr.HTML(value="<p style='margin-bottom: 0.7em'>Train an embedding; must specify a directory with a set of 512x512 images</p>")
  747. train_embedding_name = gr.Dropdown(label='Embedding', choices=sorted(sd_hijack.model_hijack.embedding_db.word_embeddings.keys()))
  748. learn_rate = gr.Number(label='Learning rate', value=5.0e-03)
  749. dataset_directory = gr.Textbox(label='Dataset directory', placeholder="Path to directory with input images")
  750. log_directory = gr.Textbox(label='Log directory', placeholder="Path to directory where to write outputs", value="textual_inversion")
  751. template_file = gr.Textbox(label='Prompt template file', value=os.path.join(script_path, "textual_inversion_templates", "style_filewords.txt"))
  752. steps = gr.Number(label='Max steps', value=100000, precision=0)
  753. create_image_every = gr.Number(label='Save an image to log directory every N steps, 0 to disable', value=1000, precision=0)
  754. save_embedding_every = gr.Number(label='Save a copy of embedding to log directory every N steps, 0 to disable', value=1000, precision=0)
  755. with gr.Row():
  756. with gr.Column(scale=2):
  757. gr.HTML(value="")
  758. with gr.Column():
  759. with gr.Row():
  760. interrupt_training = gr.Button(value="Interrupt")
  761. train_embedding = gr.Button(value="Train", variant='primary')
  762. with gr.Column():
  763. progressbar = gr.HTML(elem_id="ti_progressbar")
  764. ti_output = gr.Text(elem_id="ti_output", value="", show_label=False)
  765. ti_gallery = gr.Gallery(label='Output', show_label=False, elem_id='ti_gallery').style(grid=4)
  766. ti_preview = gr.Image(elem_id='ti_preview', visible=False)
  767. ti_progress = gr.HTML(elem_id="ti_progress", value="")
  768. ti_outcome = gr.HTML(elem_id="ti_error", value="")
  769. setup_progressbar(progressbar, ti_preview, 'ti', textinfo=ti_progress)
  770. create_embedding.click(
  771. fn=modules.textual_inversion.ui.create_embedding,
  772. inputs=[
  773. new_embedding_name,
  774. nvpt,
  775. ],
  776. outputs=[
  777. train_embedding_name,
  778. ti_output,
  779. ti_outcome,
  780. ]
  781. )
  782. train_embedding.click(
  783. fn=wrap_gradio_gpu_call(modules.textual_inversion.ui.train_embedding, extra_outputs=[gr.update()]),
  784. _js="start_training_textual_inversion",
  785. inputs=[
  786. train_embedding_name,
  787. learn_rate,
  788. dataset_directory,
  789. log_directory,
  790. steps,
  791. create_image_every,
  792. save_embedding_every,
  793. template_file,
  794. ],
  795. outputs=[
  796. ti_output,
  797. ti_outcome,
  798. ]
  799. )
  800. interrupt_training.click(
  801. fn=lambda: shared.state.interrupt(),
  802. inputs=[],
  803. outputs=[],
  804. )
  805. def create_setting_component(key):
  806. def fun():
  807. return opts.data[key] if key in opts.data else opts.data_labels[key].default
  808. info = opts.data_labels[key]
  809. t = type(info.default)
  810. args = info.component_args() if callable(info.component_args) else info.component_args
  811. if info.component is not None:
  812. comp = info.component
  813. elif t == str:
  814. comp = gr.Textbox
  815. elif t == int:
  816. comp = gr.Number
  817. elif t == bool:
  818. comp = gr.Checkbox
  819. else:
  820. raise Exception(f'bad options item type: {str(t)} for key {key}')
  821. return comp(label=info.label, value=fun, **(args or {}))
  822. components = []
  823. component_dict = {}
  824. def open_folder(f):
  825. if not shared.cmd_opts.hide_ui_dir_config:
  826. path = os.path.normpath(f)
  827. if platform.system() == "Windows":
  828. os.startfile(path)
  829. elif platform.system() == "Darwin":
  830. sp.Popen(["open", path])
  831. else:
  832. sp.Popen(["xdg-open", path])
  833. def run_settings(*args):
  834. changed = 0
  835. for key, value, comp in zip(opts.data_labels.keys(), args, components):
  836. if not opts.same_type(value, opts.data_labels[key].default):
  837. return f"Bad value for setting {key}: {value}; expecting {type(opts.data_labels[key].default).__name__}"
  838. for key, value, comp in zip(opts.data_labels.keys(), args, components):
  839. comp_args = opts.data_labels[key].component_args
  840. if comp_args and isinstance(comp_args, dict) and comp_args.get('visible') is False:
  841. continue
  842. oldval = opts.data.get(key, None)
  843. opts.data[key] = value
  844. if oldval != value:
  845. if opts.data_labels[key].onchange is not None:
  846. opts.data_labels[key].onchange()
  847. changed += 1
  848. opts.save(shared.config_filename)
  849. return f'{changed} settings changed.', opts.dumpjson()
  850. with gr.Blocks(analytics_enabled=False) as settings_interface:
  851. settings_submit = gr.Button(value="Apply settings", variant='primary')
  852. result = gr.HTML()
  853. settings_cols = 3
  854. items_per_col = int(len(opts.data_labels) * 0.9 / settings_cols)
  855. cols_displayed = 0
  856. items_displayed = 0
  857. previous_section = None
  858. column = None
  859. with gr.Row(elem_id="settings").style(equal_height=False):
  860. for i, (k, item) in enumerate(opts.data_labels.items()):
  861. if previous_section != item.section:
  862. if cols_displayed < settings_cols and (items_displayed >= items_per_col or previous_section is None):
  863. if column is not None:
  864. column.__exit__()
  865. column = gr.Column(variant='panel')
  866. column.__enter__()
  867. items_displayed = 0
  868. cols_displayed += 1
  869. previous_section = item.section
  870. gr.HTML(elem_id="settings_header_text_{}".format(item.section[0]), value='<h1 class="gr-button-lg">{}</h1>'.format(item.section[1]))
  871. component = create_setting_component(k)
  872. component_dict[k] = component
  873. components.append(component)
  874. items_displayed += 1
  875. request_notifications = gr.Button(value='Request browser notifications', elem_id="request_notifications")
  876. request_notifications.click(
  877. fn=lambda: None,
  878. inputs=[],
  879. outputs=[],
  880. _js='function(){}'
  881. )
  882. if column is not None:
  883. column.__exit__()
  884. interfaces = [
  885. (txt2img_interface, "txt2img", "txt2img"),
  886. (img2img_interface, "img2img", "img2img"),
  887. (extras_interface, "Extras", "extras"),
  888. (pnginfo_interface, "PNG Info", "pnginfo"),
  889. (modelmerger_interface, "Checkpoint Merger", "modelmerger"),
  890. (textual_inversion_interface, "Textual inversion", "ti"),
  891. (settings_interface, "Settings", "settings"),
  892. ]
  893. with open(os.path.join(script_path, "style.css"), "r", encoding="utf8") as file:
  894. css = file.read()
  895. if os.path.exists(os.path.join(script_path, "user.css")):
  896. with open(os.path.join(script_path, "user.css"), "r", encoding="utf8") as file:
  897. usercss = file.read()
  898. css += usercss
  899. if not cmd_opts.no_progressbar_hiding:
  900. css += css_hide_progressbar
  901. with gr.Blocks(css=css, analytics_enabled=False, title="Stable Diffusion") as demo:
  902. with gr.Tabs() as tabs:
  903. for interface, label, ifid in interfaces:
  904. with gr.TabItem(label, id=ifid):
  905. interface.render()
  906. if os.path.exists(os.path.join(script_path, "notification.mp3")):
  907. audio_notification = gr.Audio(interactive=False, value=os.path.join(script_path, "notification.mp3"), elem_id="audio_notification", visible=False)
  908. text_settings = gr.Textbox(elem_id="settings_json", value=lambda: opts.dumpjson(), visible=False)
  909. settings_submit.click(
  910. fn=run_settings,
  911. inputs=components,
  912. outputs=[result, text_settings],
  913. )
  914. def modelmerger(*args):
  915. try:
  916. results = modules.extras.run_modelmerger(*args)
  917. except Exception as e:
  918. print("Error loading/saving model file:", file=sys.stderr)
  919. print(traceback.format_exc(), file=sys.stderr)
  920. modules.sd_models.list_models() # to remove the potentially missing models from the list
  921. 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)]
  922. return results
  923. modelmerger_merge.click(
  924. fn=modelmerger,
  925. inputs=[
  926. primary_model_name,
  927. secondary_model_name,
  928. interp_method,
  929. interp_amount,
  930. save_as_half,
  931. custom_name,
  932. ],
  933. outputs=[
  934. submit_result,
  935. primary_model_name,
  936. secondary_model_name,
  937. component_dict['sd_model_checkpoint'],
  938. ]
  939. )
  940. paste_field_names = ['Prompt', 'Negative prompt', 'Steps', 'Face restoration', 'Seed', 'Size-1', 'Size-2']
  941. txt2img_fields = [field for field,name in txt2img_paste_fields if name in paste_field_names]
  942. img2img_fields = [field for field,name in img2img_paste_fields if name in paste_field_names]
  943. send_to_img2img.click(
  944. fn=lambda img, *args: (image_from_url_text(img),*args),
  945. _js="(gallery, ...args) => [extract_image_from_gallery_img2img(gallery), ...args]",
  946. inputs=[txt2img_gallery] + txt2img_fields,
  947. outputs=[init_img] + img2img_fields,
  948. )
  949. send_to_inpaint.click(
  950. fn=lambda x, *args: (image_from_url_text(x), *args),
  951. _js="(gallery, ...args) => [extract_image_from_gallery_inpaint(gallery), ...args]",
  952. inputs=[txt2img_gallery] + txt2img_fields,
  953. outputs=[init_img_with_mask] + img2img_fields,
  954. )
  955. img2img_send_to_img2img.click(
  956. fn=lambda x: image_from_url_text(x),
  957. _js="extract_image_from_gallery_img2img",
  958. inputs=[img2img_gallery],
  959. outputs=[init_img],
  960. )
  961. img2img_send_to_inpaint.click(
  962. fn=lambda x: image_from_url_text(x),
  963. _js="extract_image_from_gallery_inpaint",
  964. inputs=[img2img_gallery],
  965. outputs=[init_img_with_mask],
  966. )
  967. send_to_extras.click(
  968. fn=lambda x: image_from_url_text(x),
  969. _js="extract_image_from_gallery_extras",
  970. inputs=[txt2img_gallery],
  971. outputs=[extras_image],
  972. )
  973. open_txt2img_folder.click(
  974. fn=lambda: open_folder(opts.outdir_samples or opts.outdir_txt2img_samples),
  975. inputs=[],
  976. outputs=[],
  977. )
  978. open_img2img_folder.click(
  979. fn=lambda: open_folder(opts.outdir_samples or opts.outdir_img2img_samples),
  980. inputs=[],
  981. outputs=[],
  982. )
  983. open_extras_folder.click(
  984. fn=lambda: open_folder(opts.outdir_samples or opts.outdir_extras_samples),
  985. inputs=[],
  986. outputs=[],
  987. )
  988. img2img_send_to_extras.click(
  989. fn=lambda x: image_from_url_text(x),
  990. _js="extract_image_from_gallery_extras",
  991. inputs=[img2img_gallery],
  992. outputs=[extras_image],
  993. )
  994. modules.generation_parameters_copypaste.connect_paste(pnginfo_send_to_txt2img, txt2img_paste_fields, generation_info, 'switch_to_txt2img')
  995. modules.generation_parameters_copypaste.connect_paste(pnginfo_send_to_img2img, img2img_paste_fields, generation_info, 'switch_to_img2img_img2img')
  996. ui_config_file = cmd_opts.ui_config_file
  997. ui_settings = {}
  998. settings_count = len(ui_settings)
  999. error_loading = False
  1000. try:
  1001. if os.path.exists(ui_config_file):
  1002. with open(ui_config_file, "r", encoding="utf8") as file:
  1003. ui_settings = json.load(file)
  1004. except Exception:
  1005. error_loading = True
  1006. print("Error loading settings:", file=sys.stderr)
  1007. print(traceback.format_exc(), file=sys.stderr)
  1008. def loadsave(path, x):
  1009. def apply_field(obj, field, condition=None):
  1010. key = path + "/" + field
  1011. if getattr(obj,'custom_script_source',None) is not None:
  1012. key = 'customscript/' + obj.custom_script_source + '/' + key
  1013. if getattr(obj, 'do_not_save_to_config', False):
  1014. return
  1015. saved_value = ui_settings.get(key, None)
  1016. if saved_value is None:
  1017. ui_settings[key] = getattr(obj, field)
  1018. elif condition is None or condition(saved_value):
  1019. setattr(obj, field, saved_value)
  1020. if type(x) in [gr.Slider, gr.Radio, gr.Checkbox, gr.Textbox, gr.Number] and x.visible:
  1021. apply_field(x, 'visible')
  1022. if type(x) == gr.Slider:
  1023. apply_field(x, 'value')
  1024. apply_field(x, 'minimum')
  1025. apply_field(x, 'maximum')
  1026. apply_field(x, 'step')
  1027. if type(x) == gr.Radio:
  1028. apply_field(x, 'value', lambda val: val in x.choices)
  1029. if type(x) == gr.Checkbox:
  1030. apply_field(x, 'value')
  1031. if type(x) == gr.Textbox:
  1032. apply_field(x, 'value')
  1033. if type(x) == gr.Number:
  1034. apply_field(x, 'value')
  1035. visit(txt2img_interface, loadsave, "txt2img")
  1036. visit(img2img_interface, loadsave, "img2img")
  1037. visit(extras_interface, loadsave, "extras")
  1038. if not error_loading and (not os.path.exists(ui_config_file) or settings_count != len(ui_settings)):
  1039. with open(ui_config_file, "w", encoding="utf8") as file:
  1040. json.dump(ui_settings, file, indent=4)
  1041. return demo
  1042. with open(os.path.join(script_path, "script.js"), "r", encoding="utf8") as jsfile:
  1043. javascript = f'<script>{jsfile.read()}</script>'
  1044. jsdir = os.path.join(script_path, "javascript")
  1045. for filename in sorted(os.listdir(jsdir)):
  1046. with open(os.path.join(jsdir, filename), "r", encoding="utf8") as jsfile:
  1047. javascript += f"\n<script>{jsfile.read()}</script>"
  1048. def template_response(*args, **kwargs):
  1049. res = gradio_routes_templates_response(*args, **kwargs)
  1050. res.body = res.body.replace(b'</head>', f'{javascript}</head>'.encode("utf8"))
  1051. res.init_headers()
  1052. return res
  1053. gradio_routes_templates_response = gradio.routes.templates.TemplateResponse
  1054. gradio.routes.templates.TemplateResponse = template_response