Fix, OptionsCreator: export options on Linux (#5774)

* Core/Utils: Use correct env for save_filename from AppImage

* OptionsCreator: run export on a separate thread

Running a blocking call from kivy misbehaves on Linux.
This also changes '*.yaml' to '.yaml' for Utils.save_filename,
which is the correct way to call it.

* Core/Utils: destroy Tk root after save/open_filename

This allows using those functions from multiple threads.
Note that pure Tk apps should not use those functions from Utils.

* OptionsCreator: show snack when save_filename fails

* OptionsCreator: disable window while exporting

* OptionsCreator: fixing typing of added stuff
This commit is contained in:
black-sliver
2026-02-01 21:23:14 +00:00
committed by GitHub
parent c835bff570
commit 4cb518930c
5 changed files with 142 additions and 27 deletions

View File

@@ -811,29 +811,32 @@ def open_filename(title: str, filetypes: typing.Iterable[typing.Tuple[str, typin
except tkinter.TclError:
return None # GUI not available. None is the same as a user clicking "cancel"
root.withdraw()
return tkinter.filedialog.askopenfilename(title=title, filetypes=((t[0], ' '.join(t[1])) for t in filetypes),
initialfile=suggest or None)
try:
return tkinter.filedialog.askopenfilename(
title=title,
filetypes=((t[0], ' '.join(t[1])) for t in filetypes),
initialfile=suggest or None,
)
finally:
root.destroy()
def save_filename(title: str, filetypes: typing.Iterable[typing.Tuple[str, typing.Iterable[str]]], suggest: str = "") \
-> typing.Optional[str]:
logging.info(f"Opening file save dialog for {title}.")
def run(*args: str):
return subprocess.run(args, capture_output=True, text=True).stdout.split("\n", 1)[0] or None
if is_linux:
# prefer native dialog
from shutil import which
kdialog = which("kdialog")
if kdialog:
k_filters = '|'.join((f'{text} (*{" *".join(ext)})' for (text, ext) in filetypes))
return run(kdialog, f"--title={title}", "--getsavefilename", suggest or ".", k_filters)
return _run_for_stdout(kdialog, f"--title={title}", "--getsavefilename", suggest or ".", k_filters)
zenity = which("zenity")
if zenity:
z_filters = (f'--file-filter={text} ({", ".join(ext)}) | *{" *".join(ext)}' for (text, ext) in filetypes)
selection = (f"--filename={suggest}",) if suggest else ()
return run(zenity, f"--title={title}", "--file-selection", "--save", *z_filters, *selection)
return _run_for_stdout(zenity, f"--title={title}", "--file-selection", "--save", *z_filters, *selection)
# fall back to tk
try:
@@ -856,8 +859,14 @@ def save_filename(title: str, filetypes: typing.Iterable[typing.Tuple[str, typin
except tkinter.TclError:
return None # GUI not available. None is the same as a user clicking "cancel"
root.withdraw()
return tkinter.filedialog.asksaveasfilename(title=title, filetypes=((t[0], ' '.join(t[1])) for t in filetypes),
initialfile=suggest or None)
try:
return tkinter.filedialog.asksaveasfilename(
title=title,
filetypes=((t[0], ' '.join(t[1])) for t in filetypes),
initialfile=suggest or None,
)
finally:
root.destroy()
def _mp_open_directory(res: "multiprocessing.Queue[typing.Optional[str]]", *args: Any) -> None: