tkinter-to-customtkinter-converter is a beta migration assistant for converting reviewable tkinter source files into customtkinter code.
It is designed for real project migrations, not blind search-and-replace:
- supported patterns are rewritten structurally with LibCST
- unsupported or ambiguous patterns are preserved with diagnostics and
# TODO tk2ctk:comments - files are classified as
safe,review, orunsafeso you can decide what to apply automatically
This is still a beta tool. It should be used to accelerate migration, not to remove code review.
- converts supported
tkinterandttkwidget construction patterns tocustomtkinter - rewrites imports, subclass bases, and supported
.config()calls - preserves comments and source structure as much as practical
- emits human-readable and JSON reports for file-level and project-level review
- supports preview, diff, dry-run, output directory, recursive traversal, backups, and batch processing
- it does not promise a full semantic port of every
tkinterapp - it does not invent replacements for unsupported widgets
- it does not reliably resolve heavily dynamic runtime behavior
- it does not make styling, theme, or layout decisions beyond safe structural rewrites
- it does not guarantee that a converted file is ready to run without review
Python 3.11+ is required.
Install the beta from a checkout:
git clone https://github.com/Donny-GUI/tkinter-to-customtkinter-converter.git
cd tkinter-to-customtkinter-converter
python -m pip install .Install with test dependencies for local development:
python -m pip install -e .[dev]Verify the CLI is available:
tk2ctk --versionPreview a single file:
tk2ctk app.py --dry-run --diff --reportConvert a single file to a separate output:
tk2ctk app.py -o converted_app.pyConvert a project tree into a parallel directory:
tk2ctk src --recursive --output-dir converted_src --report --json-report report.jsonApply safe in-place conversion with backups:
tk2ctk src --recursive --in-place --backup --only-safe --reportFail CI if any file is classified as unsafe:
tk2ctk src --recursive --dry-run --report --fail-on-unsafeRecommended first pass:
tk2ctk src --recursive --dry-run --diff --report --json-report report.jsonThat gives you:
- unified diffs without writing files
- warnings with file and line locations
- per-file
safe/review/unsafeclassification - machine-readable output for batch inspection
Each processed file is classified as:
safe: converted with no warnings or only non-impacting informational notesreview: converted, but emitted warnings or preserved unsupported constructs within configured thresholdsunsafe: parse/transform failure, or warning volume above threshold, or unsupported-widget volume above threshold
Useful CLI controls:
--max-warnings N--max-unsupported N--fail-on-unsafe--fail-on-review--only-safe--only-review--only-unsafe--allow-unsafe-write
Default behavior is conservative:
- preview and reporting are available for all files
- in-place writes are blocked for
unsafefiles unless--allow-unsafe-writeis set
These are converted when the source pattern is statically recognizable:
tk.Tk->ctk.CTktk.Toplevel->ctk.CTkTopleveltk.Frame,ttk.Frame->ctk.CTkFrametk.Label,ttk.Label->ctk.CTkLabeltk.Button,ttk.Button->ctk.CTkButtontk.Entry,ttk.Entry->ctk.CTkEntrytk.Checkbutton,ttk.Checkbutton->ctk.CTkCheckBoxtk.Radiobutton,ttk.Radiobutton->ctk.CTkRadioButtontk.Scrollbar,ttk.Scrollbar->ctk.CTkScrollbartk.Text->ctk.CTkTextboxttk.Combobox->ctk.CTkComboBoxtk.Scale,ttk.Scale->ctk.CTkSlidertk.OptionMenu->ctk.CTkOptionMenufor the standard positional signaturettk.Progressbar->ctk.CTkProgressBarwhen normalization can be computed safelytk.Listbox-> preserved by default, or converted using an inlineCTkListboxshim with--listboxes
Other supported patterns:
import tkinter as tkfrom tkinter import ...- mixed
tkinterandttkimports - subclass bases like
class App(tk.Tk) .config(...)to.configure(...)on converted widgets- simple dynamic widget references such as
widgets = [tk.Button, tk.Label]
These are not claimed as converted. They remain tkinter or ttk code with explicit diagnostics:
tk.Menutk.Menubuttontk.Canvastk.Messagetk.PanedWindowtk.LabelFramettk.Treeviewttk.Separatorttk.Notebookttk.Panedwindowttk.Labelframe- wildcard imports such as
from tkinter import * - ambiguous
OptionMenusignatures Progressbarpatterns that depend on runtime stateScale.resolutionvalues that cannot be mapped safely
Write a machine-readable report:
tk2ctk src --recursive --dry-run --json-report report.jsonWrite the JSON report to stdout:
tk2ctk app.py --dry-run --json-report -Reports include:
- file risk class
- warning counts by severity
- unsupported widget counts
- widgets converted by type
- unsupported widgets by type
- warnings by category
- files scanned, changed, skipped, and failed
- Run
tk2ctk src --recursive --dry-run --diff --report --json-report report.json. - Review
unsafefiles first. Do not batch-apply them blindly. - Review
reviewfiles for preserved widgets, dropped options, and TODO comments. - Convert
safefiles into an output directory with--only-safe --output-dir converted_src. - For files you are comfortable applying directly, use
--in-place --backup. - Run your project tests and manually verify the UI.
tk2ctk [TARGET] [options]
Key options:
--version-o, --outfile--output-dir DIR--in-place--recursive--include GLOB--exclude GLOB--dry-run--diff--backup--listboxes--report--json-report [PATH]--max-warnings N--max-unsupported N--fail-on-unsafe--fail-on-review--only-safe--only-review--only-unsafe--allow-unsafe-write
from tk_to_ctk.converter import SourceConverter
converter = SourceConverter(convert_listboxes=False)
converted = converter.from_string(source_text)Lower-level service API:
from tk_to_ctk.models import ConversionOptions
from tk_to_ctk.service import convert_source, convert_file, convert_paths
result = convert_source(source_text, ConversionOptions(report=True))
file_result = convert_file("input.py", "output.py")
project_report = convert_paths(
["src"],
ConversionOptions(recursive=True, output_dir="converted_src", report=True),
)Be explicit about these limits when using the tool:
- unsupported widgets are preserved, not solved
- wildcard imports remain intentionally conservative
- dynamic aliasing,
exec, string-built code, and complex runtime widget factories are outside reliable static analysis safemeans low-risk structurally, not “no human review needed”- files with heavy mixed
tkinterand already-convertedcustomtkintercode can still produce noisy but correct warnings
This project is ready for beta usage as a migration assistant, but the output should still be reviewed before shipping.