mirror of https://github.com/bmaltais/kohya_ss
feat: Remember last used folder for file dialogs (#3290)
* feat: Remember last used folder for file dialogs
This commit introduces a feature to remember the last used folder for various file and folder dialogs within the GUI.
Key changes:
- Modified `KohyaSSGUIConfig` (`kohya_gui/class_gui_config.py`) to store and retrieve a `last_used_folder` value in the `config.toml` file.
- Updated file/folder dialog utility functions in `kohya_gui/common_gui.py` (e.g., `get_folder_path`, `get_file_path`, `get_saveasfilename_path`) to:
- Accept the `KohyaSSGUIConfig` instance.
- Use the stored `last_used_folder` as the initial directory for dialogs.
- Update `last_used_folder` after a successful selection.
- Updated various GUI modules (`class_folders.py`, `wd14_caption_gui.py`, and other captioning utilities) to pass the `KohyaSSGUIConfig` instance to these dialog functions.
This enhancement improves your experience by defaulting file dialogs to the most recently accessed relevant directory, streamlining the workflow for you when you frequently work with specific folders.
* Fix typo
* Fix typos
---------
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
pull/3293/head
parent
060c58a319
commit
51d87f5309
|
|
@ -8,6 +8,7 @@ from .common_gui import (
|
|||
list_dirs,
|
||||
setup_environment,
|
||||
)
|
||||
from .class_gui_config import KohyaSSGUIConfig # Added import
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
|
@ -121,7 +122,7 @@ def caption_images(
|
|||
|
||||
|
||||
# Gradio UI
|
||||
def gradio_basic_caption_gui_tab(headless=False, default_images_dir=None):
|
||||
def gradio_basic_caption_gui_tab(headless=False, default_images_dir=None, config: KohyaSSGUIConfig = {}): # Added config
|
||||
"""
|
||||
Creates a Gradio tab for basic image captioning.
|
||||
|
||||
|
|
@ -191,7 +192,7 @@ def gradio_basic_caption_gui_tab(headless=False, default_images_dir=None):
|
|||
)
|
||||
# Event handler for button click
|
||||
folder_button.click(
|
||||
get_folder_path,
|
||||
lambda: get_folder_path(config=config), # Added config
|
||||
outputs=images_dir,
|
||||
show_progress=False,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import os
|
|||
|
||||
from .common_gui import get_folder_path, scriptdir, list_dirs
|
||||
from .custom_logging import setup_logging
|
||||
from .class_gui_config import KohyaSSGUIConfig # Added import
|
||||
|
||||
# Set up logging
|
||||
log = setup_logging()
|
||||
|
|
@ -208,7 +209,7 @@ def caption_images_nucleus(
|
|||
)
|
||||
|
||||
|
||||
def gradio_blip2_caption_gui_tab(headless=False, directory_path=None):
|
||||
def gradio_blip2_caption_gui_tab(headless=False, directory_path=None, config: KohyaSSGUIConfig = {}): # Added config
|
||||
from .common_gui import create_refresh_button
|
||||
|
||||
directory_path = (
|
||||
|
|
@ -249,7 +250,7 @@ def gradio_blip2_caption_gui_tab(headless=False, directory_path=None):
|
|||
visible=(not headless),
|
||||
)
|
||||
button_directory_path_dir_input.click(
|
||||
get_folder_path,
|
||||
lambda: get_folder_path(config=config), # Added config
|
||||
outputs=directory_path_dir,
|
||||
show_progress=False,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import os
|
|||
import sys
|
||||
from .common_gui import get_folder_path, add_pre_postfix, scriptdir, list_dirs, setup_environment
|
||||
from .custom_logging import setup_logging
|
||||
from .class_gui_config import KohyaSSGUIConfig # Added import
|
||||
|
||||
# Set up logging
|
||||
log = setup_logging()
|
||||
|
|
@ -112,7 +113,7 @@ def caption_images(
|
|||
###
|
||||
|
||||
|
||||
def gradio_blip_caption_gui_tab(headless=False, default_train_dir=None):
|
||||
def gradio_blip_caption_gui_tab(headless=False, default_train_dir=None, config: KohyaSSGUIConfig = {}): # Added config
|
||||
from .common_gui import create_refresh_button
|
||||
|
||||
default_train_dir = (
|
||||
|
|
@ -152,7 +153,7 @@ def gradio_blip_caption_gui_tab(headless=False, default_train_dir=None):
|
|||
visible=(not headless),
|
||||
)
|
||||
button_train_data_dir_input.click(
|
||||
get_folder_path,
|
||||
lambda: get_folder_path(config=config), # Added config
|
||||
outputs=train_data_dir,
|
||||
show_progress=False,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ class Folders:
|
|||
)
|
||||
# Output directory button click event
|
||||
self.output_dir_folder.click(
|
||||
get_folder_path,
|
||||
lambda: get_folder_path(config=self.config),
|
||||
outputs=self.output_dir,
|
||||
show_progress=False,
|
||||
)
|
||||
|
|
@ -161,7 +161,7 @@ class Folders:
|
|||
)
|
||||
# Regularisation directory button click event
|
||||
self.reg_data_dir_folder.click(
|
||||
get_folder_path,
|
||||
lambda: get_folder_path(config=self.config),
|
||||
outputs=self.reg_data_dir,
|
||||
show_progress=False,
|
||||
)
|
||||
|
|
@ -192,7 +192,7 @@ class Folders:
|
|||
)
|
||||
# Logging directory button click event
|
||||
self.logging_dir_folder.click(
|
||||
get_folder_path,
|
||||
lambda: get_folder_path(config=self.config),
|
||||
outputs=self.logging_dir,
|
||||
show_progress=False,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ class KohyaSSGUIConfig:
|
|||
Initialize the KohyaSSGUIConfig class.
|
||||
"""
|
||||
self.config = self.load_config(config_file_path=config_file_path)
|
||||
# Initialize last_used_folder during class initialization
|
||||
self.get_last_used_folder()
|
||||
|
||||
def load_config(self, config_file_path: str = "./config.toml") -> dict:
|
||||
"""
|
||||
|
|
@ -35,6 +37,11 @@ class KohyaSSGUIConfig:
|
|||
f"No configuration file found at {config_file_path}. Initializing empty configuration."
|
||||
)
|
||||
|
||||
# Ensure last_used_folder is present, defaulting to scriptdir
|
||||
if "last_used_folder" not in config:
|
||||
config["last_used_folder"] = scriptdir
|
||||
log.debug(f"Initialized 'last_used_folder' to '{scriptdir}'")
|
||||
|
||||
return config
|
||||
|
||||
def save_config(self, config: dict, config_file_path: str = "./config.toml"):
|
||||
|
|
@ -91,3 +98,37 @@ class KohyaSSGUIConfig:
|
|||
is_loaded = self.config != {}
|
||||
log.debug(f"Configuration was loaded from file: {is_loaded}")
|
||||
return is_loaded
|
||||
|
||||
def get_last_used_folder(self) -> str:
|
||||
"""
|
||||
Retrieves the last used folder from the configuration.
|
||||
|
||||
Returns:
|
||||
str: The last used folder path.
|
||||
"""
|
||||
folder = self.config.get("last_used_folder", scriptdir)
|
||||
# Ensure that the returned path is a string, even if it's empty or None from config
|
||||
if not isinstance(folder, str):
|
||||
folder = str(folder) if folder is not None else scriptdir
|
||||
# Update the config if the type was wrong.
|
||||
self.config["last_used_folder"] = folder
|
||||
log.debug(f"Retrieved last_used_folder: {folder}")
|
||||
return folder
|
||||
|
||||
def set_last_used_folder(self, folder_path: str):
|
||||
"""
|
||||
Sets the last used folder in the configuration.
|
||||
|
||||
Parameters:
|
||||
- folder_path (str): The path to the folder to be set.
|
||||
"""
|
||||
if not isinstance(folder_path, str):
|
||||
log.error(f"Attempted to set last_used_folder with non-string value: {folder_path}")
|
||||
# Optionally, raise an error or convert, for now, let's default to scriptdir
|
||||
# or handle as an error appropriately depending on desired behavior.
|
||||
# For robustness, let's ensure it's always a string or default if invalid.
|
||||
folder_path = scriptdir
|
||||
self.config["last_used_folder"] = folder_path
|
||||
log.debug(f"Set last_used_folder to: {folder_path}")
|
||||
# Note: Saving the config should be handled explicitly by the caller if needed immediately.
|
||||
# For example, by calling gui_config.save_config(gui_config.config)
|
||||
|
|
|
|||
|
|
@ -3,10 +3,13 @@ try:
|
|||
except ImportError:
|
||||
pass
|
||||
from easygui import msgbox, ynbox
|
||||
from typing import Optional
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
from .custom_logging import setup_logging
|
||||
from .sd_modeltype import SDModelType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .class_gui_config import KohyaSSGUIConfig
|
||||
|
||||
import os
|
||||
import re
|
||||
import gradio as gr
|
||||
|
|
@ -457,11 +460,12 @@ def get_dir_and_file(file_path):
|
|||
|
||||
|
||||
def get_file_path(
|
||||
file_path="", default_extension=".json", extension_name="Config files"
|
||||
file_path="", default_extension=".json", extension_name="Config files", config: "KohyaSSGUIConfig" = None
|
||||
):
|
||||
"""
|
||||
Opens a file dialog to select a file, allowing the user to navigate and choose a file with a specific extension.
|
||||
If no file is selected, returns the initially provided file path or an empty string if not provided.
|
||||
Uses and updates last_used_folder from the config if provided.
|
||||
This function is conditioned to skip the file dialog on macOS or if specific environment variables are present,
|
||||
indicating a possible automated environment where a dialog cannot be displayed.
|
||||
|
||||
|
|
@ -492,11 +496,16 @@ def get_file_path(
|
|||
if not any(var in os.environ for var in ENV_EXCLUSION) and sys.platform != "darwin":
|
||||
current_file_path = file_path # Backup in case no file is selected
|
||||
|
||||
if not os.path.dirname(file_path):
|
||||
initial_dir = scriptdir
|
||||
else:
|
||||
initial_dir = os.path.dirname(file_path)
|
||||
initial_file = os.path.basename(file_path)
|
||||
initial_dir_to_use, initial_file = get_dir_and_file(file_path)
|
||||
|
||||
if config:
|
||||
last_used = config.get_last_used_folder()
|
||||
if last_used and os.path.isdir(last_used):
|
||||
initial_dir_to_use = last_used
|
||||
|
||||
if not initial_dir_to_use or not os.path.isdir(initial_dir_to_use): # Check if valid dir
|
||||
initial_dir_to_use = scriptdir
|
||||
|
||||
|
||||
# Initialize a hidden Tkinter window for the file dialog
|
||||
root = Tk()
|
||||
|
|
@ -504,27 +513,38 @@ def get_file_path(
|
|||
root.withdraw() # Hide the root window to show only the dialog
|
||||
|
||||
# Open the file dialog and capture the selected file path
|
||||
file_path = filedialog.askopenfilename(
|
||||
returned_path = filedialog.askopenfilename(
|
||||
filetypes=((extension_name, f"*{default_extension}"), ("All files", "*.*")),
|
||||
defaultextension=default_extension,
|
||||
initialfile=initial_file,
|
||||
initialdir=initial_dir,
|
||||
initialdir=initial_dir_to_use,
|
||||
)
|
||||
|
||||
root.destroy() # Cleanup by destroying the Tkinter root window
|
||||
|
||||
# Fallback to the initial path if no selection is made
|
||||
if not file_path:
|
||||
if returned_path: # User selected a file
|
||||
file_path = returned_path
|
||||
if config:
|
||||
config.set_last_used_folder(os.path.dirname(file_path))
|
||||
else: # User cancelled dialog
|
||||
file_path = current_file_path
|
||||
# Do not update last_used_folder if dialog is cancelled
|
||||
else:
|
||||
# For non-dialog environments (headless/macOS)
|
||||
# If a file_path is provided, is a file, and config is available,
|
||||
# update last_used_folder based on its directory.
|
||||
if file_path and os.path.isfile(file_path) and config:
|
||||
config.set_last_used_folder(os.path.dirname(file_path))
|
||||
|
||||
# Return the selected or fallback file path
|
||||
return file_path
|
||||
|
||||
|
||||
def get_any_file_path(file_path: str = "") -> str:
|
||||
def get_any_file_path(file_path: str = "", config: "KohyaSSGUIConfig" = None) -> str:
|
||||
"""
|
||||
Opens a file dialog to select any file, allowing the user to navigate and choose a file.
|
||||
If no file is selected, returns the initially provided file path or an empty string if not provided.
|
||||
Uses and updates last_used_folder from the config if provided.
|
||||
This function is conditioned to skip the file dialog on macOS or if specific environment variables are present,
|
||||
indicating a possible automated environment where a dialog cannot be displayed.
|
||||
|
||||
|
|
@ -555,7 +575,15 @@ def get_any_file_path(file_path: str = "") -> str:
|
|||
):
|
||||
current_file_path: str = file_path
|
||||
|
||||
initial_dir, initial_file = get_dir_and_file(file_path)
|
||||
initial_dir_to_use, initial_file = get_dir_and_file(file_path)
|
||||
if config:
|
||||
last_used = config.get_last_used_folder()
|
||||
if last_used and os.path.isdir(last_used):
|
||||
initial_dir_to_use = last_used
|
||||
|
||||
if not initial_dir_to_use or not os.path.isdir(initial_dir_to_use): # Check if valid dir
|
||||
initial_dir_to_use = scriptdir
|
||||
|
||||
|
||||
# Initialize a hidden Tkinter window for the file dialog
|
||||
root = Tk()
|
||||
|
|
@ -564,8 +592,8 @@ def get_any_file_path(file_path: str = "") -> str:
|
|||
|
||||
try:
|
||||
# Open the file dialog and capture the selected file path
|
||||
file_path = filedialog.askopenfilename(
|
||||
initialdir=initial_dir,
|
||||
returned_path = filedialog.askopenfilename(
|
||||
initialdir=initial_dir_to_use, # Use determined initial_dir
|
||||
initialfile=initial_file,
|
||||
)
|
||||
except Exception as e:
|
||||
|
|
@ -573,9 +601,17 @@ def get_any_file_path(file_path: str = "") -> str:
|
|||
finally:
|
||||
root.destroy()
|
||||
|
||||
# Fallback to the initial path if no selection is made
|
||||
if not file_path:
|
||||
if returned_path: # User selected a file
|
||||
file_path = returned_path
|
||||
if config:
|
||||
config.set_last_used_folder(os.path.dirname(file_path))
|
||||
else: # User cancelled dialog
|
||||
file_path = current_file_path
|
||||
# Do not update last_used_folder if dialog is cancelled
|
||||
else:
|
||||
# For non-dialog environments
|
||||
if file_path and os.path.isfile(file_path) and config:
|
||||
config.set_last_used_folder(os.path.dirname(file_path))
|
||||
except KeyError as e:
|
||||
raise EnvironmentError(f"Failed to access environment variables: {e}")
|
||||
|
||||
|
|
@ -583,10 +619,11 @@ def get_any_file_path(file_path: str = "") -> str:
|
|||
return file_path
|
||||
|
||||
|
||||
def get_folder_path(folder_path: str = "") -> str:
|
||||
def get_folder_path(folder_path: str = "", config: "KohyaSSGUIConfig" = None) -> str:
|
||||
"""
|
||||
Opens a folder dialog to select a folder, allowing the user to navigate and choose a folder.
|
||||
If no folder is selected, returns the initially provided folder path or an empty string if not provided.
|
||||
Uses and updates last_used_folder from the config if provided.
|
||||
This function is conditioned to skip the folder dialog on macOS or if specific environment variables are present,
|
||||
indicating a possible automated environment where a dialog cannot be displayed.
|
||||
|
||||
|
|
@ -609,70 +646,109 @@ def get_folder_path(folder_path: str = "") -> str:
|
|||
if not isinstance(folder_path, str):
|
||||
raise TypeError("folder_path must be a string")
|
||||
|
||||
initial_dir_to_use = scriptdir # Default initial directory
|
||||
|
||||
if config:
|
||||
last_used = config.get_last_used_folder()
|
||||
if last_used and os.path.isdir(last_used):
|
||||
initial_dir_to_use = last_used
|
||||
elif folder_path and os.path.isdir(folder_path): # Fallback to folder_path if last_used is invalid
|
||||
initial_dir_to_use = folder_path
|
||||
# If both last_used and folder_path are invalid or not provided, scriptdir is used as already set
|
||||
elif folder_path and os.path.isdir(folder_path): # No config, but folder_path is valid
|
||||
initial_dir_to_use = folder_path
|
||||
|
||||
|
||||
try:
|
||||
# Check for environment variable conditions
|
||||
# Check for environment variable conditions to skip dialog
|
||||
if any(var in os.environ for var in ENV_EXCLUSION) or sys.platform == "darwin":
|
||||
# Even if dialog is skipped, if a valid folder_path is given and config is present,
|
||||
# consider it as the "selected" path for updating last_used_folder.
|
||||
if folder_path and os.path.isdir(folder_path) and config:
|
||||
config.set_last_used_folder(folder_path)
|
||||
return folder_path or ""
|
||||
|
||||
root = Tk()
|
||||
root.withdraw()
|
||||
root.wm_attributes("-topmost", 1)
|
||||
selected_folder = filedialog.askdirectory(initialdir=folder_path or ".")
|
||||
# Use initial_dir_to_use, which has been determined based on config or folder_path
|
||||
selected_folder = filedialog.askdirectory(initialdir=initial_dir_to_use)
|
||||
root.destroy()
|
||||
return selected_folder or folder_path
|
||||
|
||||
if selected_folder: # User selected a folder
|
||||
if config:
|
||||
config.set_last_used_folder(selected_folder)
|
||||
return selected_folder
|
||||
else: # User cancelled dialog
|
||||
# Return the original folder_path or empty string, do not update last_used_folder
|
||||
return folder_path
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"Error initializing folder dialog: {e}") from e
|
||||
# Log the exception or handle it as per application's error handling policy
|
||||
log.error(f"Error initializing folder dialog: {e}")
|
||||
# Fallback to returning the original folder_path in case of an unexpected error
|
||||
return folder_path
|
||||
|
||||
|
||||
def get_saveasfile_path(
|
||||
file_path: str = "",
|
||||
defaultextension: str = ".json",
|
||||
extension_name: str = "Config files",
|
||||
config: "KohyaSSGUIConfig" = None,
|
||||
) -> str:
|
||||
"""
|
||||
Opens a file dialog to select a file name for saving, allowing the user to specify a file name and location.
|
||||
If no file is selected, returns the initially provided file path or an empty string if not provided.
|
||||
Uses and updates last_used_folder from the config if provided.
|
||||
Note: This function now uses asksaveasfilename for consistency.
|
||||
"""
|
||||
# Check if the current environment is not macOS and if the environment variables do not match the exclusion list
|
||||
if not any(var in os.environ for var in ENV_EXCLUSION) and sys.platform != "darwin":
|
||||
# Store the initial file path to use as a fallback in case no file is selected
|
||||
current_file_path = file_path
|
||||
|
||||
# Logging the current file path for debugging purposes; helps in tracking the flow of file selection
|
||||
# log.info(f'current file path: {current_file_path}')
|
||||
initial_dir_to_use, initial_file = get_dir_and_file(file_path)
|
||||
if config:
|
||||
last_used = config.get_last_used_folder()
|
||||
if last_used and os.path.isdir(last_used):
|
||||
initial_dir_to_use = last_used
|
||||
|
||||
# Split the file path into directory and file name for setting the file dialog start location and filename
|
||||
initial_dir, initial_file = get_dir_and_file(file_path)
|
||||
if not initial_dir_to_use or not os.path.isdir(initial_dir_to_use): # Check if valid dir
|
||||
initial_dir_to_use = scriptdir
|
||||
|
||||
# Initialize a hidden Tkinter window to act as the parent for the file dialog, ensuring it appears on top
|
||||
root = Tk()
|
||||
root.wm_attributes("-topmost", 1)
|
||||
root.withdraw()
|
||||
save_file_path = filedialog.asksaveasfile(
|
||||
|
||||
# Using asksaveasfilename to get the path string directly
|
||||
returned_path = filedialog.asksaveasfilename(
|
||||
filetypes=(
|
||||
(f"{extension_name}", f"{defaultextension}"),
|
||||
("All files", "*"),
|
||||
(f"{extension_name}", f"*{defaultextension}"), # Ensure wildcard for extension
|
||||
("All files", "*.*"),
|
||||
),
|
||||
defaultextension=defaultextension,
|
||||
initialdir=initial_dir,
|
||||
initialdir=initial_dir_to_use,
|
||||
initialfile=initial_file,
|
||||
)
|
||||
# Close the Tkinter root window to clean up the UI
|
||||
root.destroy()
|
||||
|
||||
# Logging the save file path for auditing purposes; useful in confirming the user's file choice
|
||||
# log.info(save_file_path)
|
||||
|
||||
# Default to the current file path if no file is selected, ensuring there's always a valid file path
|
||||
if save_file_path == None:
|
||||
if returned_path: # User selected a path
|
||||
file_path = returned_path
|
||||
if config:
|
||||
config.set_last_used_folder(os.path.dirname(file_path))
|
||||
else: # User cancelled dialog
|
||||
file_path = current_file_path
|
||||
# Do not update last_used_folder if dialog is cancelled
|
||||
else:
|
||||
# Log the selected file name for transparency and tracking user actions
|
||||
# log.info(save_file_path.name)
|
||||
# For non-dialog environments
|
||||
# If a file_path is provided (even if it doesn't exist yet, its dir might be valid)
|
||||
# and config is available, update last_used_folder.
|
||||
if file_path and config:
|
||||
dir_name = os.path.dirname(file_path)
|
||||
if dir_name and os.path.isdir(dir_name): # Check if directory is valid
|
||||
config.set_last_used_folder(dir_name)
|
||||
elif not dir_name: # Path is likely just a filename, use scriptdir
|
||||
config.set_last_used_folder(scriptdir)
|
||||
|
||||
# Update the file path with the user-selected file name, facilitating the save operation
|
||||
file_path = save_file_path.name
|
||||
|
||||
# Log the final file path for verification, ensuring the intended file is being used
|
||||
# log.info(file_path)
|
||||
|
||||
# Return the final file path, either the user-selected file or the fallback path
|
||||
return file_path
|
||||
|
||||
|
||||
|
|
@ -680,10 +756,12 @@ def get_saveasfilename_path(
|
|||
file_path: str = "",
|
||||
extensions: str = "*",
|
||||
extension_name: str = "Config files",
|
||||
config: "KohyaSSGUIConfig" = None,
|
||||
) -> str:
|
||||
"""
|
||||
Opens a file dialog to select a file name for saving, allowing the user to specify a file name and location.
|
||||
If no file is selected, returns the initially provided file path or an empty string if not provided.
|
||||
Uses and updates last_used_folder from the config if provided.
|
||||
This function is conditioned to skip the file dialog on macOS or if specific environment variables are present,
|
||||
indicating a possible automated environment where a dialog cannot be displayed.
|
||||
|
||||
|
|
@ -708,36 +786,48 @@ def get_saveasfilename_path(
|
|||
if not any(var in os.environ for var in ENV_EXCLUSION) and sys.platform != "darwin":
|
||||
# Store the initial file path to use as a fallback in case no file is selected
|
||||
current_file_path: str = file_path
|
||||
# log.info(f'current file path: {current_file_path}')
|
||||
|
||||
# Split the file path into directory and file name for setting the file dialog start location and filename
|
||||
initial_dir, initial_file = get_dir_and_file(file_path)
|
||||
initial_dir_to_use, initial_file = get_dir_and_file(file_path)
|
||||
if config:
|
||||
last_used = config.get_last_used_folder()
|
||||
if last_used and os.path.isdir(last_used):
|
||||
initial_dir_to_use = last_used
|
||||
|
||||
if not initial_dir_to_use or not os.path.isdir(initial_dir_to_use): # Check if valid dir
|
||||
initial_dir_to_use = scriptdir
|
||||
|
||||
# Initialize a hidden Tkinter window to act as the parent for the file dialog, ensuring it appears on top
|
||||
root = Tk()
|
||||
root.wm_attributes("-topmost", 1)
|
||||
root.withdraw()
|
||||
# Open the file dialog and capture the selected file path
|
||||
save_file_path = filedialog.asksaveasfilename(
|
||||
returned_path = filedialog.asksaveasfilename(
|
||||
filetypes=(
|
||||
(f"{extension_name}", f"{extensions}"),
|
||||
("All files", "*"),
|
||||
(f"{extension_name}", f"{extensions}"), # Corrected: uses extensions parameter
|
||||
("All files", "*.*"),
|
||||
),
|
||||
defaultextension=extensions,
|
||||
initialdir=initial_dir,
|
||||
defaultextension=extensions, # Corrected: uses extensions parameter
|
||||
initialdir=initial_dir_to_use,
|
||||
initialfile=initial_file,
|
||||
)
|
||||
# Close the Tkinter root window to clean up the UI
|
||||
root.destroy()
|
||||
|
||||
# Default to the current file path if no file is selected, ensuring there's always a valid file path
|
||||
if save_file_path == "":
|
||||
if returned_path: # User selected a path
|
||||
file_path = returned_path
|
||||
if config:
|
||||
config.set_last_used_folder(os.path.dirname(file_path))
|
||||
else: # User cancelled dialog
|
||||
file_path = current_file_path
|
||||
# Do not update last_used_folder if dialog is cancelled
|
||||
else:
|
||||
# Logging the save file path for auditing purposes; useful in confirming the user's file choice
|
||||
# log.info(save_file_path)
|
||||
# Update the file path with the user-selected file name, facilitating the save operation
|
||||
file_path = save_file_path
|
||||
# For non-dialog environments
|
||||
if file_path and config:
|
||||
dir_name = os.path.dirname(file_path)
|
||||
if dir_name and os.path.isdir(dir_name):
|
||||
config.set_last_used_folder(dir_name)
|
||||
elif not dir_name: # Path is likely just a filename, use scriptdir
|
||||
config.set_last_used_folder(scriptdir)
|
||||
|
||||
# Return the final file path, either the user-selected file or the fallback path
|
||||
return file_path
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import subprocess
|
|||
import os
|
||||
import sys
|
||||
from .common_gui import get_folder_path, add_pre_postfix, scriptdir, list_dirs, setup_environment
|
||||
from .class_gui_config import KohyaSSGUIConfig # Added import
|
||||
|
||||
from .custom_logging import setup_logging
|
||||
|
||||
|
|
@ -85,7 +86,7 @@ def caption_images(
|
|||
|
||||
|
||||
def gradio_git_caption_gui_tab(
|
||||
headless=False, default_train_dir=None,
|
||||
headless=False, default_train_dir=None, config: KohyaSSGUIConfig = {} # Added config
|
||||
):
|
||||
from .common_gui import create_refresh_button
|
||||
|
||||
|
|
@ -126,7 +127,7 @@ def gradio_git_caption_gui_tab(
|
|||
visible=(not headless),
|
||||
)
|
||||
button_train_data_dir_input.click(
|
||||
get_folder_path,
|
||||
lambda: get_folder_path(config=config), # Added config
|
||||
outputs=train_data_dir,
|
||||
show_progress=False,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import gradio as gr
|
||||
from easygui import msgbox, boolbox
|
||||
from .common_gui import get_folder_path, scriptdir, list_dirs
|
||||
from .class_gui_config import KohyaSSGUIConfig # Added import
|
||||
from math import ceil
|
||||
import os
|
||||
import re
|
||||
|
|
@ -250,7 +251,7 @@ def update_images(
|
|||
|
||||
|
||||
# Gradio UI
|
||||
def gradio_manual_caption_gui_tab(headless=False, default_images_dir=None):
|
||||
def gradio_manual_caption_gui_tab(headless=False, default_images_dir=None, config: KohyaSSGUIConfig = {}): # Added config
|
||||
from .common_gui import create_refresh_button
|
||||
|
||||
default_images_dir = (
|
||||
|
|
@ -293,7 +294,7 @@ def gradio_manual_caption_gui_tab(headless=False, default_images_dir=None):
|
|||
visible=(not headless),
|
||||
)
|
||||
folder_button.click(
|
||||
get_folder_path,
|
||||
lambda: get_folder_path(config=config), # Added config
|
||||
outputs=images_dir,
|
||||
show_progress=False,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -20,12 +20,12 @@ def utilities_tab(
|
|||
config: KohyaSSGUIConfig = {},
|
||||
):
|
||||
with gr.Tab("Captioning"):
|
||||
gradio_basic_caption_gui_tab(headless=headless)
|
||||
gradio_blip_caption_gui_tab(headless=headless)
|
||||
gradio_blip2_caption_gui_tab(headless=headless)
|
||||
gradio_git_caption_gui_tab(headless=headless)
|
||||
gradio_basic_caption_gui_tab(headless=headless, config=config)
|
||||
gradio_blip_caption_gui_tab(headless=headless, config=config)
|
||||
gradio_blip2_caption_gui_tab(headless=headless, config=config)
|
||||
gradio_git_caption_gui_tab(headless=headless, config=config)
|
||||
gradio_wd14_caption_gui_tab(headless=headless, config=config)
|
||||
gradio_manual_caption_gui_tab(headless=headless)
|
||||
gradio_manual_caption_gui_tab(headless=headless, config=config)
|
||||
gradio_convert_model_tab(headless=headless)
|
||||
gradio_group_images_gui_tab(headless=headless)
|
||||
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ def gradio_wd14_caption_gui_tab(
|
|||
visible=(not headless),
|
||||
)
|
||||
button_train_data_dir_input.click(
|
||||
get_folder_path,
|
||||
lambda: get_folder_path(config=config),
|
||||
outputs=train_data_dir,
|
||||
show_progress=False,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
from kohya_gui.class_gui_config import KohyaSSGUIConfig
|
||||
from kohya_gui.common_gui import get_folder_path, scriptdir
|
||||
import os
|
||||
|
||||
# Ensure the target directory exists
|
||||
os.makedirs("/tmp/test_folder1", exist_ok=True)
|
||||
|
||||
# 1. Create config (simulating app launch)
|
||||
config_handler = KohyaSSGUIConfig(config_file_path="/app/config.toml")
|
||||
print(f"Initial last_used_folder from config: {config_handler.get_last_used_folder()}") # Should be scriptdir
|
||||
|
||||
# 2. Simulate get_folder_path
|
||||
# Normally, tkinter dialog would run. We simulate its effect.
|
||||
# get_folder_path internally calls config.set_last_used_folder
|
||||
# Let's assume the dialog was opened with initialdir=scriptdir and user selected /tmp/test_folder1
|
||||
# For the test, we'll directly set it after a simulated selection to mimic the function's behavior
|
||||
# In a real scenario, get_folder_path would be called, and it would call set_last_used_folder.
|
||||
# Here, we simplify by directly manipulating for test verification.
|
||||
|
||||
# Simulate call to get_folder_path where user selects /tmp/test_folder1
|
||||
# This is a simplified mock of what would happen:
|
||||
# initial_dir = config_handler.get_last_used_folder() # This would be scriptdir
|
||||
# print(f"Dialog would open with initial_dir: {initial_dir}")
|
||||
# selected_path_by_user = "/tmp/test_folder1" # User selects this
|
||||
# if selected_path_by_user:
|
||||
# config_handler.set_last_used_folder(selected_path_by_user)
|
||||
# print(f"Set last_used_folder to: {selected_path_by_user}")
|
||||
# This is what happens inside get_folder_path:
|
||||
def mock_get_folder_path(current_path_in_field, cfg_obj, simulated_user_selection):
|
||||
# Logic from common_gui.get_folder_path for initial_dir_to_use
|
||||
initial_dir_to_use = scriptdir
|
||||
if cfg_obj:
|
||||
last_used = cfg_obj.get_last_used_folder()
|
||||
if last_used and os.path.isdir(last_used):
|
||||
initial_dir_to_use = last_used
|
||||
elif current_path_in_field and os.path.isdir(current_path_in_field):
|
||||
initial_dir_to_use = current_path_in_field
|
||||
elif current_path_in_field and os.path.isdir(current_path_in_field):
|
||||
initial_dir_to_use = current_path_in_field
|
||||
print(f"Dialog would open with initial_dir: {initial_dir_to_use}")
|
||||
|
||||
# Simulate user selection
|
||||
if simulated_user_selection:
|
||||
if cfg_obj:
|
||||
cfg_obj.set_last_used_folder(simulated_user_selection)
|
||||
print(f"Set last_used_folder to: {simulated_user_selection}")
|
||||
return simulated_user_selection
|
||||
return current_path_in_field
|
||||
|
||||
# Simulate the scenario: field is empty, user selects /tmp/test_folder1
|
||||
returned_path = mock_get_folder_path("", config_handler, "/tmp/test_folder1")
|
||||
print(f"Returned path: {returned_path}")
|
||||
|
||||
|
||||
# 3. Save config
|
||||
config_handler.save_config(config=config_handler.config, config_file_path="/app/config.toml")
|
||||
print("Config saved.")
|
||||
|
||||
# Verify by reloading
|
||||
config_handler_verify = KohyaSSGUIConfig(config_file_path="/app/config.toml")
|
||||
print(f"After save, last_used_folder from new config: {config_handler_verify.get_last_used_folder()}")
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
import os
|
||||
import sys
|
||||
import unittest.mock as mock
|
||||
|
||||
# Mock easygui and its problematic dependencies before they are imported by kohya_gui
|
||||
sys.modules['easygui'] = mock.MagicMock()
|
||||
sys.modules['tkinter'] = mock.MagicMock()
|
||||
sys.modules['Tkinter'] = mock.MagicMock() # For Python 2 fallback in easygui
|
||||
|
||||
# Attempt to preemptively mock parts of easygui that might still load
|
||||
# This is to handle the "global_state" not found if easygui's __init__ tries to import its own modules
|
||||
# that might then fail on tkinter not being truly available.
|
||||
mock_easygui = mock.MagicMock()
|
||||
mock_easygui.msgbox = mock.MagicMock()
|
||||
mock_easygui.ynbox = mock.MagicMock()
|
||||
sys.modules['easygui'] = mock_easygui
|
||||
|
||||
|
||||
# Set an env var to simulate a non-interactive environment (though mocking should handle most UI calls)
|
||||
os.environ["COLAB_GPU"] = "True"
|
||||
|
||||
from kohya_gui.class_gui_config import KohyaSSGUIConfig
|
||||
from kohya_gui.common_gui import get_folder_path, scriptdir, get_file_path, get_saveasfilename_path # Import other functions as needed
|
||||
|
||||
print(f"Scriptdir resolved to: {scriptdir}")
|
||||
# Ensure scriptdir is an absolute path for consistency, as it's used for defaults
|
||||
if not os.path.isabs(scriptdir):
|
||||
scriptdir = os.path.abspath(scriptdir)
|
||||
print(f"Updated scriptdir to absolute path: {scriptdir}")
|
||||
|
||||
|
||||
# --- Test 1: Initial state and first folder operation ---
|
||||
print("--- Test 1: Initial Folder Operation ---")
|
||||
config_handler = KohyaSSGUIConfig(config_file_path="/app/config.toml")
|
||||
initial_last_folder = config_handler.get_last_used_folder()
|
||||
print(f"Initial last_used_folder from new config object: {initial_last_folder}")
|
||||
# Depending on KohyaSSGUIConfig implementation, initial value might be scriptdir or empty string
|
||||
# before first save. get_last_used_folder itself initializes it to scriptdir if not in config.
|
||||
assert initial_last_folder == scriptdir, f"Expected {scriptdir}, got {initial_last_folder}"
|
||||
|
||||
|
||||
# Simulate selecting /tmp/test_folder1 for a folder operation
|
||||
# In non-dialog mode (due to COLAB_GPU=True), get_folder_path should use the provided 'folder_path'
|
||||
# and update the config.
|
||||
returned_path = get_folder_path(folder_path="/tmp/test_folder1", config=config_handler)
|
||||
print(f"Returned path from get_folder_path: {returned_path}")
|
||||
assert returned_path == "/tmp/test_folder1"
|
||||
current_last_folder = config_handler.get_last_used_folder()
|
||||
print(f"Last used folder after get_folder_path: {current_last_folder}")
|
||||
assert current_last_folder == "/tmp/test_folder1", f"Expected /tmp/test_folder1, got {current_last_folder}"
|
||||
|
||||
config_handler.save_config(config=config_handler.config, config_file_path="/app/config.toml")
|
||||
print("Config saved.")
|
||||
|
||||
with open("/app/config.toml", "r") as f:
|
||||
content = f.read()
|
||||
print(f"config.toml content:\n{content}")
|
||||
assert 'last_used_folder = "/tmp/test_folder1"' in content
|
||||
print("--- Test 1 Passed ---")
|
||||
|
||||
# --- Test 2: Relaunch and second folder operation ---
|
||||
print("\n--- Test 2: Relaunch and Second Folder Operation ---")
|
||||
config_handler_relaunch = KohyaSSGUIConfig(config_file_path="/app/config.toml")
|
||||
relaunch_last_folder = config_handler_relaunch.get_last_used_folder()
|
||||
print(f"On relaunch, last_used_folder is: {relaunch_last_folder}")
|
||||
assert relaunch_last_folder == "/tmp/test_folder1", f"Expected /tmp/test_folder1, got {relaunch_last_folder}"
|
||||
|
||||
# Simulate selecting /tmp/test_folder2
|
||||
returned_path_2 = get_folder_path(folder_path="/tmp/test_folder2", config=config_handler_relaunch)
|
||||
print(f"Returned path from second get_folder_path: {returned_path_2}")
|
||||
assert returned_path_2 == "/tmp/test_folder2"
|
||||
current_last_folder_2 = config_handler_relaunch.get_last_used_folder()
|
||||
print(f"Last used folder after second get_folder_path: {current_last_folder_2}")
|
||||
assert current_last_folder_2 == "/tmp/test_folder2", f"Expected /tmp/test_folder2, got {current_last_folder_2}"
|
||||
|
||||
config_handler_relaunch.save_config(config=config_handler_relaunch.config, config_file_path="/app/config.toml")
|
||||
print("Config saved for second operation.")
|
||||
|
||||
with open("/app/config.toml", "r") as f:
|
||||
content_2 = f.read()
|
||||
print(f"config.toml content after second operation:\n{content_2}")
|
||||
assert 'last_used_folder = "/tmp/test_folder2"' in content_2
|
||||
print("--- Test 2 Passed ---")
|
||||
|
||||
print("\nInitial State Check (Folders) successful.")
|
||||
|
||||
# --- Test 3: File open operation ---
|
||||
print("\n--- Test 3: File Open Operation ---")
|
||||
# Ensure target directory for file exists
|
||||
os.makedirs("/tmp/test_config_folder", exist_ok=True) # Changed from /projects to /tmp
|
||||
dummy_file_path = "/tmp/test_config_folder/my_config.json" # Changed from /projects to /tmp
|
||||
if not os.path.exists(dummy_file_path):
|
||||
with open(dummy_file_path, "w") as f: f.write("{}")
|
||||
|
||||
# Config handler is config_handler_relaunch, which has last_used_folder = /tmp/test_folder2
|
||||
# Simulate opening a file
|
||||
# In non-dialog mode, get_file_path will set last_used_folder to dirname of 'file_path'
|
||||
returned_file_path = get_file_path(file_path=dummy_file_path, config=config_handler_relaunch)
|
||||
print(f"Returned path from get_file_path: {returned_file_path}")
|
||||
assert returned_file_path == dummy_file_path
|
||||
current_last_folder_3 = config_handler_relaunch.get_last_used_folder()
|
||||
print(f"Last used folder after get_file_path: {current_last_folder_3}")
|
||||
assert current_last_folder_3 == "/tmp/test_config_folder", f"Expected /tmp/test_config_folder, got {current_last_folder_3}" # Changed from /projects to /tmp
|
||||
|
||||
config_handler_relaunch.save_config(config=config_handler_relaunch.config, config_file_path="/app/config.toml")
|
||||
with open("/app/config.toml", "r") as f:
|
||||
content_3 = f.read()
|
||||
print(f"config.toml content after file open:\n{content_3}")
|
||||
assert 'last_used_folder = "/tmp/test_config_folder"' in content_3 # Changed from /projects to /tmp
|
||||
print("--- Test 3 Passed ---")
|
||||
|
||||
# --- Test 4: File save operation ---
|
||||
print("\n--- Test 4: File Save Operation ---")
|
||||
# Ensure target directory for save exists
|
||||
os.makedirs("/tmp/another_save_folder", exist_ok=True) # Changed from /projects to /tmp
|
||||
save_file_as_path = "/tmp/another_save_folder/my_new_config.json" # Changed from /projects to /tmp
|
||||
|
||||
# Config handler still has last_used_folder = /tmp/test_config_folder
|
||||
# Simulate saving a file
|
||||
# In non-dialog mode, get_saveasfilename_path will set last_used_folder to dirname of 'file_path'
|
||||
returned_save_path = get_saveasfilename_path(file_path=save_file_as_path, config=config_handler_relaunch)
|
||||
print(f"Returned path from get_saveasfilename_path: {returned_save_path}")
|
||||
assert returned_save_path == save_file_as_path # In non-dialog mode, it returns the path given
|
||||
current_last_folder_4 = config_handler_relaunch.get_last_used_folder()
|
||||
print(f"Last used folder after get_saveasfilename_path: {current_last_folder_4}")
|
||||
assert current_last_folder_4 == "/tmp/another_save_folder", f"Expected /tmp/another_save_folder, got {current_last_folder_4}" # Changed from /projects to /tmp
|
||||
|
||||
config_handler_relaunch.save_config(config=config_handler_relaunch.config, config_file_path="/app/config.toml")
|
||||
with open("/app/config.toml", "r") as f:
|
||||
content_4 = f.read()
|
||||
print(f"config.toml content after file save:\n{content_4}")
|
||||
assert 'last_used_folder = "/tmp/another_save_folder"' in content_4 # Changed from /projects to /tmp
|
||||
print("--- Test 4 Passed ---")
|
||||
|
||||
print("\nAll Initial State and basic operations tests passed.")
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
import os
|
||||
import sys
|
||||
import unittest.mock as mock
|
||||
|
||||
# Mock easygui and its problematic dependencies
|
||||
sys.modules['easygui'] = mock.MagicMock()
|
||||
sys.modules['tkinter'] = mock.MagicMock()
|
||||
sys.modules['Tkinter'] = mock.MagicMock()
|
||||
mock_easygui = mock.MagicMock()
|
||||
mock_easygui.msgbox = mock.MagicMock()
|
||||
mock_easygui.ynbox = mock.MagicMock()
|
||||
sys.modules['easygui'] = mock_easygui
|
||||
|
||||
os.environ["COLAB_GPU"] = "True" # Simulate non-interactive environment
|
||||
|
||||
# Check if config.toml exists before KohyaSSGUIConfig is initialized
|
||||
CONFIG_PATH = "/app/config.toml"
|
||||
if os.path.exists(CONFIG_PATH):
|
||||
print(f"WARNING: {CONFIG_PATH} exists before test! Content:")
|
||||
with open(CONFIG_PATH, "r") as f:
|
||||
print(f.read())
|
||||
# Attempt to remove it again just in case
|
||||
os.remove(CONFIG_PATH)
|
||||
print(f"WARNING: Removed pre-existing {CONFIG_PATH}")
|
||||
else:
|
||||
print(f"{CONFIG_PATH} does not exist before test, as expected.")
|
||||
|
||||
from kohya_gui.class_gui_config import KohyaSSGUIConfig
|
||||
from kohya_gui.common_gui import get_folder_path, scriptdir, get_file_path, get_saveasfilename_path
|
||||
|
||||
print(f"Scriptdir resolved to: {scriptdir}")
|
||||
# Ensure scriptdir is an absolute path for consistency
|
||||
if not os.path.isabs(scriptdir): # This should not happen if common_gui.py defines it as absolute
|
||||
scriptdir = os.path.abspath(scriptdir) # Make it absolute for safety in test logic
|
||||
print(f"Updated scriptdir to absolute path: {scriptdir}")
|
||||
|
||||
|
||||
# --- Test 1: Initial state and first folder operation ---
|
||||
print("--- Test 1: Initial Folder Operation ---")
|
||||
config_handler = KohyaSSGUIConfig(config_file_path=CONFIG_PATH)
|
||||
initial_last_folder = config_handler.get_last_used_folder()
|
||||
print(f"Initial last_used_folder from new config object: {initial_last_folder}")
|
||||
assert initial_last_folder == scriptdir, f"Expected {scriptdir}, got {initial_last_folder}"
|
||||
|
||||
returned_path = get_folder_path(folder_path="/tmp/test_folder1", config=config_handler)
|
||||
print(f"Returned path from get_folder_path: {returned_path}")
|
||||
assert returned_path == "/tmp/test_folder1"
|
||||
current_last_folder = config_handler.get_last_used_folder()
|
||||
print(f"Last used folder after get_folder_path: {current_last_folder}")
|
||||
assert current_last_folder == "/tmp/test_folder1", f"Expected /tmp/test_folder1, got {current_last_folder}"
|
||||
|
||||
config_handler.save_config(config=config_handler.config, config_file_path=CONFIG_PATH)
|
||||
print("Config saved.")
|
||||
|
||||
with open(CONFIG_PATH, "r") as f:
|
||||
content = f.read()
|
||||
print(f"config.toml content:\n{content}")
|
||||
assert 'last_used_folder = "/tmp/test_folder1"' in content
|
||||
print("--- Test 1 Passed ---")
|
||||
|
||||
# --- Test 2: Relaunch and second folder operation ---
|
||||
print("\n--- Test 2: Relaunch and Second Folder Operation ---")
|
||||
config_handler_relaunch = KohyaSSGUIConfig(config_file_path=CONFIG_PATH)
|
||||
relaunch_last_folder = config_handler_relaunch.get_last_used_folder()
|
||||
print(f"On relaunch, last_used_folder is: {relaunch_last_folder}")
|
||||
assert relaunch_last_folder == "/tmp/test_folder1", f"Expected /tmp/test_folder1, got {relaunch_last_folder}"
|
||||
|
||||
returned_path_2 = get_folder_path(folder_path="/tmp/test_folder2", config=config_handler_relaunch)
|
||||
print(f"Returned path from second get_folder_path: {returned_path_2}")
|
||||
assert returned_path_2 == "/tmp/test_folder2"
|
||||
current_last_folder_2 = config_handler_relaunch.get_last_used_folder()
|
||||
print(f"Last used folder after second get_folder_path: {current_last_folder_2}")
|
||||
assert current_last_folder_2 == "/tmp/test_folder2", f"Expected /tmp/test_folder2, got {current_last_folder_2}"
|
||||
|
||||
config_handler_relaunch.save_config(config=config_handler_relaunch.config, config_file_path=CONFIG_PATH)
|
||||
print("Config saved for second operation.")
|
||||
|
||||
with open(CONFIG_PATH, "r") as f:
|
||||
content_2 = f.read()
|
||||
print(f"config.toml content after second operation:\n{content_2}")
|
||||
assert 'last_used_folder = "/tmp/test_folder2"' in content_2
|
||||
print("--- Test 2 Passed ---")
|
||||
|
||||
print("\nInitial State Check (Folders) successful.")
|
||||
|
||||
# --- Test 3: File open operation ---
|
||||
print("\n--- Test 3: File Open Operation ---")
|
||||
os.makedirs("/tmp/test_config_folder", exist_ok=True)
|
||||
dummy_file_path = "/tmp/test_config_folder/my_config.json"
|
||||
if not os.path.exists(dummy_file_path):
|
||||
with open(dummy_file_path, "w") as f: f.write("{}")
|
||||
|
||||
returned_file_path = get_file_path(file_path=dummy_file_path, config=config_handler_relaunch)
|
||||
print(f"Returned path from get_file_path: {returned_file_path}")
|
||||
assert returned_file_path == dummy_file_path
|
||||
current_last_folder_3 = config_handler_relaunch.get_last_used_folder()
|
||||
print(f"Last used folder after get_file_path: {current_last_folder_3}")
|
||||
assert current_last_folder_3 == "/tmp/test_config_folder", f"Expected /tmp/test_config_folder, got {current_last_folder_3}"
|
||||
|
||||
config_handler_relaunch.save_config(config=config_handler_relaunch.config, config_file_path=CONFIG_PATH)
|
||||
with open(CONFIG_PATH, "r") as f:
|
||||
content_3 = f.read()
|
||||
print(f"config.toml content after file open:\n{content_3}")
|
||||
assert 'last_used_folder = "/tmp/test_config_folder"' in content_3
|
||||
print("--- Test 3 Passed ---")
|
||||
|
||||
# --- Test 4: File save operation ---
|
||||
print("\n--- Test 4: File Save Operation ---")
|
||||
os.makedirs("/tmp/another_save_folder", exist_ok=True)
|
||||
save_file_as_path = "/tmp/another_save_folder/my_new_config.json"
|
||||
|
||||
returned_save_path = get_saveasfilename_path(file_path=save_file_as_path, config=config_handler_relaunch)
|
||||
print(f"Returned path from get_saveasfilename_path: {returned_save_path}")
|
||||
assert returned_save_path == save_file_as_path
|
||||
current_last_folder_4 = config_handler_relaunch.get_last_used_folder()
|
||||
print(f"Last used folder after get_saveasfilename_path: {current_last_folder_4}")
|
||||
assert current_last_folder_4 == "/tmp/another_save_folder", f"Expected /tmp/another_save_folder, got {current_last_folder_4}"
|
||||
|
||||
config_handler_relaunch.save_config(config=config_handler_relaunch.config, config_file_path=CONFIG_PATH)
|
||||
with open(CONFIG_PATH, "r") as f:
|
||||
content_4 = f.read()
|
||||
print(f"config.toml content after file save:\n{content_4}")
|
||||
assert 'last_used_folder = "/tmp/another_save_folder"' in content_4
|
||||
print("--- Test 4 Passed ---")
|
||||
|
||||
print("\nAll Initial State and basic operations tests passed.")
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
import os
|
||||
# Set an env var to simulate a non-interactive environment
|
||||
os.environ["COLAB_GPU"] = "True"
|
||||
|
||||
from kohya_gui.class_gui_config import KohyaSSGUIConfig
|
||||
from kohya_gui.common_gui import get_folder_path, scriptdir # scriptdir is used by get_last_used_folder as a default
|
||||
|
||||
# --- Test 1: Initial state and first operation ---
|
||||
print("--- Test 1 ---")
|
||||
# 1. Create config (simulating app launch, config file doesn't exist yet)
|
||||
config_handler = KohyaSSGUIConfig(config_file_path="/app/config.toml")
|
||||
# Ensure last_used_folder defaults to scriptdir (or an empty string, depending on implementation details)
|
||||
# get_last_used_folder initializes it to scriptdir if not in config
|
||||
print(f"Initial last_used_folder from new config object: {config_handler.get_last_used_folder()}")
|
||||
|
||||
# 2. Simulate get_folder_path where user "selects" /tmp/test_folder1
|
||||
# In non-dialog mode, get_folder_path will set last_used_folder if folder_path is valid and config is present.
|
||||
# We pass folder_path="/tmp/test_folder1" as if it was typed into a field and then an action triggered.
|
||||
# Or, as if it's the path being "opened" or "selected".
|
||||
returned_path = get_folder_path(folder_path="/tmp/test_folder1", config=config_handler)
|
||||
print(f"Returned path from get_folder_path: {returned_path}")
|
||||
print(f"Last used folder after get_folder_path: {config_handler.get_last_used_folder()}")
|
||||
|
||||
# 3. Save config
|
||||
config_handler.save_config(config=config_handler.config, config_file_path="/app/config.toml")
|
||||
print("Config saved.")
|
||||
|
||||
# Verify by reading the file content
|
||||
with open("/app/config.toml", "r") as f:
|
||||
content = f.read()
|
||||
print(f"config.toml content:\n{content}")
|
||||
assert 'last_used_folder = "/tmp/test_folder1"' in content
|
||||
|
||||
print("--- Test 1 Passed ---")
|
||||
|
||||
# --- Test 2: Relaunch and second operation ---
|
||||
print("\n--- Test 2 ---")
|
||||
# 1. Simulate relaunch (load existing config)
|
||||
config_handler_relaunch = KohyaSSGUIConfig(config_file_path="/app/config.toml")
|
||||
print(f"On relaunch, last_used_folder is: {config_handler_relaunch.get_last_used_folder()}")
|
||||
assert config_handler_relaunch.get_last_used_folder() == "/tmp/test_folder1"
|
||||
|
||||
# 2. Simulate opening the same dialog again. get_folder_path will determine initial_dir.
|
||||
# We are interested in what initial_dir would be.
|
||||
# The common_gui.get_folder_path function has this logic:
|
||||
# initial_dir_to_use = scriptdir
|
||||
# if config:
|
||||
# last_used = config.get_last_used_folder()
|
||||
# if last_used and os.path.isdir(last_used):
|
||||
# initial_dir_to_use = last_used
|
||||
# So, initial_dir_to_use should become /tmp/test_folder1.
|
||||
# (This part is harder to directly assert without refactoring get_folder_path to return initial_dir for testing,
|
||||
# or by checking logs if we had more verbose logging for initial_dir decision)
|
||||
# For now, we trust that get_last_used_folder returning /tmp/test_folder1 means the dialog would use it.
|
||||
|
||||
# 3. Simulate selecting another folder: /tmp/test_folder2
|
||||
returned_path_2 = get_folder_path(folder_path="/tmp/test_folder2", config=config_handler_relaunch)
|
||||
print(f"Returned path from second get_folder_path: {returned_path_2}")
|
||||
print(f"Last used folder after second get_folder_path: {config_handler_relaunch.get_last_used_folder()}")
|
||||
|
||||
# 4. Save config again
|
||||
config_handler_relaunch.save_config(config=config_handler_relaunch.config, config_file_path="/app/config.toml")
|
||||
print("Config saved for second operation.")
|
||||
|
||||
# Verify by reading the file content
|
||||
with open("/app/config.toml", "r") as f:
|
||||
content_2 = f.read()
|
||||
print(f"config.toml content after second operation:\n{content_2}")
|
||||
assert 'last_used_folder = "/tmp/test_folder2"' in content_2
|
||||
|
||||
print("--- Test 2 Passed ---")
|
||||
|
||||
print("\nInitial State Check successful.")
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
import os
|
||||
import sys
|
||||
import unittest.mock as mock
|
||||
|
||||
# Mock UI elements
|
||||
sys.modules['easygui'] = mock.MagicMock()
|
||||
sys.modules['tkinter'] = mock.MagicMock()
|
||||
sys.modules['Tkinter'] = mock.MagicMock()
|
||||
mock_easygui = mock.MagicMock()
|
||||
mock_easygui.msgbox = mock.MagicMock()
|
||||
mock_easygui.ynbox = mock.MagicMock()
|
||||
sys.modules['easygui'] = mock_easygui
|
||||
|
||||
os.environ["COLAB_GPU"] = "True" # Simulate non-interactive environment
|
||||
|
||||
from kohya_gui.class_gui_config import KohyaSSGUIConfig
|
||||
from kohya_gui.common_gui import get_folder_path, scriptdir # Assuming scriptdir is /app
|
||||
|
||||
CONFIG_PATH = "/app/config.toml"
|
||||
|
||||
print(f"Scriptdir is: {scriptdir}")
|
||||
|
||||
# --- Test 3: Corrupted last_used_folder path ---
|
||||
print("\n--- Test 3: Corrupted last_used_folder path ---")
|
||||
# 1. Create config.toml with a corrupted path
|
||||
with open(CONFIG_PATH, "w") as f:
|
||||
f.write('last_used_folder = "this/is/not/a/valid/path"\n')
|
||||
print(f"Created corrupted {CONFIG_PATH}")
|
||||
|
||||
# 2. Load config
|
||||
config_corrupted = KohyaSSGUIConfig(config_file_path=CONFIG_PATH)
|
||||
corrupted_val = config_corrupted.get_last_used_folder()
|
||||
print(f"get_last_used_folder() with corrupted path returned: {corrupted_val}")
|
||||
# get_last_used_folder itself just returns the value if it's a string.
|
||||
assert corrupted_val == "this/is/not/a/valid/path"
|
||||
|
||||
# 3. Simulate a folder operation. common_gui.get_folder_path should handle invalid path gracefully.
|
||||
# It should default to scriptdir because "this/is/not/a/valid/path" is not a valid directory.
|
||||
# We can't directly check initial_dir_to_use without modifying common_gui,
|
||||
# but we can infer it by checking that last_used_folder is NOT updated if the dialog was "cancelled" (empty input path)
|
||||
# and that it IS updated if a new path is "selected".
|
||||
|
||||
# Simulate calling get_folder_path, where it would try to use the corrupted path, fail, and use scriptdir.
|
||||
# If user then cancels (empty string for folder_path), it should not update the corrupted value.
|
||||
# However, our non-dialog mode logic in get_folder_path for COLAB_GPU is:
|
||||
# if folder_path and os.path.isdir(folder_path) and config: config.set_last_used_folder(folder_path)
|
||||
# So, if folder_path is empty, it won't update.
|
||||
returned_path_corr1 = get_folder_path(folder_path="", config=config_corrupted) # Simulate empty input or cancel
|
||||
assert config_corrupted.get_last_used_folder() == "this/is/not/a/valid/path", "Corrupted path should not change on empty selection"
|
||||
print("Graceful handling of corrupted path (no selection) passed.")
|
||||
|
||||
# 4. Simulate selecting a valid folder
|
||||
returned_path_corr2 = get_folder_path(folder_path="/tmp/good_folder", config=config_corrupted)
|
||||
assert returned_path_corr2 == "/tmp/good_folder"
|
||||
assert config_corrupted.get_last_used_folder() == "/tmp/good_folder", "Path should update to /tmp/good_folder"
|
||||
print("Graceful handling of corrupted path (new selection) passed.")
|
||||
|
||||
# 5. Save and verify config file
|
||||
config_corrupted.save_config(config=config_corrupted.config, config_file_path=CONFIG_PATH)
|
||||
with open(CONFIG_PATH, "r") as f:
|
||||
content = f.read()
|
||||
print(f"config.toml content after fixing corrupted path:\n{content}")
|
||||
assert 'last_used_folder = "/tmp/good_folder"' in content
|
||||
print("--- Test 3 Passed ---")
|
||||
|
||||
|
||||
# --- Test 4: last_used_folder key missing ---
|
||||
print("\n--- Test 4: last_used_folder key missing ---")
|
||||
# 1. Create config.toml without the key (or with other keys)
|
||||
with open(CONFIG_PATH, "w") as f:
|
||||
f.write('another_key = "some_value"\n') # No last_used_folder
|
||||
print(f"Created {CONFIG_PATH} with missing last_used_folder key.")
|
||||
|
||||
# 2. Load config. load_config() should add last_used_folder = scriptdir
|
||||
config_missing = KohyaSSGUIConfig(config_file_path=CONFIG_PATH)
|
||||
val_after_load = config_missing.get_last_used_folder()
|
||||
print(f"get_last_used_folder() after loading config with missing key: {val_after_load}")
|
||||
# The get_last_used_folder() itself ensures it returns scriptdir if key is missing or value is bad type,
|
||||
# and load_config also initializes it.
|
||||
assert val_after_load == scriptdir, f"Expected scriptdir, got {val_after_load}"
|
||||
|
||||
# 3. Simulate a folder operation
|
||||
returned_path_miss = get_folder_path(folder_path="/tmp/another_good_folder", config=config_missing)
|
||||
assert returned_path_miss == "/tmp/another_good_folder"
|
||||
assert config_missing.get_last_used_folder() == "/tmp/another_good_folder"
|
||||
print("Graceful handling of missing key (new selection) passed.")
|
||||
|
||||
# 4. Save and verify
|
||||
config_missing.save_config(config=config_missing.config, config_file_path=CONFIG_PATH)
|
||||
with open(CONFIG_PATH, "r") as f:
|
||||
content = f.read()
|
||||
print(f"config.toml content after fixing missing key:\n{content}")
|
||||
assert 'last_used_folder = "/tmp/another_good_folder"' in content
|
||||
assert 'another_key = "some_value"' in content # Ensure other keys are preserved
|
||||
print("--- Test 4 Passed ---")
|
||||
|
||||
print("\nAll Edge Case tests passed.")
|
||||
Loading…
Reference in New Issue