CTkFileDialog
A modern and fully customizable File Dialog for CustomTkinter — a must-have extension pack!
🚀 Features
- 🔍 Autocomplete in the path entry field (with
Tab,Up, andDown) - 🧩 Flexible filetype filters: accepts simple extensions or tkinter-style
(label, pattern)tuples (patterns may contain multiple space‑separated globs). Wildcards like*/*.*are treated as match‑all. - � Search/filter files in real-time
- 🖼️ Live image preview
- 🎥 Video thumbnail preview
- 📁 Directory selection (single or multiple)
- 📄📁 Combined file-or-folder selection (single or multiple)
- ➕ Create new folders from within the dialog (
foldercreation) - 💾 Save file dialog (return path or open file), with automatic extension (
defaultext) - ❔ Tooltip support
- 🖥️ Shell Path Syntax Support
- ⌨️ Backspace using Alt + Left Arrow shortcut
- 👀 Multiple view modes (Grid and List view in Default dialog)
- 📊 Sort files by name, date, size, type, or last modified
- 💡 Data type validation at runtime and for static type analyzers
📦 Installation
# Using bash
git clone https://github.com/hmidani-abdelilah/CTkFileDialog-plus
cd CTkFileDialog-plus
python3 -m venv .venv
source .venv/bin/activate
pip3 install -r requirements.txt
# On Windows
git clone https://github.com/hmidani-abdelilah/CTkFileDialog-plus
cd CTkFileDialog-plus
python3 -m venv .venv
.\.venv\Scripts\activate.ps1 # In PowerShell
pip3 install -r requirements.txt
# Or using pip
python3 -m venv .venv
source .venv/bin/activate # On PowerShell use .\.venv\Scripts\activate.ps1
pip3 install CTkFileDialog-plus
📁 Project structure
CTkFileDialog-plus/
├── LICENSE # project license terms
├── README.md # user and developer documentation
├── requirements.txt # Python dependencies
├── test.py # general package usage example
├── test_save_as.py # save-as dialog regression/test example
├── CTkFileDialog/ # package source code
│ ├── __init__.py # public package API
│ ├── _functions.py # dialog helper functions exposed to users
│ ├── _system.py # cross-platform path handling support
│ ├── Dialog.py # dialog imports and public component exports
│ ├── Constants/ # reusable package constants
│ │ ├── __init__.py
│ │ └── constants.py
│ ├── core/ # core filesystem/search/sorting logic
│ │ ├── __init__.py
│ │ ├── filesystem.py
│ │ ├── search.py
│ │ └── sorting.py
│ ├── icons/ # icon assets for the dialogs
│ │ ├── _IconsMini/
│ │ └── *.png
│ ├── preview/ # image/video preview helpers
│ │ ├── __init__.py
│ │ └── media.py
│ ├── resources/ # shared resource loading utilities
│ │ ├── __init__.py
│ │ └── icons.py
│ ├── system/ # platform-specific system utilities
│ │ ├── __init__.py
│ │ └── platform.py
│ ├── ui/ # dialog UI implementations and widgets
│ │ ├── __init__.py
│ │ ├── default_dialog.py
│ │ ├── mini_dialog.py
│ │ └── tooltip.py
│ └── utils/ # small helper utilities
│ ├── __init__.py
│ └── helpers.py
├── Images/ # README screenshots showing light and dark themes
│ ├── MiniDialogDark.png
│ ├── MiniDialogLight.png
│ ├── NormalDialogDark.png
│ └── NormalDialogLight.png
🧪 Demo — All Methods
🗂️ Open File
import customtkinter as ctk
from CTkFileDialog import askopenfilename
ctk.set_appearance_mode("Dark")
ctk.set_default_color_theme("blue")
def open_file():
# you can pass simple extensions...
# path = askopenfilename(preview_img=True, autocomplete=True)
# or tkinter‑style tuples with labels and multiple globs:
path = askopenfilename(
filetypes=[
("Text files","*.txt"),
("Python","*.py *.pyw"),
("All","*.*"),
],
preview_img=True,
autocomplete=True,
)
if path:
result_label.configure(text=f"Selected file:\n{path}")
app = ctk.CTk()
app.title("askopenfilename Demo")
app.geometry("500x200")
ctk.CTkButton(app, text="Open File", command=open_file).pack(pady=20)
result_label = ctk.CTkLabel(app, text="Waiting for file selection...")
result_label.pack()
app.mainloop()
🗂️ Open Multiple Files
import customtkinter as ctk
from CTkFileDialog import askopenfilenames
ctk.set_appearance_mode("Dark")
ctk.set_default_color_theme("blue")
def open_files():
paths = askopenfilenames(
filetypes=[("Images","*.jpg *.png"), ("All","*.*")],
preview_img=True,
autocomplete=True,
)
if paths:
result_label.configure(text="Selected files:\n" + "\n".join(paths))
app = ctk.CTk()
app.title("askopenfilenames Demo")
app.geometry("500x300")
ctk.CTkButton(app, text="Open Multiple Files", command=open_files).pack(pady=20)
result_label = ctk.CTkLabel(app, text="Waiting for file selection...", wraplength=450)
result_label.pack()
app.mainloop()
📁 Select Directory
import customtkinter as ctk
from CTkFileDialog import askdirectory
ctk.set_appearance_mode("Dark")
ctk.set_default_color_theme("blue")
def select_directory():
folder = askdirectory(autocomplete=True)
if folder:
result_label.configure(text=f"Selected directory:\n{folder}")
app = ctk.CTk()
app.title("askdirectory Demo")
app.geometry("500x200")
ctk.CTkButton(app, text="Select Directory", command=select_directory).pack(pady=20)
result_label = ctk.CTkLabel(app, text="Waiting for directory selection...")
result_label.pack()
app.mainloop()
📁 Select Single Directory (askopendirname)
import customtkinter as ctk
from CTkFileDialog import askopendirname
ctk.set_appearance_mode("Dark")
ctk.set_default_color_theme("blue")
def select_directory():
folder = askopendirname(autocomplete=True)
if folder:
result_label.configure(text=f"Selected directory:\n{folder}")
app = ctk.CTk()
app.title("askopendirname Demo")
app.geometry("500x200")
ctk.CTkButton(app, text="Select Directory", command=select_directory).pack(pady=20)
result_label = ctk.CTkLabel(app, text="Waiting for directory selection...")
result_label.pack()
app.mainloop()
📁 Select Multiple Directories (askopendirnames)
import customtkinter as ctk
from CTkFileDialog import askopendirnames
ctk.set_appearance_mode("Dark")
ctk.set_default_color_theme("blue")
def select_directories():
# Hold Ctrl or Shift while clicking to select several folders
folders = askopendirnames(autocomplete=True)
if folders:
result_label.configure(text="Selected directories:\n" + "\n".join(folders))
app = ctk.CTk()
app.title("askopendirnames Demo")
app.geometry("500x300")
ctk.CTkButton(app, text="Select Directories", command=select_directories).pack(pady=20)
result_label = ctk.CTkLabel(app, text="Waiting for directory selection...", wraplength=450)
result_label.pack()
app.mainloop()
📄📁 Select a File or a Folder (askopenpathname)
import customtkinter as ctk
from CTkFileDialog import askopenpathname
ctk.set_appearance_mode("Dark")
ctk.set_default_color_theme("blue")
def select_path():
path = askopenpathname(autocomplete=True)
if path:
result_label.configure(text=f"Selected:\n{path}")
app = ctk.CTk()
app.title("askopenpathname Demo")
app.geometry("500x200")
ctk.CTkButton(app, text="Select File or Folder", command=select_path).pack(pady=20)
result_label = ctk.CTkLabel(app, text="Waiting for selection...")
result_label.pack()
app.mainloop()
📄📁 Select Multiple Files and/or Folders (askopenpathnames)
import customtkinter as ctk
from CTkFileDialog import askopenpathnames
ctk.set_appearance_mode("Dark")
ctk.set_default_color_theme("blue")
def select_paths():
# Hold Ctrl or Shift while clicking to mix files and folders in one selection
items = askopenpathnames(autocomplete=True)
if items:
result_label.configure(text="Selected:\n" + "\n".join(items))
app = ctk.CTk()
app.title("askopenpathnames Demo")
app.geometry("500x300")
ctk.CTkButton(app, text="Select Files/Folders", command=select_paths).pack(pady=20)
result_label = ctk.CTkLabel(app, text="Waiting for selection...", wraplength=450)
result_label.pack()
app.mainloop()
import customtkinter as ctk
from CTkFileDialog import asksaveasfilename
ctk.set_appearance_mode("Dark")
ctk.set_default_color_theme("blue")
def save_as_filename():
path = asksaveasfilename(autocomplete=True)
if path:
result_label.configure(text=f"Save file as:\n{path}")
# You can also auto-append an extension if the user doesn't type one:
# path = asksaveasfilename(autocomplete=True, defaultext=".txt")
app = ctk.CTk()
app.title("asksaveasfilename Demo")
app.geometry("500x200")
ctk.CTkButton(app, text="Save As (Filename Only)", command=save_as_filename).pack(pady=20)
result_label = ctk.CTkLabel(app, text="Waiting for filename...")
result_label.pack()
app.mainloop()
💾 Save As (write to file)
import customtkinter as ctk
from CTkFileDialog import asksaveasfile
ctk.set_appearance_mode("Dark")
ctk.set_default_color_theme("blue")
def save_as_file():
file = asksaveasfile(autocomplete=True)
if file:
file.write("This file was created using the demo.")
file.close()
result_label.configure(text=f"File saved:\n{file.name}")
# Alternatively, asksaveasfile supports the context manager protocol:
# with asksaveasfile(autocomplete=True) as file:
# if file:
# file.write("This file was created using the demo.")
app = ctk.CTk()
app.title("asksaveasfile Demo")
app.geometry("500x200")
ctk.CTkButton(app, text="Save As (Real File)", command=save_as_file).pack(pady=20)
result_label = ctk.CTkLabel(app, text="Waiting for save location...")
result_label.pack()
app.mainloop()
📂 Open File as Object
import customtkinter as ctk
from CTkFileDialog import askopenfile
ctk.set_appearance_mode("Dark")
ctk.set_default_color_theme("blue")
def open_file_obj():
f = askopenfile(mode='rb',
filetypes=[("Binary","*.bin"), ("All","*.*")])
if f:
data = f.read()
f.close()
result_label.configure(text=f"Read {len(data)} bytes from {f.name}")
app = ctk.CTk()
app.title("askopenfile Demo")
app.geometry("500x200")
ctk.CTkButton(app, text="Open File Object", command=open_file_obj).pack(pady=20)
result_label = ctk.CTkLabel(app, text="Waiting for file selection...")
result_label.pack()
app.mainloop()
📂 Open Multiple Files as Objects
import customtkinter as ctk
from CTkFileDialog import askopenfiles
ctk.set_appearance_mode("Dark")
ctk.set_default_color_theme("blue")
def open_files_obj():
files = askopenfiles(mode='r', filetypes=[("Text","*.txt")])
if files:
for f in files:
content = f.read()
f.close()
print(f"{f.name}: {len(content)} chars")
result_label.configure(text=f"Opened {len(files)} files, see console output.")
app = ctk.CTk()
app.title("askopenfiles Demo")
app.geometry("500x200")
ctk.CTkButton(app, text="Open Files Objects", command=open_files_obj).pack(pady=20)
result_label = ctk.CTkLabel(app, text="Waiting for file selection...")
result_label.pack()
app.mainloop()
🧩 Parameters
| Parameter | Description |
|---|---|
filetypes |
Optional filter list: either ['.txt', '.md'] or [('Text','*.txt'),('Py','*.py *.pyw'),('All','*.*')]. Wildcards are normalized internally. |
hidden |
Show hidden files or directories (False by default). |
preview_img |
Enable image preview in the file dialog. |
video_preview |
Show first frame of video files as thumbnail (experimental). |
autocomplete |
Enable path autocompletion with Tab, Up, and Down. |
initial_dir |
Set the initial directory when opening the dialog. |
tool_tip |
Enable the tool tip. |
foldercreation |
Enable a "New Folder" button so the user can create folders from within the dialog (True by default). |
defaultext |
(asksaveasfilename/asksaveasfile only) Extension appended to the filename if the user didn't type one (default is none). |
style |
Defines the dialog style, by default it will be 'Default' but you can choose a small one ('Mini'). |
geometry |
Defines the dialog size. For functions that support both styles, pass a tuple (default_geometry, mini_geometry). |
title |
Define the title for the dialog window. Defaults to CTkFileDialog. |
🎯 Using New Features
🔎 Search/Filter Files
Both Default and Mini dialogs include a search bar. Simply type in the search field to filter files and directories in real-time:
file = askopenfilename(style='Default')
# Use the search bar in the dialog to filter results
👀 View Modes (Default Dialog Only)
The Default dialog supports two view modes:
- Grid View: Icon-based grid layout (default)
- List View: Detailed list with file info (size, date modified, type)
Toggle between modes using the view buttons in the toolbar.
📊 Sorting Options (Default Dialog Only)
Sort files by:
- Name (alphabetical)
- Date (creation/modification time)
- Type (file extension)
- Size (file size)
- Last Modified (most recent first)
Select a sort option from the sort menu in the toolbar.
This module has constants that can be used outside or inside the dialog, they are used to obtain paths like /home/user or /home/user/.config/ Here is a basic example
#!/usr/bin/env python3
import customtkinter as ctk
from CTkFileDialog import askopenfilename
from CTkFileDialog.Constants import HOME
root = ctk.CTk()
def open_file():
f = askopenfilename(initial_dir=HOME, autocomplete=True)
if f:
print(f"file selected: {f}")
ctk.CTkButton(master=root, command=open_file, text="Open File").pack(padx=10, pady=10, anchor=ctk.CENTER)
root.mainloop()
And here are the constants available from the package
| Parameter | Description |
|---|---|
PWD |
Current working directory (e.g., where the program was launched) |
HOME |
User's home directory (e.g., /home/user or C:\Users\user) |
TEMP |
Temporary directory (fallback to /tmp if no env vars are set) |
CONFIG_DIR |
XDG-compliant user configuration directory (default: ~/.config) |
CACHE_DIR |
XDG-compliant user cache directory (default: ~/.cache) |
PATH |
System PATH split into a list of directories |
DATA_DIR |
XDG-compliant user data directory (default: ~/.local/share) |
VENV |
Active Python virtual environment (venv or conda), fallback to PWD |
DOWNLOAD_DIR |
Default download directory (e.g., ~/Downloads or C:\Users\user\Download). |
DOWNLOAD_DIR_VIDEO |
Default video download directory (e.g., ~/Downloads/Video or C:\Users\user\Download\Video). |
🌙 Dark Mode Normal
☀️ Light Mode Preview
🌙 Dark Mode Mini
☀️ Light Mode Mini
The mini design wasn't made by me; it was made by this user. Additionally, I did not design the normal interface; it was developed by this user, and they deserve all the credit. I would like to express my gratitude to them for developing that design beforehand.I revamped the interface, established a new framework, and incorporated additional features.
Metadata
Release files for CTKFileDialog-plus 2.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| ctkfiledialog_plus-2.2.0.tar.gz | 224.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ctkfiledialog_plus-2.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 461.9 kB
Release files / ctkfiledialog_plus-2.2.0.tar.gz
| Download URL | ctkfiledialog_plus-2.2.0.tar.gz |
|---|---|
| Size | 224.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e68d95266f856d7f7b4f12318869d1a0db58092d799836378696f308060fafbc
|
|
BLAKE2b-256 checksum How to use checksums |
da1bf7f599908de32117ad757dd2004d1b6ccd31b9351cc362f66823262f6a96
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / ctkfiledialog_plus-2.2.0-py3-none-any.whl
| Download URL | ctkfiledialog_plus-2.2.0-py3-none-any.whl |
|---|---|
| Size | 237.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ef21db0ec6094a3dd44d8d5b4db13389400acd80eb060d40422c1be7b45a34f3
|
|
BLAKE2b-256 checksum How to use checksums |
b6abaa0d1245617e508efb4605858e1ead8fb2dc3a24b8dfee27c31a21f84168
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Security for CTKFileDialog-plus 2.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.