-
-
Notifications
You must be signed in to change notification settings - Fork 0
How Programming Works
CTkMaker splits a CustomTkinter app into two layers — a visual layer you design on the canvas, and a behavior layer you write in plain Python. This page is the single map of how the two connect.
| Layer | What it is | Where it lives | Who writes it |
|---|---|---|---|
| Visual layer | Widgets, properties, layout, bindings |
.ctkproj files inside assets/pages/
|
You — by dragging on the canvas + editing Properties |
| Behavior layer | Methods that run when events fire |
.py files inside assets/scripts/<page>/
|
You — by hand, in your editor of choice |
The two layers are wired together by widget names. A widget called submit_btn in the builder becomes self.submit_btn in the generated code; an event bound to on_submit calls self._behavior.on_submit at runtime. Rename safely from inside the builder — both sides update together.
Four building blocks cover every piece of behavior wiring. Pick the right one for the right job.
| Ingredient | Purpose | Scope | Detailed page |
|---|---|---|---|
| Variables | Shared values that several widgets read or write together (entry text, switch state, accent color). Live two-way for Tk-native bindings, live one-way for cosmetic ones. | Global = whole page · Local = one window | Variables |
| Object References | Typed pointers that let one handler reach a widget — or another window — by a stable Python name. | Local = a widget inside the form · Global = a Window or Dialog | Object References |
| Event Handlers | Methods invoked when the user interacts with a widget (click, keypress, value change). The bridge from the visual layer into the behavior layer. | One method on the window's behavior class — or a library function — per bound event | Event Handlers |
| Library Scripts | Shared helpers across the windows on a page (helpers.py, services/auth.py). Imported by behavior files; can also be called directly from events when attached. |
Page-scoped, one folder per page | Scripts Panel |
| Goal | Reach for |
|---|---|
| Two widgets must show the same value | Variable — bind both to the same one |
| Theme color shared across the page |
Global color variable + bind every relevant fg_color / text_color
|
| Handler needs to grab a value out of an Entry |
Object Reference on the Entry; call self.username_entry.get()
|
| Click a Button in window A to open dialog B |
Global Object Reference on dialog B — self.b_dialog(self.window)
|
| Reusable validator / API client |
Library script + from . import helpers in the behavior file |
| Make a Button call a library function directly without writing a stub | Library function as event target — pick it from the event picker's Library tab (the script must be attached to the window first) |
The four ingredients in one window. The visual layer holds two Entries (username_entry, password_entry), one Button (submit_btn), one Label (status_label), one Switch (remember_switch), and a Toplevel dialog (ForgotPasswordDialog).
The behavior file at assets/scripts/login/login.py:
from . import helpers # Library script
from typing import Generic, TypeVar
T = TypeVar("T")
class ref(Generic[T]): ...
class LoginPage:
# Object References (declared in the Properties panel; annotations stay in sync)
username_entry: ref["CTkEntry"] = ref()
password_entry: ref["CTkEntry"] = ref()
status_label: ref["CTkLabel"] = ref()
forgot_dialog: ref["ForgotPasswordDialog"] = ref() # global ref
def setup(self, window):
self.window = window # gives access to variables on the host class
# Event Handler — bound to submit_btn.command
def on_submit(self):
u = self.username_entry.get()
if not helpers.validate_email(u): # Library function call
self.status_label.configure(text="Invalid email")
return
# Variable read — var_remember is a local bool variable bound to remember_switch
if self.window.var_remember.get():
helpers.save_credentials(u, self.password_entry.get())
self.window.var_signed_in.set(True) # Variable write — propagates everywhere bound
# Event Handler — bound to "Forgot password?" link's <Button-1>
def on_forgot_click(self, event):
self.forgot_dialog(self.window) # instantiate the dialog via global refNotice each ingredient pulling its weight:
| Line | Ingredient at work |
|---|---|
self.username_entry.get() |
Local Object Reference |
helpers.validate_email(...) |
Library Script (imported) |
self.window.var_remember.get() |
Local Variable (read) |
self.window.var_signed_in.set(True) |
Local Variable (write — fans out to every bound widget) |
self.forgot_dialog(self.window) |
Global Object Reference (Dialog target) |
on_submit itself |
Event Handler |
You did not write the wiring code (__init__, _build_ui, the command= kwarg, the import statement, the setup(self, window) call). The exporter does that — see Exporting Code.
What CTkMaker emits when you export the page:
from assets.scripts.login.login import LoginPage # behavior file
from assets.scripts.login import helpers # auto-emitted when a Library function is bound
class MainWindow(ctk.CTk):
def __init__(self):
super().__init__()
# Variables — both globals and locals declared on the main class
self.var_remember = tk.BooleanVar(value=False)
self.var_signed_in = tk.BooleanVar(value=False)
self._behavior = LoginPage()
self._build_ui()
# Object References wired AFTER _build_ui:
self._behavior.username_entry = self.username_entry
self._behavior.password_entry = self.password_entry
self._behavior.status_label = self.status_label
self._behavior.forgot_dialog = ForgotPasswordDialog
self._behavior.setup(self)
def _build_ui(self):
# Event Handlers become command= and bind() calls:
self.submit_btn = ctk.CTkButton(
self, text="Sign In",
command=self._behavior.on_submit,
)
# Variable bindings become textvariable= / variable= kwargs (Tier A)
# plus _bind_var_to_*(...) helpers for cosmetic ones (Tier B / C).
...The behavior file imports cleanly. There is no glue code on your side. Everything you set up visually surfaces in the generated .py exactly where you'd expect a hand-written app to put it.
| If you want to… | Read |
|---|---|
| Learn each ingredient in depth | Variables · Object References · Event Handlers · Scripts Panel |
| See the full property schema for the widgets you're using | Widgets |
| Understand what the exporter writes | Exporting Code |
| Run the project as you build | Preview |
See also — Home · Getting Started · Window Properties