""" Native Windows file/folder picker, used by the Kivy GUI. Kivy's built-in FileChooserListView has no address bar, no Quick Access and awkward keyboard navigation, which makes finding a file on a real PC painful. This shells out to the standard Windows dialog instead. It runs in a **separate process** on purpose: Tk wants to own the main event loop, and Kivy's SDL2 loop is already running in the parent. Isolating Tk in a child process avoids that conflict entirely, and the result comes back on stdout. Usage: python native_file_dialog.py [--dir START_DIR] [--save] [--folder] [--title T] Output (one line on stdout): SELECTED: on success CANCELLED if the user dismissed the dialog """ from __future__ import annotations import argparse import os import sys IMAGE_TYPES = [ ("Image files", "*.png;*.jpg;*.jpeg;*.bmp;*.gif;*.tif;*.tiff;*.webp"), ("PNG", "*.png"), ("JPEG", "*.jpg;*.jpeg"), ("Bitmap", "*.bmp"), ("All files", "*.*"), ] def _default_dir(start: str | None) -> str: """Pick a sensible starting folder.""" if start and os.path.isdir(start): return start if start and os.path.isfile(start): return os.path.dirname(start) # Prefer the user's Pictures folder, then their home directory. home = os.path.expanduser("~") pictures = os.path.join(home, "Pictures") if os.path.isdir(pictures): return pictures return home if os.path.isdir(home) else os.getcwd() def main() -> int: parser = argparse.ArgumentParser(description="Native file picker") parser.add_argument("--dir", default=None, help="initial directory or file") parser.add_argument("--title", default="Select a file") parser.add_argument( "--folder", action="store_true", help="pick a folder instead of a file" ) parser.add_argument( "--save", action="store_true", help="use the save-as dialog" ) args = parser.parse_args() try: import tkinter from tkinter import filedialog except ImportError: print("ERROR:tkinter is not available in this Python installation", file=sys.stderr) return 2 root = tkinter.Tk() root.withdraw() # Keep the dialog on top of the Kivy window rather than behind it. root.attributes("-topmost", True) root.update() start = _default_dir(args.dir) try: if args.folder: result = filedialog.askdirectory( title=args.title, initialdir=start, mustexist=True ) elif args.save: result = filedialog.asksaveasfilename( title=args.title, initialdir=start, defaultextension=".png", filetypes=IMAGE_TYPES, ) else: result = filedialog.askopenfilename( title=args.title, initialdir=start, filetypes=IMAGE_TYPES ) finally: try: root.destroy() except tkinter.TclError: pass if result: print(f"SELECTED:{os.path.normpath(result)}") else: print("CANCELLED") return 0 if __name__ == "__main__": raise SystemExit(main())