Browse File or Folder in Tk (tkinter)

/images/browse-file-or-folder-in-tk-tkinter/tkinter.filedialog.png

Tk provides functions to display dialogs for browsing files or folders on Windows, Linux (or any other Unix-based system) and macOS. In Python those functions are accessible through the tkinter.filedialog standard module. The most important ones are:

  • askopenfilename() / askopenfilenames()

  • askdirectory()

  • asksaveasfilename()

The askopenfilename() and askopenfilenames() functions display a dialog to open one or more files. askdirectory() does the same for browsing a folder (it is not possible to select more than one folder). asksaveasfilename() displays a dialog similar to the first two functions, but to save a file instead of opening it. We will see their differences in a moment.

On Windows and macOS these functions work internally with native dialogs provided by the OS, which ensures that you always get an up-to-date and modern tool for browsing files or folders. On the Microsoft system, Tk makes use of the IFileDialog, IFileOpenDialog and IFileSaveDialog native interfaces available starting with Windows Vista. On Windows XP and earlier, the GetOpenFileName() and GetSaveFileName() functions are used. The implementation can be seen in the tkWinDialog.c file of the Tk source code. On macOS, Tk also works with the NSOpenPanel and NSSavePanel native interfaces. The implementation is available in the tkMacOSXDialog.c file (written in Objective-C). On Unix systems the dialogs simulate the Windows (95) window and are written directly in Tcl in the tkfbox.tcl file.

All functions have a similar interface. They can be called without arguments and return a string (with the exception of askopenfilenames(), which returns a tuple of strings). For example, to display a dialog to open a file, use:

from tkinter import filedialog
# Display the dialog for browsing files.
filename = filedialog.askopenfilename()
# Print the selected file path.
print(filename)

The dialog allows you to walk through the entire file system and select a file from it. The dialog texts are displayed in the language configured in the operating system. The path of the file selected by the user is returned as a string and stored, in this case, in the filename variable. If the user cancels or closes the dialog, the result is an empty string or tuple (depending on the operating system), so you'll probably want to do add a conditional after calling the function. Since both empty tuples and empty strings evaluate to False, it is not necessary to distinguish one case from the other. For example:

from pathlib import Path
from tkinter import filedialog
filename = filedialog.askopenfilename()
if filename:
    # Read and print the content (in bytes) of the file.
    print(Path(filename).read_bytes())
else:
    print("No file selected.")

The askopenfilenames() function (note the "s" at the end) allows the user to select more than one file in the same folder. The result is in this case a tuple containing the paths of the selected files. Here, too, an empty string or tuple is returned when the dialog is cancelled.

filenames = filedialog.askopenfilenames()
if filenames:
    print("Selected files:")
    for filename in filenames:
        print(filename)
else:
    print("No files selected.")

askdirectory() is similar to askopenfilename(), but only allows you to select folders:

directory = filedialog.askdirectory()
print(directory)

Note that in these three functions the selected folders or files must exist. If the user types a non-existent file or folder name, the dialog displays an error message.

The asksaveasfilename() function, on the other hand, is used to select a file that doesn't exist yet by typing its name into the textbox, typically to implement a file save feature. If the selected file already exists, the dialog displays a message to the user asking for his confirmation to replace the file. The return value has the same behavior as in askopenfilename().

# File save dialog.
filename = filedialog.asksaveasfilename()
print(filename)

By default, the dialogs prompted by the three file-browse functions show all types of files. You can configure the dialog to show only certain types of files by using the filetype argument:

filename = filedialog.askopenfilename(
    filetypes=(
        ("Text files", "*.txt"),
        ("Python Files", ("*.py", "*.pyx")),
        ("All Files", "*.*")
    )
)

The argument receives a tuple of 2-tuples. The first element of the inner tuples specifies the name of the file type that will be displayed to the user. The second can be a string or a tuple of strings containing the patterns to be used. For example, the *.txt string shows only files ending with .txt, while the ("*.py", "*.pyx") tuple displays files ending with both .py or .pyx. The *.* pattern is used to display files with any name and extension. When the filetypes argument is present, the dialog includes a dropdown list to show certain file types:

/images/browse-file-or-folder-in-tk-tkinter/tkinter.filedialog.gif

In these four functions the intialdir argument can be passed to define the initial path in which the dialog will appear. For example:

# Initialize in the drive root.
filename = filedialog.askopenfilename(initialdir="C:/")

Or to initialize in the directory where the Python interpreter is located:

from pathlib import Path
from tkinter import filedialog
import sys
filename = filedialog.askopenfilename(
    initialdir=Path(sys.executable).parent
)

If intialdir is not specified, the initial path depends on the operating system. On Unix and macOS systems it is the current working directory, which is usually where your Python file is located. On Windows it is the last directory from which a file was successfully selected in that same application. On macOS sometimes the value of initialdir is ignored, depending on user settings.

Finally, the title and parent window of the dialog can be specified via the title and parent parameters, respectively. The dialog closes automatically if the parent window is destroyed.

from tkinter import filedialog
import tkinter as tk
root = tk.Tk()
root.title("Main Window")
toplevel = tk.Toplevel()
toplevel.title("Secondary Window")
filename = filedialog.askopenfilename(
    parent=toplevel,
    title="Browse File"
)
root.mainloop()