diff --git a/Launcher.py b/Launcher.py index 3c1812bb..5bacbb12 100644 --- a/Launcher.py +++ b/Launcher.py @@ -20,7 +20,6 @@ from Utils import is_frozen, user_path, local_path, init_logging, open_filename, from shutil import which import shlex from enum import Enum, auto -import logging def open_host_yaml(): @@ -38,22 +37,16 @@ def open_host_yaml(): def open_patch(): + suffixes = [] + for c in components: + if isfile(get_exe(c)[-1]): + suffixes += c.file_identifier.suffixes if c.type == Type.CLIENT and \ + isinstance(c.file_identifier, SuffixIdentifier) else [] try: - import tkinter - import tkinter.filedialog + filename = open_filename('Select patch', (('Patches', suffixes),)) except Exception as e: - logging.error("Could not load tkinter, which is likely not installed. " - "This attempt was made because Launcher.open_patch was used.") - raise e + messagebox('Error', str(e), error=True) else: - root = tkinter.Tk() - root.withdraw() - suffixes = [] - for c in components: - if isfile(get_exe(c)[-1]): - suffixes += c.file_identifier.suffixes if c.type == Type.CLIENT and \ - isinstance(c.file_identifier, SuffixIdentifier) else [] - filename = tkinter.filedialog.askopenfilename(filetypes=(('Patches', ' '.join(suffixes)),)) file, _, component = identify(filename) if file and component: launch([*get_exe(component), file], component.cli) diff --git a/MultiServer.py b/MultiServer.py index 8d1a48a7..6e4638bd 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -1945,18 +1945,8 @@ async def main(args: argparse.Namespace): try: if not data_filename: - try: - import tkinter - import tkinter.filedialog - except Exception as e: - logging.error("Could not load tkinter, which is likely not installed. " - "This attempt was made because no .archipelago file was provided as argument. " - "Either provide a file or ensure the tkinter package is installed.") - raise e - else: - root = tkinter.Tk() - root.withdraw() - data_filename = tkinter.filedialog.askopenfilename(filetypes=(("Multiworld data", "*.archipelago *.zip"),)) + filetypes = (("Multiworld data", (".archipelago", ".zip")),) + data_filename = Utils.open_filename("Select multiworld data", filetypes) ctx.load(data_filename, args.use_embedded_options) diff --git a/Utils.py b/Utils.py index 6b18dc89..3a199227 100644 --- a/Utils.py +++ b/Utils.py @@ -525,6 +525,36 @@ def get_fuzzy_results(input_word: str, wordlist: typing.Sequence[str], limit: ty ) +def open_filename(title: str, filetypes: typing.Sequence[typing.Tuple[str, typing.Sequence[str]]]) \ + -> typing.Optional[str]: + 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 + kdialog = shutil.which('kdialog') + if kdialog: + k_filters = '|'.join((f'{text} (*{" *".join(ext)})' for (text, ext) in filetypes)) + return run(kdialog, f'--title={title}', '--getopenfilename', '.', k_filters) + zenity = shutil.which('zenity') + if zenity: + z_filters = (f'--file-filter={text} ({", ".join(ext)}) | *{" *".join(ext)}' for (text, ext) in filetypes) + return run(zenity, f'--title={title}', '--file-selection', *z_filters) + + # fall back to tk + try: + import tkinter + import tkinter.filedialog + except Exception as e: + logging.error('Could not load tkinter, which is likely not installed. ' + f'This attempt was made because open_filename was used for "{title}".') + raise e + else: + root = tkinter.Tk() + root.withdraw() + return tkinter.filedialog.askopenfilename(title=title, filetypes=((t[0], ' '.join(t[1])) for t in filetypes)) + + def messagebox(title: str, text: str, error: bool = False) -> None: def is_kivy_running(): if 'kivy' in sys.modules: