Skip to content

Event Handlers

Lasha Kandelaki edited this page May 19, 2026 · 4 revisions

Event Handlers

Bind widget events (click, change, key press…) to Python methods. Each window owns its own behavior file; CTkMaker writes the stub, you write the body.

Behavior file per window

Every window keeps its handlers in its own Python file:

<project>/assets/scripts/<page>/<window>.py

Inside is one class — <WindowSlug>Page — with a setup(self, window) method and one method per bound handler. The exporter imports this class and wires it into the generated UI automatically.

The file is created lazily on the first handler attach (or eagerly when a new window is added).

Need shared helpers across the windows on a page? Add a library script (helpers.py, api.py, sub-packages) alongside the behavior files and import it via from . import helpers.

Bind a handler

Two paths — both end up at the same place.

Canvas right-click → Add handler ▸ Cascade lists every event the widget supports (command, <Return>, <FocusOut>, …). Pick one → CTkMaker adds an empty target row that you fill in from the Properties panel.

Properties panel → Events group The Events group lands at the end of the widget's schema. Each event has its own outer [+] button that adds an Add target placeholder row. Hover any event header to see a one-line description of when it fires plus any caveat.

Unity-style row layout

Each handler renders as a parent row with two indented children:

▾  Page Script · on_submit                      [×]
   ├─ Function   on_submit                  [pick]
   └─ event      (positional Tk arg)
  • Parent row — shows the target prefix (Page Script, Object Reference, Library) and the resolved method or function name. [×] unbinds (no file mutation).
  • Function row[pick] opens a method-picker filtered to public + signature-compatible methods only. Private (_-prefixed) and async functions are hidden; command events list zero-arg methods, bind events list methods that accept one positional argument.
  • Parameter rows — appear when the target is a Library function with extra positional args. Tab / Enter commits an inline-edited value.

The inner [+] on the parent row opens the flat target picker — see Target types below.

Target types

The flat target picker lists three categories in one menu:

Target What it calls Export shape
Page Script A method on the window's own behavior class (the .py next to the window). The default — pick a method or type a new name to generate a stub. command=self._behavior.on_submit
Object Reference A method on a widget reached through a local Object References slot. Useful for "click button A → focus entry B" without writing a wrapper method. command=lambda: self._behavior.username_entry.focus_set()
Library function A top-level function in a library script attached to the window (Properties panel → Attached Scripts group, or Scripts panel attached-column DnD). Picker only lists modules attached to this window. command=lambda: helpers.save_log() with a matching from . import helpers auto-emitted at the top of the file

Path-as-identity rule — when you rename a library .py or move it between folders, every event binding pointing at it invalidates the same way as a renamed method. Re-pick the target after the move.

Default vs Advanced

Widgets with many bind events (CTkLabel for example) split their event list:

  • Default — the small set of events you usually want (click, hover, value change).
  • Advanced — collapsible sub-group for rare events, high-frequency events, or events with a precondition. CTkMaker tags these with a warning so you don't bind them by accident:
    • <Motion>, <Configure> — fire at 60+ Hz; keep the handler cheap.
    • <FocusIn>, <FocusOut>, <KeyPress>, <KeyRelease> — require takefocus=True on the widget; nothing fires otherwise.

The same partition shows up in the canvas right-click cascade as a nested Advanced ▸ submenu.

Action How
Add target row Outer [+] on event header, or right-click → Add action
Pick target Inner [+] on the parent row → flat target picker (Page Script / Object Reference / Library)
Pick function [pick] button on the Function row → method picker filtered by visibility + signature
Open in editor Double-click parent row, F7, or right-click → Open in editor (Page Script + Library only)
Reorder Right-click parent row → Move up / Move down
Unbind [×] on parent row, or right-click → Unbind. No file mutation — the method stays in the .py.

Multiple actions per event

One event can run several methods in order. The exporter chains them as a lambda: (m1(), m2(), m3()) for command= events, or as separate bind(seq, fn, add="+") calls for bind-style events. Reordering rows in the Events group changes the call order.

Editor

F7 (or any "Open in editor" entry) launches your editor at the right line.

Configure in Settings → Editor:

Preset Notes
Auto VS Code → Notepad++ → IDLE fallback chain
VS Code Recommended; opens project folder + jumps to line
Notepad++ Lightweight
IDLE Always available — runs python -m idlelib
Custom Free-form command with {file}, {line}, {folder}, {python} placeholders

Object Tree marker

A widget with at least one bound handler shows a glyph after its name in the Object Tree.

Window rename / delete

  • Rename a window — the .py file and class header are renamed in lockstep. Comments and bodies stay intact.
  • Delete a window — a dialog surfaces widget / variable / script-line counts and offers two paths for the .py:
    • Delete → routes the file to the OS recycle bin
    • Save copy → moves it to <project>/assets/scripts_archive/<page>/

Action delete works the same: a confirmation dialog (Cancel / Open in editor / Delete) before the method is removed from the file. Blank lines and comments around the method are preserved.

Preview & export

Ctrl+R (Preview) runs the project in a separate console window so any print() from your handler stays visible alongside the UI.

On export, every window with handlers gets:

from assets.scripts.<page>.<window> import <Class>Pageself._behavior = <Class>Page()
self._behavior.setup(self)

…and each event becomes a command=self._behavior.method_name (or a bind(...) call after __init__).


See alsoProperties Panel · Variables · Scripts Panel · Settings · Exporting Code

Clone this wiki locally