From 36eff170465e35e1b0c211dbe15ae5dde08a1be2 Mon Sep 17 00:00:00 2001 From: Mathias Scheider Date: Thu, 1 Oct 2026 17:18:04 +0200 Subject: [PATCH] filedialogs --- src/utils/filedialogs.py | 44 ++++++++++++++++++++++++++++++++++++++++ test/test_filedialogs.py | 35 ++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 src/utils/filedialogs.py create mode 100644 test/test_filedialogs.py diff --git a/src/utils/filedialogs.py b/src/utils/filedialogs.py new file mode 100644 index 0000000..263d918 --- /dev/null +++ b/src/utils/filedialogs.py @@ -0,0 +1,44 @@ +import tkinter +import tkinter.filedialog + + +def get_dir_path_from_dialog(**kwargs): + """Open a native directory picker and return the selected path. + + All ``kwargs`` are passed through unchanged to + :func:`tkinter.filedialog.askdirectory`. The most useful ones: + + - ``initialdir`` (str): directory the dialog opens in. + - ``title`` (str): title of the dialog window. + - ``mustexist`` (bool, default ``True``): only return existing directories. + + Returns an empty string if the user cancels the dialog. + """ + root = tkinter.Tk() + root.withdraw() + try: + return tkinter.filedialog.askdirectory(**kwargs) + finally: + root.destroy() + + +def get_file_path_from_dialog(**kwargs): + """Open a native file picker and return the selected file path. + + All ``kwargs`` are passed through unchanged to + :func:`tkinter.filedialog.askopenfilename`. The most useful ones: + + - ``initialdir`` (str): directory the dialog opens in. + - ``initialfile`` (str): file pre-selected in the dialog. + - ``filetypes`` (list of (label, pattern) tuples): restrict which files + are shown, e.g. ``[("Text files", "*.txt"), ("All files", "*")]``. + - ``title`` (str): title of the dialog window. + + Returns an empty string if the user cancels the dialog. + """ + root = tkinter.Tk() + root.withdraw() + try: + return tkinter.filedialog.askopenfilename(**kwargs) + finally: + root.destroy() diff --git a/test/test_filedialogs.py b/test/test_filedialogs.py new file mode 100644 index 0000000..857d52a --- /dev/null +++ b/test/test_filedialogs.py @@ -0,0 +1,35 @@ +from unittest import mock + +from utils.filedialogs import get_dir_path_from_dialog, get_file_path_from_dialog + + +def test_get_dir_path_from_dialog_returns_selected_path(): + with mock.patch("tkinter.Tk"), mock.patch( + "tkinter.filedialog.askdirectory", return_value="/tmp/somewhere" + ) as dialog: + result = get_dir_path_from_dialog(initialdir="/tmp", title="Pick") + assert result == "/tmp/somewhere" + dialog.assert_called_once_with(initialdir="/tmp", title="Pick") + + +def test_get_dir_path_from_dialog_returns_empty_string_on_cancel(): + with mock.patch("tkinter.Tk"), mock.patch( + "tkinter.filedialog.askdirectory", return_value="" + ): + assert get_dir_path_from_dialog() == "" + + +def test_get_file_path_from_dialog_returns_selected_path(): + with mock.patch("tkinter.Tk"), mock.patch( + "tkinter.filedialog.askopenfilename", return_value="/tmp/file.txt" + ) as dialog: + result = get_file_path_from_dialog(filetypes=[("Text", "*.txt")]) + assert result == "/tmp/file.txt" + dialog.assert_called_once_with(filetypes=[("Text", "*.txt")]) + + +def test_get_file_path_from_dialog_returns_empty_string_on_cancel(): + with mock.patch("tkinter.Tk"), mock.patch( + "tkinter.filedialog.askopenfilename", return_value="" + ): + assert get_file_path_from_dialog() == ""