PyTkQuickGui is a visual drag-and-drop builder for Python desktop interfaces using tkinter and ttkbootstrap. Design a window on the live canvas, edit widget attributes and layout, save the project as JSON, then generate a readable Python program.
The project is approaching beta. Grid and Place projects are usable and under active testing; Pack remains disabled while the other two geometry managers are stabilised.
Seven ready made projects ship in examples/ - INSTALL.TXT has the
one line that copies them into the tool's directory. Each of these is the same
project as rendered by the generated ttkbootstrap program and by the generated
Flet program:
| ttkbootstrap output | Flet output |
|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
examples/ also holds SimplePlace (a starter with one of each basic control),
Calculator (nineteen hand placed buttons), UserForm (entries, a combo box and a
notebook), sudoku3 (a 91 widget Grid project) and sudokupack (Grid, small).
Screenshots of all of them, in both backends, are in
docs/.
- Builds ttkbootstrap interfaces visually on a live design surface.
- Uses responsive Grid layout by default, or free-form Place layout.
- Moves, resizes, duplicates, deep-clones, deletes, groups, and re-parents widgets.
- Edits widget attributes with colour, font, image, entry, combo, and spinbox controls.
- Gives Grid and Place widgets useful type-specific layout defaults.
- Keeps child layouts inside Frame, Labelframe, and Panedwindow containers.
- Saves human-readable JSON projects with atomic writes and rolling backups.
- Preserves design-time callback and Tk-variable names through save, reload, duplication, and code generation.
- Generates clean multiline Python calls and preserves callback bodies edited in a previously generated file.
- Supports undo and redo for the main editing operations.
- Applies ttkbootstrap themes to the builder and generated program.
- Python 3.10 or newer
- tkinter (sometimes supplied as a separate operating-system package)
- ttkbootstrap 2.0 or newer
- tkfontchooser
- coloredlogs
- Pillow
Flet is optional and only needed to run programs written by
File → Generate Flet. The designer itself never imports flet. Install
flet>=1.0 — the generated programs target the 1.0 API (they still run on the
0.8x releases, with a version warning).
The complete Python dependency list is in
requirments.txt. The filename is retained for compatibility
with existing setup instructions.
On Debian or Ubuntu, install tkinter if it is not already present:
sudo apt install python3-tksudo apt install python3.13-full
git clone https://github.com/ChrisMcGowanAu/PyTkQuickGui.git
cd PyTkQuickGui
python -m venv venv
source venv/bin/activate
pip install -r requirments.txt
python pytkquickgui.pyOn Windows, activate the environment with:
venv\Scripts\activate- Run
python pytkquickgui.py. - Select File → New Project, enter a project name, and choose Grid or Place.
- Right-click the design surface and choose a widget.
- Drag the widget to move it. In Place mode, drag an edge to resize it. In Grid mode, movement changes its row and column and edge drags adjust its span.
- Right-click the widget and use Edit for attributes or Layout for geometry.
- Use File → Save Project to save the JSON project.
- Use File → Trial Run to preview it or File → Generate Python to write a program. File → Generate Flet writes the same layout as a Flet application.
Several ready made projects ship in examples/. Copy them into the tool's
directory and open one to see a finished project, or to compare the Python and
Flet output:
mkdir -p "$HOME/.config/pytkgui/examples"
cp -r examples/* "$HOME/.config/pytkgui/examples/"Then File → Open Project and pick a folder such as
~/.config/pytkgui/examples/Calculator. See INSTALL.TXT for the Windows
equivalent and a list of what each example shows.
The top toolbar shows the active layout manager. Grid projects also expose:
- Rows and Cols controls for the root design grid.
- Grid settings for guide colour, row and column minimum sizes, padding, and saving the current settings as tool defaults.
- An undo status at the right.
The geometry manager is selected for the project. Once widgets exist, switching manager is blocked because mixing managers in one Tk parent leads to invalid layouts. Start a new project to use a different manager.
| Menu | Important actions |
|---|---|
| File | New, open, close, save, save as, Trial Run, Generate Python, Trial Run (Flet), Generate Flet |
| Edit | Undo, redo, group selected widgets, ungroup |
| Theme | Light, dark, and legacy ttkbootstrap themes |
| Tools | Label borders, default fonts/styles, backups, widget tree, Compact Grid |
| Help | Welcome and the in-application guide |
Right-click empty space to open the widget palette. The current palette contains these container widgets:
- Frame
- Labelframe
- Panedwindow
These regular widgets are currently enabled:
- Label
- Button
- Entry
- Combobox
- Spinbox
- Checkbutton
- Radiobutton
- Scale
- Progressbar
- Canvas
- Text
- Listbox
- Separator
Other Tk and ttkbootstrap widget implementations remain in the source while their designer behaviour is completed, but they are not offered in the palette.
| Action | Result |
|---|---|
| Edit | Opens the scrollable attribute editor |
| Layout | Opens geometry values for the current manager |
| Duplicate | Copies one widget |
| Clone | Deep-copies a container and its children |
| Re-Parent | Places the widget in the enclosing container, or back at the root |
| Delete | Removes the widget |
| Add to Selection | Adds the widget to the current multi-selection |
| Group Selected | Creates a logical group from selected widgets |
The Edit and Layout popups put the action buttons at both the top and bottom. The duplicate controls are intentional: short forms remain convenient while long forms do not force the user to scroll to a particular end. Popups can be moved with the yellow drag handle.
The attribute editor stores callback fields such as command and variable
fields such as textvariable as Python names, rather than trusting Tk's
internal Tcl command strings. Use valid top-level Python identifiers for these
values, for example save_record or customer_name.
If a widget has both a text and a textvariable, the text you type is also
written into the variable. Tk ignores text once textvariable is set, so
without that the caption would be dropped - the design would keep showing
whatever the variable held, and the generated program would fall back to its own
default. Writing it to the variable keeps the design, the saved project and the
generated program in agreement. A widget that keeps its state in a variable
(a checkbutton or radiobutton) is left alone: its caption stays in text.
Grid is the default and is recommended for responsive forms. Widgets use
row, column, columnspan, rowspan, padx, pady, ipadx, ipady, and
sticky.
New widgets receive type-specific defaults. For example, entries span more columns than labels, while text areas and containers span several rows and columns. Open a widget's Layout popup and select Save default to make its current Grid layout the default for future widgets of that type.
Rows and columns expand with the design surface. Container widgets keep their own internal grid dimensions, including extra tracks created while editing. Those dimensions are stored in the project and reproduced in Trial Run and generated Python.
Use Tools → Compact Grid to remove unoccupied gaps and reduce the configured root grid to its occupied extent.
Example generated geometry:
Widget0.grid(
row=2,
column=1,
columnspan=3,
rowspan=1,
sticky="nsew",
padx=2,
pady=2,
)Place is useful for free-form prototypes and fixed-position controls. It stores
x, y, width, and height.
The drop position always follows the pointer. Initial width and height are
type-specific: containers and text areas start larger than buttons and labels.
Resize a widget, open Layout, and select Save default to use that size
for future widgets of the same type.
Example generated geometry:
Widget0.place(
x=80,
y=48,
width=180,
height=32,
anchor="nw",
bordermode="inside",
)Pack is disabled for new projects. Compatibility code remains for older project files, but its visual editing model is deferred until Grid and Place are stable.
tool_defaults.py contains the built-in Grid and Place defaults. Optional
tool_defaults.json files are layered in this order, from lowest to highest
precedence:
/etc/pytkgui/tool_defaults.jsontool_defaults.jsonbeside the PyTkQuickGui source modulestool_defaults.jsonin the directory from which the tool was launched- The user's configuration file
The user file is normally:
- Linux:
~/.config/pytkgui/tool_defaults.json - Linux with
XDG_CONFIG_HOME:$XDG_CONFIG_HOME/pytkgui/tool_defaults.json - Windows:
%APPDATA%\pytkgui\tool_defaults.json
Missing files are ignored. Files may contain only the values they need to override; nested widget defaults are merged field by field. The user file has highest priority so Save as tool default and Save default take effect without modifying a system or source installation.
The top-level Grid settings are:
{
"gridRows": 25,
"gridCols": 25,
"gridLineColor": "",
"gridRowMinsize": "2.5m",
"gridColMinsize": "5m",
"gridRowPad": "2.5m",
"gridColPad": "5m"
}Per-widget records live under gridWidgetDefaults and
placeWidgetDefaults. Place records contain only width and height.
Flet output is configured by two further keys, described under
Generating Flet: fletGridMode (responsive or
absolute) and fletWidgetPolicy (per widget type: full, placeholder or
skip).
Grid rows, columns, guide colour, minimum sizes, and padding are also saved in each project. Project values override tool defaults when that project is opened; tool defaults remain the starting values for new projects.
New projects are stored below the platform configuration directory, normally:
~/.config/pytkgui/MyProject/
The active file is MyProject.json. Each save is written to a temporary file,
parsed for validation, and atomically replaces the active file. Up to five
earlier versions are rotated as:
MyProject-save1.json
MyProject-save2.json
...
MyProject-save5.json
Tools → Open backup file can load a saved backup.
Project JSON includes:
- project name, theme, and geometry manager
- window and Grid settings
- widget identity and creation order
- parent/child relationships
- Place or Grid geometry
- container Grid dimensions
- editable widget attributes
- callback and Tk-variable design names
- image references and logical groups
- the last generated Python path, when available
Older .pk1 pickle projects can still be detected and opened. Because pickle
can execute code while loading, open legacy files only when you trust their
source. The next save writes the project in JSON format.
File → Generate Python proposes <project-name>.py in the last output
directory. Widget constructors and geometry calls are formatted one argument
per line:
Widget1 = ttk.Frame(
rootWidget,
width="0",
height="0",
cursor="arrow",
style="primary.TFrame",
)The generated file contains marked sections for Tk variables, callback functions, widgets, and the main program. Callback names referenced by widgets receive an initial stub:
def save_record():
# AUTO-GENERATED STUB
print("save_record")When the same generated file is selected again, PyTkQuickGui preserves edited callback bodies and customised Tk-variable initialisers. Widget construction and geometry sections are rebuilt from the current project. User functions no longer referenced by a widget are also retained.
Trial Run writes and launches a temporary generated file. It is intended for layout testing and does not replace the explicitly saved Python file.
File → Generate Flet writes a standalone Flet program
(import flet as ft, def main(page), ft.run(main)) from the same project
data. The translation lives in flet_generator.py and is a pure function of the
project, so it can be tested without a display.
Run one in a browser to test it without a desktop build, and to open it from another machine:
venv/bin/flet run --web ~/Calculator/Calculator_flet.py # add --port 8550The generated program is unchanged either way - flet run --web serves the same
file, and python <program>.py still runs it as a desktop application.
INSTALL.TXT has the details, including --host 0.0.0.0.
| Designer | Flet output |
|---|---|
| Place | ft.Stack with absolute left/top/width/height |
| Grid (default) | nested ft.Column/ft.Row; rows share the space and a widget with sticky=nsew fills its cell, so the layout stretches with the window the way ttk does |
| Grid (exact positions) | ft.Stack with rowspan/columnspan reproduced exactly; cells are sized from the widgets themselves (the designer's measurements, the tool default per type as a floor, minsize only as a floor like Tk) |
| Pack | ft.Row/ft.Column groups by side |
| Frame / Canvas | ft.Container (background preserved) holding an ft.Stack |
| Labelframe | ft.Container with the caption above the content (below for a south labelanchor), aligned as the designer's labelanchor says - centred for the default n. A borderwidth of 0 draws no box, as ttk does |
| Notebook | a panel ft.Container holding ft.Tabs with ft.TabBar and ft.TabBarView; the tab bar is pinned to ttk's manner (compact padding, left aligned, bootstyle accent on the selected tab) because Material's defaults are roomier and overflowed small notebooks |
| Panedwindow | ft.Row/ft.Column (Flet has no draggable splitter) |
| Label / Button / Entry / Combobox / Checkbutton / Radiobutton / Scale / Progressbar / Separator | ft.Text / ft.Button / ft.TextField / ft.Dropdown / ft.Checkbox / ft.RadioGroup / ft.Slider / ft.ProgressBar / ft.Divider |
| Spinbox | ft.Row of a numeric ft.TextField and ↑/↓ ft.IconButtons, stepping by the saved from/to/increment through a generated _step_value helper |
| Treeview | ft.DataTable with the saved column headings (rows are never stored in the project) |
| Listbox | ft.ListView |
| Scrollbar | folded into the widget it scrolls: scroll=ft.Scrollbar(...) on a ft.ListView, or a scrolling ft.Column wrapped around a canvas/table |
| Text | a multiline ft.TextField inside a ft.Container that gives it the designer's exact box (Flet sizes a multiline field from its line count, not from a height); its own scrollbar is dropped |
command, textvariable, variable |
on_click/on_change, plain Python values, bool(...) for check buttons |
| Tk colours | CSS hex (named Tk colours are mapped, unknown names are dropped) |
Things worth knowing:
-
The output targets the Flet 1.0 control API (
ft.Button,ft.DropdownOption,TabBar/TabBarView) while staying compatible with the 0.8x releases. Flet 1.0 removesft.ElevatedButton, so buttons are emitted asft.Button, which both generations provide. -
Every generated program opens with a version guard. It compares the running Flet against
MINIMUM_FLET_VERSION('1.0') and, when the runtime is older, names the installed version and the upgrade command:warning: This program targets Flet 1.0 or later. Installed: 0.86.5. Upgrade with: pip install --upgrade fletThe program still runs — the 0.8x API is compatible — but set
FLET_VERSION_STRICT = Truein the generated file to make it refuse to start instead of warning. -
Tk options with no Flet equivalent (
takefocus,cursor,style, …) are left out and listed in aTranslation notescomment per widget, so nothing disappears silently. -
reliefwith aborderwidthabove zero draws a border, as ttk does - the designer's own signal, so a label or frame the designer gaverelief=solid, borderwidth=1comes out bordered rather than flat. -
Flet attaches scrollbars to a scrollable control (
ft.Column(scroll=ft.Scrollbar())) rather than exposing a free-standing widget, andft.RadioGroup/ft.Dividerare not positional controls, so they are wrapped in aft.Containerto keep the designer's coordinates. -
A variable starts at the value its widget showed in the designer rather than a fixed
'0.0': type the value into the widget on the canvas (or tick a checkbutton), save or generate, and that is what the generated program initialises. A variable with nothing captured still starts at'0.0', and the Python backend emits it astk.StringVar(rootWin, '123'). -
A widget bound to a
textvariable(a sudoku cell, a calculator display, a text area) keeps that binding: the generated program declares the variable, points the control at it, and providesset_text(name, value)to change it - which updates the variable and every control showing it, the way a Tk variable does. One variable can drive many controls. -
Each generated handler stub starts with
global <variables>, so a plain assignment in your own code updates the module variable rather than creating a local name (Flet controls hold plain Python values, so unlike atk.StringVarthere is nothing to.set()). Useset_text('calcvar', '4')when you also want the widgets refreshed;set_textuses the page recorded inmain(), so no page argument is needed. The same tip is written into the generated file's Flet variables section, next to the variables it is about. -
The window opens at a size worked out from the design rather than a fixed 800x600: Grid and Pack from the widgets and their spans, Place from the furthest edge anything is placed at. It is clamped to 1280x900 and written to the generated file as
WINDOW_WIDTH/WINDOW_HEIGHTwith a comment, so adjusting it is a one line edit. Grid and Pack layouts reflow as the window is resized; Place positions are absolute, so they stay where the designer put them (a widget using relative width/height still stretches). -
Text style is pinned to a normal weight: Flet renders a
ft.TextStylewhose weight is left unset in bold, which made button captions and check/radio labels heavier than the ttk originals. -
Notebook tab captions are stored as
tab_labelsmetadata ("Home,Config") next to the widget, because they live on the tab ids rather than in the notebook's options — a label typed in the attribute editor now survives a save and reload, and both backends write it (notebook.add(frame, text=…)andft.Tab(label=…)). A tab with no label is called "Tab". -
Trial Run (Flet) launches the generated program with
python3. -
Regeneration keeps your work: a handler you have taken over - the
# AUTO-GENERATED STUBline removed - and a variable line you have changed are carried over from the file you last generated to, exactly as the Python backend does. Untouched stubs are regenerated. -
ttkbootstrap themes are not Flet themes; the project theme is emitted as a
THEMEconstant for reference only.
Place is the recommended target for Flet output. It translates one to one: every widget keeps the coordinates the designer gave it, the result looks like the Trial Run, and there is nothing to interpret. If a Flet app is the goal, design in Place.
Grid works, but it is an interpretation, and it may need size tweaks. Flet
has no grid layout at all - no spans, no minsize, no weights - so a Grid
project is rebuilt from rows and columns of expand weights. Known differences:
rowspanis approximated: a widget that covers several rows occupies one band, at its designed size, instead of stretching down across the rows. Exact positions (the dialog option, or Tools → Flet: exact Grid positions) reproduces spans properly, at the cost of not reflowing.minsizeis only a floor in Tk, and the designer's minsizes are Tk units (2.5mis about 9 pixels), so cell sizes are worked out from the widgets themselves. A design that relies on minsizes for its proportions will look different.stickydecides which axes a widget fills; partial stickies (ew,w) keep the designed size on the axes they do not fill.
Pack is the weakest of the three and is best avoided for Flet output.
A project is just JSON, so a program can be produced without opening the
designer - useful for scripted or generated UIs. The designer opens such a file
with File → Open Project as usual, and flet_generator.emit_program() /
buildPython() accept the same dictionary directly.
The smallest useful file needs the project keys plus one record per widget:
{
"formatVersion": 2,
"ProjectName": "handwritten",
"geomManager": "Place",
"theme": "darkly",
"backgroundColor": "skyBlue3",
"imageFileNames": [],
"widgetCount": 2,
"Widget0": {
"WidgetName": "ttk::button",
"WidgetParent": "rootWidget",
"Place": {"x": "16", "y": "16", "width": "120", "height": "32", "anchor": "nw"},
"GeomData": {},
"Attribute0": {"Key": "text", "Value": "Say hello"},
"Attribute1": {"Key": "style", "Value": "success.TButton"},
"Widget0-KeyCount": 2
}
}WidgetNameis the Tk widget type (ttk::button,ttk::frame,canvas,text,listbox, …);WidgetParentis another widget name orrootWidget.- Attributes are Tk options:
text,textvariable,command,style,values,from/to/increment, and so on. Keys Flet cannot express are reported in the generated file rather than silently dropped. - Coordinates are relative to the parent, exactly as the designer stores them.
widgetNameListis optional here: the loader rebuilds it from the widgets' parents if it is missing, and the designer writes it back on the next save.themeis what drives Flet colours, so it is worth setting even by hand.
Project colours come from the ttkbootstrap theme named in the project, so a
Flet build looks like the Trial Run rather than like Flet's defaults. Your
widgets carry a style such as primary.TButton, secondary.Outline.TButton
or success.Inverse.TLabel, and the generator resolves each one:
| Style | Flet result |
|---|---|
primary.TButton |
filled with the theme's primary colour, text in black or white depending on the fill |
secondary.Outline.TButton |
theme background, primary-coloured border and caption |
primary.TLabel |
theme text colour (no background) |
success.Inverse.TLabel |
filled with the bootstyle colour, contrasting text |
primary.TFrame |
filled with the bootstyle colour |
primary.TLabelframe |
theme surface, bootstyle border, theme text caption |
primary.TEntry / TCombobox / TSpinbox |
the theme's input surface and text, with the theme's border colour |
primary.Horizontal.TProgressbar |
bootstyle-coloured bar on the theme's trough colour |
primary.Horizontal.TScale |
bootstyle-coloured track and thumb |
primary.TCheckbutton / TRadiobutton |
bootstyle-coloured indicator, theme-coloured caption |
primary.TNotebook |
theme surface with the theme's border |
| (window) | the theme's background, with Flet's own theme set to dark or light to match |
The colours are exported from ttkbootstrap itself into
flet_theme_colors.json by tools/export_theme_colors.py: each theme's
bootstyle colours and the resolved colours of real widgets, probed with
style.lookup. That matters because the Bootswatch-derived themes shade their
bootstyle colour (minty's buttons are #609b8a, not its #78c2ad slot) and
because ttk chooses black or white text per fill. The generator reads the JSON
only, so it still needs neither Tk nor ttkbootstrap.
Regenerate the palette after upgrading ttkbootstrap:
python3 tools/export_theme_colors.py # needs ttkbootstrap 2.x and a displayApproximations worth knowing: a ttk progressbar draws its fill from a shaded image, so the Flet bar uses the bootstyle colour directly; the same applies to scale tracks and separator lines. If a project's theme is missing from the palette (a custom theme, or a very old project), generation still works and falls back to the palette slot colours — Tools → Flet compatibility report says which happened.
Tools → Flet compatibility report lists every widget in the project with the control it becomes, or why it cannot be one:
Widget0 canvas ft.Container + ft.Stack (3 children)
Widget6 ttk::scrollbar wraps Widget0 in a scrolling ft.Column (...) [folded]
Summary: 7 widgets - 6 mapped, 1 folded into a scroll target, 0 placeholder, 0 skipped
Generate Flet and Trial Run (Flet) ask how to lay out a Grid project before writing anything, because the two mappings suit different purposes:
- Responsive - rows and columns grow with the window, so the layout
stretches the way a Flet app is expected to. A widget with
sticky=nsewfills its cell; one withsticky=ewkeeps the height the designer gave it. Row heights follow their content, so a one line widget is not stretched to the height of the tallest row. - Exact positions - a
ft.Stackthat reproduces the designer's pixel layout, includingrowspanandcolumnspan, but stays fixed when the window is resized.
The dialog shows the window size it worked out from the design and remembers
your answer in tool_defaults.json as fletGridMode. Place and Pack projects
have a single mapping, so they are not asked. Tools → Flet: exact Grid
positions still sets the remembered default without generating.
Both settings live in tool_defaults.json, next to the existing geometry
defaults, so they can be edited by hand or layered per project:
{
"fletGridMode": "responsive",
"fletWidgetPolicy": {
"default": "full",
"treeview": "skip",
"canvas": "placeholder"
}
}The policy is per widget type, using the same lower-case keys as
gridWidgetDefaults:
| Policy | Effect |
|---|---|
full (default) |
Emit the best mapping above; fall back to a placeholder only when Flet has no equivalent at all |
placeholder |
Emit a plain ft.Container with a note instead of the mapping — useful when a lossy mapping would be more misleading than an obvious gap |
skip |
Leave the widget out of the generated program entirely; children of a skipped container are promoted to its parent |
Skipping a widget never removes it from the project or from the Python backend — it only affects Flet output.
The Theme menu groups ttkbootstrap 2.0 light and dark themes and retains legacy theme names for older projects. The selected theme is stored in the project and used by generated Python.
Changing a theme can alter requested widget sizes. Grid layouts normally absorb those differences; check a Trial Run when exact Place dimensions matter.
- If a project JSON was hand-edited, validate its JSON syntax first.
- If a generated callback is skipped, check that its name is a valid Python identifier and not a Python keyword.
- If Grid guides appear stale after resizing, resize the main window once and report the project JSON and steps needed to reproduce it.
- Use the most recent
-saveN.jsonbackup if an active project file is damaged. - Runtime detail is written through Python logging. Benign Tk lookups on a widget already destroyed during cleanup are logged at debug level.
Tk and Flet both carry C code, so a fault can end the process with a segfault rather than a traceback. Three things make that diagnosable:
-
A Python traceback on a fatal signal.
faulthandleris enabled at startup, so a crash prints the Python frame that was running to stderr.kill -USR1 <pid>prints a traceback of every thread on demand, which is the quickest way to see where a hung window is stuck. -
Core dumps are kept by systemd. No
ulimitneeded - the kernel pipes cores tosystemd-coredump:coredumpctl list # every crash, with pid and time coredumpctl info <pid> # summary and stack trace coredumpctl gdb <pid> # load it in gdb: bt, py-bt coredumpctl dump <pid> --output=core # then: gdb -batch -ex bt /usr/bin/python3.12 core
-
Python frames in gdb need
sudo apt install python3.12-dbg; without it a core shows only C frames (_PyEval_EvalFrameDefaultand friends), with itpy-btnames the Python function.
A crash seen here in the wild, for reference: Tk_Get3DBorderFromObj inside
Tk_Free3DBorderFromObj inside Tk_FreeConfigOptions, reached from
Tk_BindEvent - Tk freeing a widget's border while a binding was still running,
i.e. a widget destroyed from inside a binding or a validate= callback. If you
hit it, the traceback from the steps above points at the Python line.
- Pack cannot be selected for a new project.
- The palette deliberately exposes a smaller widget set while remaining widgets are stabilised.
- Some complex widgets require application-specific setup that a visual builder cannot infer, such as connecting scrollbars to targets.
- Place is the default for a new project, and the recommended target for Flet output: it translates one to one, where Grid has to be interpreted.
- Generated files are suggested as
<home>/<project>/<project>_ttk.pyand<project>_flet.py, so both outputs of a project sit together; the path you choose is remembered with the project and used for edit preservation. - Flet has no draggable splitter, and a Tk scrollbar with no scrollable target (or a horizontal one) stays a placeholder Container.
- Exact Grid positions sizes a cell from the widgets in it, not from the
stored minsize:
2.5mis only 9px, and Tk treats minsize as a floor too. A designer widget that measures small because its caption comes from an emptytextvariablenever shrinks below the tool default for its type. Exact mode keeps the layout fixed instead of reflowing, so use it for position parity, not for a window the user is expected to resize. - In Grid output the empty columns the designer draws still take a share of the
width, because the Python backend gives every column
weight=1- so a board that spans 9 of 10 columns leaves a strip on the right in both backends. - Trial Run and visual editing require a desktop session with Tk support.
- Legacy pickle compatibility is temporary and should be treated as a migration path to JSON.
Run the unit tests from the repository root:
python -m unittest discover -s tests -vWhen contributing:
- Work on a focused branch.
- Keep generated and persistence formats backwards-compatible where practical.
- Use
logginginstead of diagnosticprintcalls in application code. - Test both Grid and Place, including a child widget inside a container.
- Test a save, reload, Trial Run, and generated Python file.
- Run the Flet generator over the same project (
Generate Flet) when widget mapping or geometry changes, and re-runtools/export_theme_colors.pywhen ttkbootstrap itself is upgraded. - Check at least one light and one dark ttkbootstrap theme.
Bug reports are most useful when they include the project JSON, the selected geometry manager, the sequence of editing actions, and the complete traceback.
Several Flet mappings rest on measured behaviour - a multiline field takes its
height from min_lines, a ft.TextStyle with no weight is drawn bold, Material
paints a check/radio fill in every state, ft.Tabs takes a
TabBar/TabBarView content - and a Flet upgrade can change any of them
without changing the major version. tools/smoke_flet_generated.py is the
answer: it generates every saved project (default, exact-position Grid and
skip-optional modes), parses the result, builds the controls with the installed
Flet and exits non-zero on failure.
venv/bin/python tools/smoke_flet_generated.py # the saved projects
venv/bin/python tools/smoke_flet_generated.py --all # including backups
venv/bin/python tools/smoke_flet_generated.py ~/elsewhere/*.jsonMIT. See LICENSE.
PyTkQuickGui — Chris McGowan, 2024–2026.







