ui_settings.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. import gradio as gr
  2. from modules import ui_common, shared, script_callbacks, scripts, sd_models, sysinfo, timer, shared_items, paths_internal, util
  3. from modules.call_queue import wrap_gradio_call_no_job
  4. from modules.options import options_section
  5. from modules.shared import opts
  6. from modules.ui_components import FormRow
  7. from modules.ui_gradio_extensions import reload_javascript
  8. from concurrent.futures import ThreadPoolExecutor, as_completed
  9. from pathlib import Path
  10. def get_value_for_setting(key):
  11. value = getattr(opts, key)
  12. info = opts.data_labels[key]
  13. args = info.component_args() if callable(info.component_args) else info.component_args or {}
  14. args = {k: v for k, v in args.items() if k not in {'precision'}}
  15. return gr.update(value=value, **args)
  16. def create_setting_component(key, is_quicksettings=False):
  17. def fun():
  18. return opts.data[key] if key in opts.data else opts.data_labels[key].default
  19. info = opts.data_labels[key]
  20. t = type(info.default)
  21. args = info.component_args() if callable(info.component_args) else info.component_args
  22. if info.component is not None:
  23. comp = info.component
  24. elif t == str:
  25. comp = gr.Textbox
  26. elif t == int:
  27. comp = gr.Number
  28. elif t == bool:
  29. comp = gr.Checkbox
  30. else:
  31. raise Exception(f'bad options item type: {t} for key {key}')
  32. elem_id = f"setting_{key}"
  33. if info.refresh is not None:
  34. if is_quicksettings:
  35. res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {}))
  36. ui_common.create_refresh_button(res, info.refresh, info.component_args, f"refresh_{key}")
  37. else:
  38. with FormRow():
  39. res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {}))
  40. ui_common.create_refresh_button(res, info.refresh, info.component_args, f"refresh_{key}")
  41. else:
  42. res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {}))
  43. return res
  44. class UiSettings:
  45. submit = None
  46. result = None
  47. interface = None
  48. components = None
  49. component_dict = None
  50. dummy_component = None
  51. quicksettings_list = None
  52. quicksettings_names = None
  53. text_settings = None
  54. show_all_pages = None
  55. show_one_page = None
  56. search_input = None
  57. def run_settings(self, *args):
  58. changed = []
  59. for key, value, comp in zip(opts.data_labels.keys(), args, self.components):
  60. assert comp == self.dummy_component or opts.same_type(value, opts.data_labels[key].default), f"Bad value for setting {key}: {value}; expecting {type(opts.data_labels[key].default).__name__}"
  61. for key, value, comp in zip(opts.data_labels.keys(), args, self.components):
  62. if comp == self.dummy_component:
  63. continue
  64. if opts.set(key, value):
  65. changed.append(key)
  66. try:
  67. opts.save(shared.config_filename)
  68. except RuntimeError:
  69. return opts.dumpjson(), f'{len(changed)} settings changed without save: {", ".join(changed)}.'
  70. return opts.dumpjson(), f'{len(changed)} settings changed{": " if changed else ""}{", ".join(changed)}.'
  71. def run_settings_single(self, value, key):
  72. if not opts.same_type(value, opts.data_labels[key].default):
  73. return gr.update(visible=True), opts.dumpjson()
  74. if value is None or not opts.set(key, value):
  75. return gr.update(value=getattr(opts, key)), opts.dumpjson()
  76. opts.save(shared.config_filename)
  77. return get_value_for_setting(key), opts.dumpjson()
  78. def register_settings(self):
  79. script_callbacks.ui_settings_callback()
  80. def create_ui(self, loadsave, dummy_component):
  81. self.components = []
  82. self.component_dict = {}
  83. self.dummy_component = dummy_component
  84. shared.settings_components = self.component_dict
  85. # we add this as late as possible so that scripts have already registered their callbacks
  86. opts.data_labels.update(options_section(('callbacks', "Callbacks", "system"), {
  87. **shared_items.callbacks_order_settings(),
  88. }))
  89. opts.reorder()
  90. with gr.Blocks(analytics_enabled=False) as settings_interface:
  91. with gr.Row():
  92. with gr.Column(scale=6):
  93. self.submit = gr.Button(value="Apply settings", variant='primary', elem_id="settings_submit")
  94. with gr.Column():
  95. restart_gradio = gr.Button(value='Reload UI', variant='primary', elem_id="settings_restart_gradio")
  96. self.result = gr.HTML(elem_id="settings_result")
  97. self.quicksettings_names = opts.quicksettings_list
  98. self.quicksettings_names = {x: i for i, x in enumerate(self.quicksettings_names) if x != 'quicksettings'}
  99. self.quicksettings_list = []
  100. previous_section = None
  101. current_tab = None
  102. current_row = None
  103. with gr.Tabs(elem_id="settings"):
  104. for i, (k, item) in enumerate(opts.data_labels.items()):
  105. section_must_be_skipped = item.section[0] is None
  106. if previous_section != item.section and not section_must_be_skipped:
  107. elem_id, text = item.section
  108. if current_tab is not None:
  109. current_row.__exit__()
  110. current_tab.__exit__()
  111. gr.Group()
  112. current_tab = gr.TabItem(elem_id=f"settings_{elem_id}", label=text)
  113. current_tab.__enter__()
  114. current_row = gr.Column(elem_id=f"column_settings_{elem_id}", variant='compact')
  115. current_row.__enter__()
  116. previous_section = item.section
  117. if k in self.quicksettings_names and not shared.cmd_opts.freeze_settings:
  118. self.quicksettings_list.append((i, k, item))
  119. self.components.append(dummy_component)
  120. elif section_must_be_skipped:
  121. self.components.append(dummy_component)
  122. else:
  123. component = create_setting_component(k)
  124. self.component_dict[k] = component
  125. self.components.append(component)
  126. if current_tab is not None:
  127. current_row.__exit__()
  128. current_tab.__exit__()
  129. with gr.TabItem("Defaults", id="defaults", elem_id="settings_tab_defaults"):
  130. loadsave.create_ui()
  131. with gr.TabItem("Sysinfo", id="sysinfo", elem_id="settings_tab_sysinfo"):
  132. download_sysinfo = gr.Button(value='Download system info', elem_id="internal-download-sysinfo", visible=False)
  133. open_sysinfo = gr.Button(value='Open as text in a new page', elem_id="internal-open-sysinfo", visible=False)
  134. sysinfo_html = gr.HTML('''<a class="sysinfo_big_link" onclick="gradioApp().getElementById('internal-download-sysinfo').click();">Download system info</a><br/><a onclick="gradioApp().getElementById('internal-open-sysinfo').click();">(or open as text in a new page)</a>''', elem_id="sysinfo_download")
  135. sysinfo_textbox = gr.Textbox(label='Sysinfo textarea', elem_id="internal-sysinfo-textbox", visible=False)
  136. def create_sysinfo():
  137. sysinfo_str = sysinfo.get()
  138. if len(sysinfo_utf8 := sysinfo_str.encode('utf8')) > 2 ** 20: # 1MB
  139. sysinfo_path = Path(paths_internal.script_path) / 'tmp' / 'sysinfo.json'
  140. sysinfo_path.parent.mkdir(parents=True, exist_ok=True)
  141. sysinfo_path.write_bytes(sysinfo_utf8)
  142. return gr.update(), gr.update(value=f'file={util.truncate_path(sysinfo_path)}')
  143. return gr.update(), gr.update(value=sysinfo_str)
  144. download_sysinfo.click(
  145. fn=create_sysinfo, outputs=[sysinfo_html, sysinfo_textbox], show_progress=True).success(
  146. fn=None, _js='downloadSysinfo'
  147. )
  148. open_sysinfo.click(
  149. fn=create_sysinfo, outputs=[sysinfo_html, sysinfo_textbox], show_progress=True).success(
  150. fn=None, _js='openTabSysinfo'
  151. )
  152. with gr.Row():
  153. with gr.Column(scale=1):
  154. sysinfo_check_file = gr.File(label="Check system info for validity", type='binary')
  155. with gr.Column(scale=1):
  156. sysinfo_check_output = gr.HTML("", elem_id="sysinfo_validity")
  157. with gr.Column(scale=100):
  158. pass
  159. with gr.TabItem("Actions", id="actions", elem_id="settings_tab_actions"):
  160. request_notifications = gr.Button(value='Request browser notifications', elem_id="request_notifications")
  161. download_localization = gr.Button(value='Download localization template', elem_id="download_localization")
  162. reload_script_bodies = gr.Button(value='Reload custom script bodies (No ui updates, No restart)', variant='secondary', elem_id="settings_reload_script_bodies")
  163. with gr.Row():
  164. unload_sd_model = gr.Button(value='Unload SD checkpoint to RAM', elem_id="sett_unload_sd_model")
  165. reload_sd_model = gr.Button(value='Load SD checkpoint to VRAM from RAM', elem_id="sett_reload_sd_model")
  166. with gr.Row():
  167. calculate_all_checkpoint_hash = gr.Button(value='Calculate hash for all checkpoint', elem_id="calculate_all_checkpoint_hash")
  168. calculate_all_checkpoint_hash_threads = gr.Number(value=1, label="Number of parallel calculations", elem_id="calculate_all_checkpoint_hash_threads", precision=0, minimum=1)
  169. with gr.TabItem("Licenses", id="licenses", elem_id="settings_tab_licenses"):
  170. gr.HTML(shared.html("licenses.html"), elem_id="licenses")
  171. self.show_all_pages = gr.Button(value="Show all pages", elem_id="settings_show_all_pages")
  172. self.show_one_page = gr.Button(value="Show only one page", elem_id="settings_show_one_page", visible=False)
  173. self.show_one_page.click(lambda: None)
  174. self.search_input = gr.Textbox(value="", elem_id="settings_search", max_lines=1, placeholder="Search...", show_label=False)
  175. self.text_settings = gr.Textbox(elem_id="settings_json", value=lambda: opts.dumpjson(), visible=False)
  176. def call_func_and_return_text(func, text):
  177. def handler():
  178. t = timer.Timer()
  179. func()
  180. t.record(text)
  181. return f'{text} in {t.total:.1f}s'
  182. return handler
  183. unload_sd_model.click(
  184. fn=call_func_and_return_text(sd_models.unload_model_weights, 'Unloaded the checkpoint'),
  185. inputs=[],
  186. outputs=[self.result]
  187. )
  188. reload_sd_model.click(
  189. fn=call_func_and_return_text(lambda: sd_models.send_model_to_device(shared.sd_model), 'Loaded the checkpoint'),
  190. inputs=[],
  191. outputs=[self.result]
  192. )
  193. request_notifications.click(
  194. fn=lambda: None,
  195. inputs=[],
  196. outputs=[],
  197. _js='function(){}'
  198. )
  199. download_localization.click(
  200. fn=lambda: None,
  201. inputs=[],
  202. outputs=[],
  203. _js='download_localization'
  204. )
  205. def reload_scripts():
  206. scripts.reload_script_body_only()
  207. reload_javascript() # need to refresh the html page
  208. reload_script_bodies.click(
  209. fn=reload_scripts,
  210. inputs=[],
  211. outputs=[]
  212. )
  213. restart_gradio.click(
  214. fn=shared.state.request_restart,
  215. _js='restart_reload',
  216. inputs=[],
  217. outputs=[],
  218. )
  219. def check_file(x):
  220. if x is None:
  221. return ''
  222. if sysinfo.check(x.decode('utf8', errors='ignore')):
  223. return 'Valid'
  224. return 'Invalid'
  225. sysinfo_check_file.change(
  226. fn=check_file,
  227. inputs=[sysinfo_check_file],
  228. outputs=[sysinfo_check_output],
  229. )
  230. def calculate_all_checkpoint_hash_fn(max_thread):
  231. checkpoints_list = sd_models.checkpoints_list.values()
  232. with ThreadPoolExecutor(max_workers=max_thread) as executor:
  233. futures = [executor.submit(checkpoint.calculate_shorthash) for checkpoint in checkpoints_list]
  234. completed = 0
  235. for _ in as_completed(futures):
  236. completed += 1
  237. print(f"{completed} / {len(checkpoints_list)} ")
  238. print("Finish calculating hash for all checkpoints")
  239. calculate_all_checkpoint_hash.click(
  240. fn=calculate_all_checkpoint_hash_fn,
  241. inputs=[calculate_all_checkpoint_hash_threads],
  242. )
  243. self.interface = settings_interface
  244. def add_quicksettings(self):
  245. with gr.Row(elem_id="quicksettings", variant="compact"):
  246. for _i, k, _item in sorted(self.quicksettings_list, key=lambda x: self.quicksettings_names.get(x[1], x[0])):
  247. component = create_setting_component(k, is_quicksettings=True)
  248. self.component_dict[k] = component
  249. def add_functionality(self, demo):
  250. self.submit.click(
  251. fn=wrap_gradio_call_no_job(lambda *args: self.run_settings(*args), extra_outputs=[gr.update()]),
  252. inputs=self.components,
  253. outputs=[self.text_settings, self.result],
  254. )
  255. for _i, k, _item in self.quicksettings_list:
  256. component = self.component_dict[k]
  257. info = opts.data_labels[k]
  258. if isinstance(component, gr.Textbox):
  259. methods = [component.submit, component.blur]
  260. elif hasattr(component, 'release'):
  261. methods = [component.release]
  262. else:
  263. methods = [component.change]
  264. for method in methods:
  265. method(
  266. fn=lambda value, key=k: self.run_settings_single(value, key=key),
  267. inputs=[component],
  268. outputs=[component, self.text_settings],
  269. show_progress=info.refresh is not None,
  270. )
  271. button_set_checkpoint = gr.Button('Change checkpoint', elem_id='change_checkpoint', visible=False)
  272. button_set_checkpoint.click(
  273. fn=lambda value, _: self.run_settings_single(value, key='sd_model_checkpoint'),
  274. _js="function(v){ var res = desiredCheckpointName; desiredCheckpointName = ''; return [res || v, null]; }",
  275. inputs=[self.component_dict['sd_model_checkpoint'], self.dummy_component],
  276. outputs=[self.component_dict['sd_model_checkpoint'], self.text_settings],
  277. )
  278. component_keys = [k for k in opts.data_labels.keys() if k in self.component_dict]
  279. def get_settings_values():
  280. return [get_value_for_setting(key) for key in component_keys]
  281. demo.load(
  282. fn=get_settings_values,
  283. inputs=[],
  284. outputs=[self.component_dict[k] for k in component_keys],
  285. queue=False,
  286. )
  287. def search(self, text):
  288. print(text)
  289. return [gr.update(visible=text in (comp.label or "")) for comp in self.components]