From 08f594a54bc33e6bac642229f56022b4e151554c Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Wed, 13 Sep 2023 11:26:28 -0400 Subject: [PATCH] fix missing paths --- javascript/style.css | 3 +++ javascript/ui.js | 6 +++++ modules/paths.py | 2 +- modules/shared.py | 61 ++++++++++++++++++++++---------------------- modules/ui.py | 8 ++++-- modules/ui_common.py | 14 ++++++++++ 6 files changed, 61 insertions(+), 33 deletions(-) diff --git a/javascript/style.css b/javascript/style.css index 396c2d5ed..d5778e9ed 100644 --- a/javascript/style.css +++ b/javascript/style.css @@ -276,6 +276,9 @@ div.controlnet_main_options { display: grid; grid-template-columns: 1fr 1fr; gri .log-monitor { display: none; justify-content: unset !important; overflow: hidden; padding: 0; margin-top: auto; font-family: monospace; font-size: 0.85em; } .log-monitor td, .log-monitor th { padding-left: 1em; } +/* custom component */ +.folder-selector textarea { height: 2em !important; padding: 6px !important; } + /* Workaround for Gradio dropdowns capturing clicks during and after fadeout */ .gradio-dropdown > label > div > div:first-child:not(.showOptions) ~ ul.options { pointer-events: none; } diff --git a/javascript/ui.js b/javascript/ui.js index 47575c695..232bb5eb1 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -378,6 +378,12 @@ function previewTheme() { }); } +async function browseFolder() { + const f = await window.showDirectoryPicker(); + if (f && f.kind === 'directory') return f.name; + return null; +} + async function reconnectUI() { const gallery = gradioApp().getElementById('txt2img_gallery'); if (!gallery) return; diff --git a/modules/paths.py b/modules/paths.py index f4b991f5c..18dea2fb1 100644 --- a/modules/paths.py +++ b/modules/paths.py @@ -68,7 +68,7 @@ def create_paths(opts, log=None): fullpath = os.path.join(data_path, tgt) relpath = os.path.relpath(fullpath, script_path) opts.data[folder] = relpath - return + return relpath create_path(data_path) create_path(script_path) diff --git a/modules/shared.py b/modules/shared.py index f8a2f5d02..d98bbf448 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -203,7 +203,7 @@ else: class OptionInfo: - def __init__(self, default=None, label="", component=None, component_args=None, onchange=None, section=None, refresh=None, submit=None, comment_before='', comment_after=''): + def __init__(self, default=None, label="", component=None, component_args=None, onchange=None, section=None, refresh=None, folder=None, submit=None, comment_before='', comment_after=''): self.default = default self.label = label self.component = component @@ -211,6 +211,7 @@ class OptionInfo: self.onchange = onchange self.section = section self.refresh = refresh + self.folder = folder self.comment_before = comment_before # HTML text that will be added after label in UI self.comment_after = comment_after # HTML text that will be added before label in UI self.submit = submit @@ -441,26 +442,26 @@ options_templates.update(options_section(('diffusers', "Diffusers Settings"), { })) options_templates.update(options_section(('system-paths', "System Paths"), { - "temp_dir": OptionInfo("", "Directory for temporary images; leave empty for default"), + "temp_dir": OptionInfo("", "Directory for temporary images; leave empty for default", folder=True), "clean_temp_dir_at_start": OptionInfo(True, "Cleanup non-default temporary directory when starting webui"), - "ckpt_dir": OptionInfo(os.path.join(paths.models_path, 'Stable-diffusion'), "Folder with stable diffusion models"), - "diffusers_dir": OptionInfo(os.path.join(paths.models_path, 'Diffusers'), "Folder with Hugggingface models"), - "vae_dir": OptionInfo(os.path.join(paths.models_path, 'VAE'), "Folder with VAE files"), + "ckpt_dir": OptionInfo(os.path.join(paths.models_path, 'Stable-diffusion'), "Folder with stable diffusion models", folder=True), + "diffusers_dir": OptionInfo(os.path.join(paths.models_path, 'Diffusers'), "Folder with Hugggingface models", folder=True), + "vae_dir": OptionInfo(os.path.join(paths.models_path, 'VAE'), "Folder with VAE files", folder=True), "sd_lora": OptionInfo("", "Add LoRA to prompt", gr.Textbox, {"visible": False}), - "lora_dir": OptionInfo(os.path.join(paths.models_path, 'Lora'), "Folder with LoRA network(s)"), - "lyco_dir": OptionInfo(os.path.join(paths.models_path, 'LyCORIS'), "Folder with LyCORIS network(s)"), - "styles_dir": OptionInfo(os.path.join(paths.data_path, 'styles.csv'), "File or Folder with user-defined styles"), - "embeddings_dir": OptionInfo(os.path.join(paths.models_path, 'embeddings'), "Folder with textual inversion embeddings"), - "hypernetwork_dir": OptionInfo(os.path.join(paths.models_path, 'hypernetworks'), "Folder with Hypernetwork models"), - "codeformer_models_path": OptionInfo(os.path.join(paths.models_path, 'Codeformer'), "Folder with codeformer models"), - "gfpgan_models_path": OptionInfo(os.path.join(paths.models_path, 'GFPGAN'), "Folder with GFPGAN models"), - "esrgan_models_path": OptionInfo(os.path.join(paths.models_path, 'ESRGAN'), "Folder with ESRGAN models"), - "bsrgan_models_path": OptionInfo(os.path.join(paths.models_path, 'BSRGAN'), "Folder with BSRGAN models"), - "realesrgan_models_path": OptionInfo(os.path.join(paths.models_path, 'RealESRGAN'), "Folder with RealESRGAN models"), - "scunet_models_path": OptionInfo(os.path.join(paths.models_path, 'ScuNET'), "Folder with ScuNET models"), - "swinir_models_path": OptionInfo(os.path.join(paths.models_path, 'SwinIR'), "Folder with SwinIR models"), - "ldsr_models_path": OptionInfo(os.path.join(paths.models_path, 'LDSR'), "Folder with LDSR models"), - "clip_models_path": OptionInfo(os.path.join(paths.models_path, 'CLIP'), "Folder with CLIP models"), + "lora_dir": OptionInfo(os.path.join(paths.models_path, 'Lora'), "Folder with LoRA network(s)", folder=True), + "lyco_dir": OptionInfo(os.path.join(paths.models_path, 'LyCORIS'), "Folder with LyCORIS network(s)", folder=True), + "styles_dir": OptionInfo(os.path.join(paths.data_path, 'styles.csv'), "File or Folder with user-defined styles", folder=True), + "embeddings_dir": OptionInfo(os.path.join(paths.models_path, 'embeddings'), "Folder with textual inversion embeddings", folder=True), + "hypernetwork_dir": OptionInfo(os.path.join(paths.models_path, 'hypernetworks'), "Folder with Hypernetwork models", folder=True), + "codeformer_models_path": OptionInfo(os.path.join(paths.models_path, 'Codeformer'), "Folder with codeformer models", folder=True), + "gfpgan_models_path": OptionInfo(os.path.join(paths.models_path, 'GFPGAN'), "Folder with GFPGAN models", folder=True), + "esrgan_models_path": OptionInfo(os.path.join(paths.models_path, 'ESRGAN'), "Folder with ESRGAN models", folder=True), + "bsrgan_models_path": OptionInfo(os.path.join(paths.models_path, 'BSRGAN'), "Folder with BSRGAN models", folder=True), + "realesrgan_models_path": OptionInfo(os.path.join(paths.models_path, 'RealESRGAN'), "Folder with RealESRGAN models", folder=True), + "scunet_models_path": OptionInfo(os.path.join(paths.models_path, 'ScuNET'), "Folder with ScuNET models", folder=True), + "swinir_models_path": OptionInfo(os.path.join(paths.models_path, 'SwinIR'), "Folder with SwinIR models", folder=True), + "ldsr_models_path": OptionInfo(os.path.join(paths.models_path, 'LDSR'), "Folder with LDSR models", folder=True), + "clip_models_path": OptionInfo(os.path.join(paths.models_path, 'CLIP'), "Folder with CLIP models", folder=True), })) options_templates.update(options_section(('saving-images', "Image Options"), { @@ -506,19 +507,19 @@ options_templates.update(options_section(('saving-paths', "Image Naming & Paths" "use_save_to_dirs_for_ui": OptionInfo(False, "Save images to a subdirectory when using Save button"), "directories_filename_pattern": OptionInfo("[date]", "Directory name pattern", component_args=hide_dirs), "directories_max_prompt_words": OptionInfo(8, "Max prompt words for [prompt_words] pattern", gr.Slider, {"minimum": 1, "maximum": 99, "step": 1, **hide_dirs}), - "outdir_samples": OptionInfo("", "Output directory for images", component_args=hide_dirs), - "outdir_txt2img_samples": OptionInfo("outputs/text", 'Output directory for txt2img images', component_args=hide_dirs), - "outdir_img2img_samples": OptionInfo("outputs/image", 'Output directory for img2img images', component_args=hide_dirs), - "outdir_extras_samples": OptionInfo("outputs/extras", 'Output directory for images from extras tab', component_args=hide_dirs), - "outdir_save": OptionInfo("outputs/save", "Directory for saving images using the Save button", component_args=hide_dirs), - "outdir_init_images": OptionInfo("outputs/init-images", "Directory for saving init images when using img2img", component_args=hide_dirs), + "outdir_samples": OptionInfo("", "Output directory for images", component_args=hide_dirs, folder=True), + "outdir_txt2img_samples": OptionInfo("outputs/text", 'Output directory for txt2img images', component_args=hide_dirs, folder=True), + "outdir_img2img_samples": OptionInfo("outputs/image", 'Output directory for img2img images', component_args=hide_dirs, folder=True), + "outdir_extras_samples": OptionInfo("outputs/extras", 'Output directory for images from extras tab', component_args=hide_dirs, folder=True), + "outdir_save": OptionInfo("outputs/save", "Directory for saving images using the Save button", component_args=hide_dirs, folder=True), + "outdir_init_images": OptionInfo("outputs/init-images", "Directory for saving init images when using img2img", component_args=hide_dirs, folder=True), "outdir_sep_grids": OptionInfo("

Grids

", "", gr.HTML), "grid_extended_filename": OptionInfo(True, "Add extended info (seed, prompt) to filename when saving grid"), "grid_save_to_dirs": OptionInfo(False, "Save grids to a subdirectory"), - "outdir_grids": OptionInfo("", "Output directory for grids", component_args=hide_dirs), - "outdir_txt2img_grids": OptionInfo("outputs/grids", 'Output directory for txt2img grids', component_args=hide_dirs), - "outdir_img2img_grids": OptionInfo("outputs/grids", 'Output directory for img2img grids', component_args=hide_dirs), + "outdir_grids": OptionInfo("", "Output directory for grids", component_args=hide_dirs, folder=True), + "outdir_txt2img_grids": OptionInfo("outputs/grids", 'Output directory for txt2img grids', component_args=hide_dirs, folder=True), + "outdir_img2img_grids": OptionInfo("outputs/grids", 'Output directory for img2img grids', component_args=hide_dirs, folder=True), })) @@ -545,7 +546,7 @@ options_templates.update(options_section(('live-preview', "Live Previews"), { "live_previews_enable": OptionInfo(True, "Show live previews of the created image"), "show_progress_grid": OptionInfo(True, "Show previews of all images generated in a batch as a grid"), "notification_audio_enable": OptionInfo(False, "Play a sound when images are finished generating"), - "notification_audio_path": OptionInfo("html/notification.mp3","Path to notification sound", component_args=hide_dirs), + "notification_audio_path": OptionInfo("html/notification.mp3","Path to notification sound", component_args=hide_dirs, folder=True), "show_progress_every_n_steps": OptionInfo(1, "Live preview display period", gr.Slider, {"minimum": -1, "maximum": 32, "step": 1}), "show_progress_type": OptionInfo("Approximate NN", "Live preview method", gr.Radio, {"choices": ["Full VAE", "Approximate NN", "Approximate simple", "TAESD"]}), "live_preview_content": OptionInfo("Combined", "Live preview subject", gr.Radio, {"choices": ["Combined", "Prompt", "Negative prompt"]}), @@ -620,7 +621,7 @@ options_templates.update(options_section(('training', "Training"), { "save_training_settings_to_txt": OptionInfo(True, "Save training settings to a text file on training start"), "dataset_filename_word_regex": OptionInfo("", "Filename word regex"), "dataset_filename_join_string": OptionInfo(" ", "Filename join string"), - "embeddings_templates_dir": OptionInfo(os.path.join(paths.script_path, 'train', 'templates'), "Embeddings train templates directory"), + "embeddings_templates_dir": OptionInfo(os.path.join(paths.script_path, 'train', 'templates'), "Embeddings train templates directory", folder=True), "training_image_repeats_per_epoch": OptionInfo(1, "Number of repeats for a single input image per epoch", gr.Number, {"precision": 0}), "training_write_csv_every": OptionInfo(0, "Save CSV file containing the loss to log directory"), "training_enable_tensorboard": OptionInfo(False, "Enable tensorboard logging"), diff --git a/modules/ui.py b/modules/ui.py index 34d777990..d4c3ec908 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -929,11 +929,15 @@ def create_ui(startup_timer = None): if info.refresh is not None: if is_quicksettings: res = comp(label=info.label, value=fun(), elem_id=elem_id, **args) - create_refresh_button(res, info.refresh, info.component_args, f"refresh_{key}") + ui_common.create_refresh_button(res, info.refresh, info.component_args, f"refresh_{key}") else: with FormRow(): res = comp(label=info.label, value=fun(), elem_id=elem_id, **args) - create_refresh_button(res, info.refresh, info.component_args, f"refresh_{key}") + ui_common.create_refresh_button(res, info.refresh, info.component_args, f"refresh_{key}") + elif info.folder is not None: + with FormRow(): + res = comp(label=info.label, value=fun(), elem_id=elem_id, elem_classes="folder-selector", **args) + ui_common.create_browse_button(res, f"folder_{key}") else: try: res = comp(label=info.label, value=fun(), elem_id=elem_id, **args) diff --git a/modules/ui_common.py b/modules/ui_common.py index dd4d2225e..b4f31734c 100644 --- a/modules/ui_common.py +++ b/modules/ui_common.py @@ -229,3 +229,17 @@ def create_refresh_button(refresh_component, refresh_method, refreshed_args, ele refresh_button = ToolButton(value=symbols.refresh, elem_id=elem_id) refresh_button.click(fn=refresh, inputs=[], outputs=[refresh_component]) return refresh_button + +def create_browse_button(browse_component, elem_id): + + def browse(folder): + # import subprocess + if folder is not None: + return gr.update(value = folder) + return gr.update() + + from modules.ui_components import ToolButton + browse_button = ToolButton(value=symbols.folder, elem_id=elem_id) + browse_button.click(fn=browse, _js="async () => await browseFolder()", inputs=[browse_component], outputs=[browse_component]) + # browse_button.click(fn=browse, inputs=[browse_component], outputs=[browse_component]) + return browse_button