The vocabulary of GUI programming β widgets, geometry, events, variables, dialogs and menus β explained conceptually and shown in code. Examples use Tkinter (the universal baseline); the ideas map directly onto every toolkit.
A widget is any interactive or display element. Conceptually they fall into a few families. Below are the most common Tkinter widgets and their cross-toolkit equivalents.
| Widget | Purpose | Tkinter class | Qt equivalent |
|---|---|---|---|
| Label | Show static text/image | ttk.Label | QLabel |
| Button | Trigger an action | ttk.Button | QPushButton |
| Entry | Single-line text input | ttk.Entry | QLineEdit |
| Text | Multi-line text area | tk.Text | QTextEdit |
| Checkbutton | Boolean on/off | ttk.Checkbutton | QCheckBox |
| Radiobutton | One-of-many choice | ttk.Radiobutton | QRadioButton |
| Combobox | Dropdown selection | ttk.Combobox | QComboBox |
| Listbox | Scrollable list | tk.Listbox | QListWidget |
| Slider/Scale | Pick a numeric range | ttk.Scale | QSlider |
| Progressbar | Show progress | ttk.Progressbar | QProgressBar |
| Treeview/Table | Tabular / tree data | ttk.Treeview | QTreeView |
| Frame | Group / container | ttk.Frame | QFrame |
| Canvas | Free-form drawing | tk.Canvas | QGraphicsView |
In Tkinter every widget takes its parent as the first argument, followed by configuration options as keyword arguments:
widget = ttk.Button(
parent, # where it lives in the widget tree
text="Save", # option: visible text
width=12, # option: width in characters
command=save_file, # option: callback on click
)
A widget exists once created, but it is invisible until a geometry manager places it. Tkinter offers three. Never mix them within the same parent container.
w.pack(side="top", # top | bottom | left | right
fill="x", # none | x | y | both β stretch to fill
expand=True, # claim extra space when window grows
padx=8, pady=4) # outer padding in pixels
w.grid(row=0, column=1,
columnspan=2, # span multiple columns
sticky="ew", # n/s/e/w β which edges to cling to
padx=4, pady=4)
# Make a column/row stretch when the window resizes:
parent.columnconfigure(1, weight=1)
parent.rowconfigure(0, weight=1)
w.place(x=20, y=30) # exact pixels
w.place(relx=0.5, rely=0.5, # 50% across / down
anchor="center") # center the widget on that point
Use grid() for forms, pack() for simple toolbars/stacks, and
place() only for special overlays. grid + weight is
the key to resizable, professional layouts.
Interactivity comes from connecting events (something the user did) to callbacks (a function you wrote). There are two mechanisms:
command option (high level)ttk.Button(root, text="Go", command=on_go)
# pass arguments with a lambda:
ttk.Button(root, text="Delete", command=lambda: delete(item_id))
bind() (any event)def on_key(event):
print("key:", event.keysym, "at", event.x, event.y)
root.bind("<Return>", on_key) # Enter key
root.bind("<Button-1>", on_key) # left mouse click
root.bind("<KeyPress>", on_key) # any key
root.bind("<Control-s>", on_key) # Ctrl+S
root.bind("<Motion>", on_key) # mouse moved
| Event pattern | Fires when⦠|
|---|---|
<Button-1/2/3> | Left / middle / right mouse pressed |
<Double-Button-1> | Double left-click |
<Return> / <Escape> | Enter / Esc key |
<KeyPress-a> | The "a" key is pressed |
<Control-c> | Ctrl+C combination |
<Enter> / <Leave> | Pointer enters / leaves a widget (hover) |
<Configure> | Widget is resized or moved |
Qt expresses the same idea differently: button.clicked.connect(handler).
Every widget exposes named signals you connect to slots (handlers). The concept β
"event β handler" β is identical.
Tkinter offers special variable objects that stay synchronised with a widget. Read the variable to get the current value; set it to update the widget β no manual refresh needed.
name = tk.StringVar(value="Ada")
agree = tk.BooleanVar()
volume = tk.IntVar(value=50)
ttk.Entry(root, textvariable=name).pack()
ttk.Checkbutton(root, text="I agree", variable=agree).pack()
ttk.Scale(root, from_=0, to=100, variable=volume).pack()
print(name.get(), agree.get(), volume.get()) # read
name.set("Grace") # write β entry updates
# React whenever the value changes:
name.trace_add("write", lambda *_: print("now:", name.get()))
After creation you can change options at runtime with config() (alias
configure()) and read them by indexing:
btn = ttk.Button(root, text="On")
btn.config(text="Off", state="disabled") # change options
current = btn["text"] # read an option
label.config(foreground="white", background="#3776ab")
entry.delete(0, "end") # clear an Entry
entry.insert(0, "default text")
Toolkits ship ready-made dialogs for common interactions so you don't rebuild them.
from tkinter import messagebox, filedialog, simpledialog, colorchooser
messagebox.showinfo("Done", "File saved.")
ok = messagebox.askyesno("Quit", "Exit without saving?")
path = filedialog.askopenfilename(filetypes=[("Text", "*.txt")])
save = filedialog.asksaveasfilename(defaultextension=".txt")
folder = filedialog.askdirectory()
age = simpledialog.askinteger("Age", "How old are you?")
color = colorchooser.askcolor()
menubar = tk.Menu(root)
root.config(menu=menubar)
file_menu = tk.Menu(menubar, tearoff=0)
file_menu.add_command(label="New", accelerator="Ctrl+N", command=new_file)
file_menu.add_command(label="Open", command=open_file)
file_menu.add_separator()
file_menu.add_command(label="Exit", command=root.quit)
menubar.add_cascade(label="File", menu=file_menu)
root.bind("<Control-n>", lambda e: new_file()) # wire the accelerator
The Canvas is a free-form surface for shapes, images, lines and custom graphics β the basis for charts, diagrams and simple games.
c = tk.Canvas(root, width=300, height=200, background="white")
c.pack()
c.create_line(10, 10, 290, 10, fill="blue", width=3)
c.create_rectangle(20, 40, 120, 100, fill="#ffd43b")
c.create_oval(150, 40, 250, 120, outline="red")
item = c.create_text(150, 160, text="Hello", font=("Segoe UI", 16))
c.move(item, 0, -5) # shift an existing item
Use after() to schedule work on the event loop without blocking it β ideal
for clocks, animations and polling background results.
import time
def tick():
clock.config(text=time.strftime("%H:%M:%S"))
root.after(1000, tick) # re-run in 1000 ms
clock = ttk.Label(root, font=("Segoe UI", 24))
clock.pack(padx=20, pady=20)
tick() # start the loop
root.mainloop()
Calling time.sleep(5) inside a callback freezes the whole window for 5
seconds. Use after() for delays, and threads for heavy work
(see Best Practices β Threading).
| Task | Command (Tkinter) |
|---|---|
| Create window | root = tk.Tk() |
| Title / size | root.title("X"); root.geometry("400x300") |
| Start app | root.mainloop() |
| Add label | ttk.Label(root, text="Hi").pack() |
| Button + action | ttk.Button(root, text="Go", command=fn) |
| Read text input | entry.get() |
| Layout: stack | w.pack(side="top", fill="x") |
| Layout: table | w.grid(row=0, column=1) |
| Bind event | root.bind("<Return>", handler) |
| Change option | w.config(text="New") |
| Timer | root.after(1000, fn) |
| Message box | messagebox.showinfo("T", "msg") |
| Close window | root.destroy() |
Useful guides and tools (from webpage_links.xlsx).