ui_common.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. import json
  2. import html
  3. import os
  4. import platform
  5. import sys
  6. import gradio as gr
  7. import subprocess as sp
  8. from modules import call_queue, shared
  9. from modules.generation_parameters_copypaste import image_from_url_text
  10. import modules.images
  11. from modules.ui_components import ToolButton
  12. folder_symbol = '\U0001f4c2' # 📂
  13. refresh_symbol = '\U0001f504' # 🔄
  14. def update_generation_info(generation_info, html_info, img_index):
  15. try:
  16. generation_info = json.loads(generation_info)
  17. if img_index < 0 or img_index >= len(generation_info["infotexts"]):
  18. return html_info, gr.update()
  19. return plaintext_to_html(generation_info["infotexts"][img_index]), gr.update()
  20. except Exception:
  21. pass
  22. # if the json parse or anything else fails, just return the old html_info
  23. return html_info, gr.update()
  24. def plaintext_to_html(text, classname=None):
  25. content = "<br>\n".join(html.escape(x) for x in text.split('\n'))
  26. return f"<p class='{classname}'>{content}</p>" if classname else f"<p>{content}</p>"
  27. def save_files(js_data, images, do_make_zip, index):
  28. import csv
  29. filenames = []
  30. fullfns = []
  31. #quick dictionary to class object conversion. Its necessary due apply_filename_pattern requiring it
  32. class MyObject:
  33. def __init__(self, d=None):
  34. if d is not None:
  35. for key, value in d.items():
  36. setattr(self, key, value)
  37. data = json.loads(js_data)
  38. p = MyObject(data)
  39. path = shared.opts.outdir_save
  40. save_to_dirs = shared.opts.use_save_to_dirs_for_ui
  41. extension: str = shared.opts.samples_format
  42. start_index = 0
  43. only_one = False
  44. if index > -1 and shared.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
  45. only_one = True
  46. images = [images[index]]
  47. start_index = index
  48. os.makedirs(shared.opts.outdir_save, exist_ok=True)
  49. with open(os.path.join(shared.opts.outdir_save, "log.csv"), "a", encoding="utf8", newline='') as file:
  50. at_start = file.tell() == 0
  51. writer = csv.writer(file)
  52. if at_start:
  53. writer.writerow(["prompt", "seed", "width", "height", "sampler", "cfgs", "steps", "filename", "negative_prompt"])
  54. for image_index, filedata in enumerate(images, start_index):
  55. image = image_from_url_text(filedata)
  56. is_grid = image_index < p.index_of_first_image
  57. i = 0 if is_grid else (image_index - p.index_of_first_image)
  58. p.batch_index = image_index-1
  59. fullfn, txt_fullfn = modules.images.save_image(image, path, "", seed=p.all_seeds[i], prompt=p.all_prompts[i], extension=extension, info=p.infotexts[image_index], grid=is_grid, p=p, save_to_dirs=save_to_dirs)
  60. filename = os.path.relpath(fullfn, path)
  61. filenames.append(filename)
  62. fullfns.append(fullfn)
  63. if txt_fullfn:
  64. filenames.append(os.path.basename(txt_fullfn))
  65. fullfns.append(txt_fullfn)
  66. writer.writerow([data["prompt"], data["seed"], data["width"], data["height"], data["sampler_name"], data["cfg_scale"], data["steps"], filenames[0], data["negative_prompt"]])
  67. # Make Zip
  68. if do_make_zip:
  69. zip_fileseed = p.all_seeds[index-1] if only_one else p.all_seeds[0]
  70. namegen = modules.images.FilenameGenerator(p, zip_fileseed, p.all_prompts[0], image, True)
  71. zip_filename = namegen.apply(shared.opts.grid_zip_filename_pattern or "[datetime]_[[model_name]]_[seed]-[seed_last]")
  72. zip_filepath = os.path.join(path, f"{zip_filename}.zip")
  73. from zipfile import ZipFile
  74. with ZipFile(zip_filepath, "w") as zip_file:
  75. for i in range(len(fullfns)):
  76. with open(fullfns[i], mode="rb") as f:
  77. zip_file.writestr(filenames[i], f.read())
  78. fullfns.insert(0, zip_filepath)
  79. return gr.File.update(value=fullfns, visible=True), plaintext_to_html(f"Saved: {filenames[0]}")
  80. def create_output_panel(tabname, outdir):
  81. from modules import shared
  82. import modules.generation_parameters_copypaste as parameters_copypaste
  83. def open_folder(f):
  84. if not os.path.exists(f):
  85. print(f'Folder "{f}" does not exist. After you create an image, the folder will be created.')
  86. return
  87. elif not os.path.isdir(f):
  88. print(f"""
  89. WARNING
  90. An open_folder request was made with an argument that is not a folder.
  91. This could be an error or a malicious attempt to run code on your computer.
  92. Requested path was: {f}
  93. """, file=sys.stderr)
  94. return
  95. if not shared.cmd_opts.hide_ui_dir_config:
  96. path = os.path.normpath(f)
  97. if platform.system() == "Windows":
  98. os.startfile(path)
  99. elif platform.system() == "Darwin":
  100. sp.Popen(["open", path])
  101. elif "microsoft-standard-WSL2" in platform.uname().release:
  102. sp.Popen(["wsl-open", path])
  103. else:
  104. sp.Popen(["xdg-open", path])
  105. with gr.Column(variant='panel', elem_id=f"{tabname}_results"):
  106. with gr.Group(elem_id=f"{tabname}_gallery_container"):
  107. result_gallery = gr.Gallery(label='Output', show_label=False, elem_id=f"{tabname}_gallery").style(columns=4)
  108. generation_info = None
  109. with gr.Column():
  110. with gr.Row(elem_id=f"image_buttons_{tabname}", elem_classes="image-buttons"):
  111. open_folder_button = gr.Button(folder_symbol, visible=not shared.cmd_opts.hide_ui_dir_config)
  112. if tabname != "extras":
  113. save = gr.Button('Save', elem_id=f'save_{tabname}')
  114. save_zip = gr.Button('Zip', elem_id=f'save_zip_{tabname}')
  115. buttons = parameters_copypaste.create_buttons(["img2img", "inpaint", "extras"])
  116. open_folder_button.click(
  117. fn=lambda: open_folder(shared.opts.outdir_samples or outdir),
  118. inputs=[],
  119. outputs=[],
  120. )
  121. if tabname != "extras":
  122. download_files = gr.File(None, file_count="multiple", interactive=False, show_label=False, visible=False, elem_id=f'download_files_{tabname}')
  123. with gr.Group():
  124. html_info = gr.HTML(elem_id=f'html_info_{tabname}', elem_classes="infotext")
  125. html_log = gr.HTML(elem_id=f'html_log_{tabname}', elem_classes="html-log")
  126. generation_info = gr.Textbox(visible=False, elem_id=f'generation_info_{tabname}')
  127. if tabname == 'txt2img' or tabname == 'img2img':
  128. generation_info_button = gr.Button(visible=False, elem_id=f"{tabname}_generation_info_button")
  129. generation_info_button.click(
  130. fn=update_generation_info,
  131. _js="function(x, y, z){ return [x, y, selected_gallery_index()] }",
  132. inputs=[generation_info, html_info, html_info],
  133. outputs=[html_info, html_info],
  134. show_progress=False,
  135. )
  136. save.click(
  137. fn=call_queue.wrap_gradio_call(save_files),
  138. _js="(x, y, z, w) => [x, y, false, selected_gallery_index()]",
  139. inputs=[
  140. generation_info,
  141. result_gallery,
  142. html_info,
  143. html_info,
  144. ],
  145. outputs=[
  146. download_files,
  147. html_log,
  148. ],
  149. show_progress=False,
  150. )
  151. save_zip.click(
  152. fn=call_queue.wrap_gradio_call(save_files),
  153. _js="(x, y, z, w) => [x, y, true, selected_gallery_index()]",
  154. inputs=[
  155. generation_info,
  156. result_gallery,
  157. html_info,
  158. html_info,
  159. ],
  160. outputs=[
  161. download_files,
  162. html_log,
  163. ]
  164. )
  165. else:
  166. html_info_x = gr.HTML(elem_id=f'html_info_x_{tabname}')
  167. html_info = gr.HTML(elem_id=f'html_info_{tabname}', elem_classes="infotext")
  168. html_log = gr.HTML(elem_id=f'html_log_{tabname}')
  169. paste_field_names = []
  170. if tabname == "txt2img":
  171. paste_field_names = modules.scripts.scripts_txt2img.paste_field_names
  172. elif tabname == "img2img":
  173. paste_field_names = modules.scripts.scripts_img2img.paste_field_names
  174. for paste_tabname, paste_button in buttons.items():
  175. parameters_copypaste.register_paste_params_button(parameters_copypaste.ParamBinding(
  176. paste_button=paste_button, tabname=paste_tabname, source_tabname="txt2img" if tabname == "txt2img" else None, source_image_component=result_gallery,
  177. paste_field_names=paste_field_names
  178. ))
  179. return result_gallery, generation_info if tabname != "extras" else html_info_x, html_info, html_log
  180. def create_refresh_button(refresh_component, refresh_method, refreshed_args, elem_id):
  181. def refresh():
  182. refresh_method()
  183. args = refreshed_args() if callable(refreshed_args) else refreshed_args
  184. for k, v in args.items():
  185. setattr(refresh_component, k, v)
  186. return gr.update(**(args or {}))
  187. refresh_button = ToolButton(value=refresh_symbol, elem_id=elem_id)
  188. refresh_button.click(
  189. fn=refresh,
  190. inputs=[],
  191. outputs=[refresh_component]
  192. )
  193. return refresh_button