Tutorial: Learn Python GUI with Tkinter from Scratch (2026)
I built my first desktop app with Tkinter back when Python 3 was still new — a simple inventory manager for a small warehouse. Tkinter is Python's standard GUI toolkit, wrapping the Tcl/Tk graphical library. It's not the prettiest framework out there, but it ships with every Python installation, it's cross-platform (Windows, macOS, Linux), and it's capable of building functional desktop applications without any third-party dependencies.
This tutorial covers the core Tkinter widgets and patterns I've used in production: frames for layout management, canvas for drawing, event binding for user interaction, and the ttk (themed) widgets for modern-looking controls. We'll build a simple drawing application that lets users sketch, change colors, and clear the canvas.
Setting Up the Main Window and Geometry
Every Tkinter application starts with a Tk instance representing the main window. The geometry method sets window size and position, and title sets the window title. mainloop() enters the event loop that waits for user interactions. Without mainloop(), the window appears and immediately disappears. I always separate window configuration into a setup function for clarity.
import tkinter as tk
root = tk.Tk()
root.title("My App")
root.geometry("800x600+100+50") # width x height + x_offset + y_offset
root.resizable(True, True) # allow resizing
root.mainloop()
Widgets: Labels, Buttons, and Entry Fields
Tkinter provides a set of basic widgets: Label for text, Button for clickable actions, Entry for single-line text input, and Text for multi-line editing. Each widget is created with its parent as the first argument, followed by configuration options. The command parameter on Button connects it to a callback function. Widgets are placed using a geometry manager — pack, grid, or place.
def on_click():
name = entry.get()
greeting.config(text=f"Hello, {name}!")
root = tk.Tk()
root.title("Greeter")
label = tk.Label(root, text="Enter your name:")
label.pack(pady=5)
entry = tk.Entry(root, width=30)
entry.pack(pady=5)
button = tk.Button(root, text="Greet", command=on_click)
button.pack(pady=5)
greeting = tk.Label(root, text="")
greeting.pack(pady=5)
root.mainloop()
Layout Management with Grid and Pack
The grid geometry manager organizes widgets in rows and columns, which gives you precise control over layout without absolute positioning. The pack manager stacks widgets along a side (top, bottom, left, right). I use grid for form-like layouts and pack for toolbars and status bars. Widgets can span multiple rows or columns with rowspan and columnspan, and padding is set with padx and pady.
root = tk.Tk()
root.title("Grid Layout")
# Form using grid
tk.Label(root, text="Name:").grid(row=0, column=0, sticky="e", padx=5, pady=5)
tk.Entry(root).grid(row=0, column=1, padx=5, pady=5)
tk.Label(root, text="Email:").grid(row=1, column=0, sticky="e", padx=5, pady=5)
tk.Entry(root).grid(row=1, column=1, padx=5, pady=5)
tk.Button(root, text="Submit").grid(row=2, column=0, columnspan=2, pady=10)
# Pack for toolbar
frame = tk.Frame(root, bd=1, relief=tk.RAISED)
frame.pack(fill=tk.X)
tk.Button(frame, text="File").pack(side=tk.LEFT, padx=2)
tk.Button(frame, text="Edit").pack(side=tk.LEFT, padx=2)
Event Binding and Keyboard Input
Widgets generate events for mouse clicks, key presses, window resizes, and more. The bind() method connects an event pattern to a handler function. Event patterns like '
def on_click(event):
print(f"Clicked at ({event.x}, {event.y})")
def on_key(event):
print(f"Key pressed: {event.keysym}")
root = tk.Tk()
canvas = tk.Canvas(root, width=400, height=300, bg="white")
canvas.pack()
canvas.bind("", on_click) # Left click
canvas.bind("", on_click) # Drag
root.bind("", on_key) # Any key
root.bind("", lambda e: root.quit()) # Escape to quit
Canvas: Drawing Shapes and Graphics
The Canvas widget is a versatile drawing surface. It supports lines, rectangles, ovals, arcs, polygons, text, and images. Each drawn item is an object that can be moved, resized, or deleted later. Tagging items with tags (like 'shape' or 'selected') lets you operate on groups. I use Canvas for custom widgets, games, charts, and any situation where standard widgets aren't enough.
root = tk.Tk()
root.title("Canvas Drawing")
c = tk.Canvas(root, width=500, height=400, bg="white")
c.pack()
# Draw shapes
c.create_rectangle(50, 50, 150, 150, fill="blue", tags="shape")
c.create_oval(200, 50, 300, 150, fill="red", tags="shape")
c.create_line(350, 50, 450, 150, width=3, fill="green")
c.create_text(250, 300, text="Hello Canvas", font=("Arial", 20))
# Move items with tags
c.move("shape", 0, 50) # Move all 'shape' items down by 50
Building a Simple Drawing Application
Let's combine everything into a functional drawing app. The user can draw freehand with the mouse, pick colors from predefined swatches, and clear the canvas. This demonstrates event binding (mouse motion), state management (current color), and Canvas operations in a cohesive program. Tkinter's simplicity shines when the application logic is straightforward.
class DrawingApp:
def __init__(self, root):
self.root = root
self.root.title("Sketch Pad")
self.color = "black"
self.canvas = tk.Canvas(root, width=600, height=400, bg="white")
self.canvas.pack()
self.canvas.bind("", self.paint)
frame = tk.Frame(root)
frame.pack()
colors = ["black", "red", "blue", "green", "orange"]
for c in colors:
btn = tk.Button(frame, bg=c, width=3, command=lambda col=c: setattr(self, "color", col))
btn.pack(side=tk.LEFT, padx=2)
tk.Button(root, text="Clear", command=lambda: self.canvas.delete("all")).pack()
def paint(self, event):
x, y = event.x, event.y
r = 3
self.canvas.create_oval(x-r, y-r, x+r, y+r, fill=self.color, outline=self.color)
root = tk.Tk()
app = DrawingApp(root)
root.mainloop()
Frequently Asked Questions
Is Tkinter outdated in 2026?
Tkinter is stable, not outdated. It's maintained, ships with Python, and works on all platforms. Modern alternatives include PyQt, wxPython, and web-based UIs with Eel or Electron. Choose Tkinter when you need zero-dependency GUIs or simple internal tools.
How do I create a menu bar?
Use tk.Menu. Create a menu bar, add menus (File, Edit), and add commands (Open, Save) with accelerator keys. Attach to root with root.config(menu=menubar).
Can Tkinter handle multithreading?
Tkinter is not thread-safe — GUI updates must happen on the main thread. Use after() to schedule updates from worker threads, or use queue.Queue to communicate between threads and the GUI.
How do I create custom widgets?
Subclass tk.Frame or tk.Canvas and compose existing widgets inside. Override methods and bind events to create custom behavior. The Canvas widget is particularly useful for custom drawing widgets.
Originally published on Ayodhyyya. Last updated June 1, 2026.