GUI with Tkinter
Tkinter is Python's standard GUI library. Learn how to create desktop applications with windows, buttons, text fields, and other interactive widgets.
What is Tkinter?
Tkinter is Python's de-facto standard GUI (Graphical User Interface) package. It is a thin object-oriented layer on top of Tcl/Tk. Tkinter is included with standard Linux, Microsoft Windows and macOS installs of Python.
"Tkinter provides a powerful object-oriented interface to the Tk GUI toolkit, making GUI programming in Python simple and intuitive."
Basic Window
Create your first Tkinter window:
import tkinter as tk
# Create main window
root = tk.Tk()
root.title("My First Tkinter App")
root.geometry("400x300")
# Add a label
label = tk.Label(root, text="Hello, Tkinter!", font=("Arial", 16))
label.pack(pady=20)
# Start the event loop
root.mainloop()
Common Widgets
Essential Tkinter widgets:
import tkinter as tk
def show_message():
message = entry.get()
result_label.config(text=f"Hello, {message}!")
root = tk.Tk()
root.title("Tkinter Widgets Demo")
root.geometry("400x400")
# Label
title_label = tk.Label(root, text="Enter your name:", font=("Arial", 12))
title_label.pack(pady=10)
# Entry (Text input)
entry = tk.Entry(root, width=30, font=("Arial", 12))
entry.pack(pady=5)
# Button
button = tk.Button(root, text="Submit", command=show_message,
bg="blue", fg="white", font=("Arial", 12))
button.pack(pady=10)
# Result label
result_label = tk.Label(root, text="", font=("Arial", 14), fg="green")
result_label.pack(pady=10)
# Text widget (multi-line text)
text_widget = tk.Text(root, height=5, width=40)
text_widget.pack(pady=10)
text_widget.insert(tk.END, "This is a multi-line text widget.\nYou can type here!")
root.mainloop()
Layout Management
Different layout managers in Tkinter:
import tkinter as tk
root = tk.Tk()
root.title("Layout Management")
root.geometry("500x400")
# Pack layout
frame1 = tk.Frame(root, bg="lightblue", padx=10, pady=10)
frame1.pack(side=tk.TOP, fill=tk.X)
tk.Label(frame1, text="Pack Layout", bg="lightblue").pack()
tk.Button(frame1, text="Button 1").pack(side=tk.LEFT, padx=5)
tk.Button(frame1, text="Button 2").pack(side=tk.LEFT, padx=5)
tk.Button(frame1, text="Button 3").pack(side=tk.LEFT, padx=5)
# Grid layout
frame2 = tk.Frame(root, bg="lightgreen", padx=10, pady=10)
frame2.pack(side=tk.TOP, fill=tk.X)
tk.Label(frame2, text="Grid Layout", bg="lightgreen").grid(row=0, column=0, columnspan=3, pady=5)
tk.Label(frame2, text="Name:", bg="lightgreen").grid(row=1, column=0, sticky=tk.W, padx=5)
tk.Entry(frame2).grid(row=1, column=1, padx=5)
tk.Button(frame2, text="Submit").grid(row=1, column=2, padx=5)
tk.Label(frame2, text="Email:", bg="lightgreen").grid(row=2, column=0, sticky=tk.W, padx=5)
tk.Entry(frame2).grid(row=2, column=1, padx=5)
tk.Button(frame2, text="Clear").grid(row=2, column=2, padx=5)
# Place layout (absolute positioning)
frame3 = tk.Frame(root, bg="lightyellow", padx=10, pady=10)
frame3.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
tk.Label(frame3, text="Place Layout (Absolute)", bg="lightyellow").place(x=10, y=10)
tk.Button(frame3, text="Top-Left").place(x=20, y=40)
tk.Button(frame3, text="Center").place(relx=0.5, rely=0.5, anchor=tk.CENTER)
tk.Button(frame3, text="Bottom-Right").place(relx=1.0, rely=1.0, anchor=tk.SE, x=-10, y=-10)
root.mainloop()
Checkboxes and Radio Buttons
Selection widgets:
import tkinter as tk
def show_selections():
selected_languages = []
if python_var.get():
selected_languages.append("Python")
if java_var.get():
selected_languages.append("Java")
if cpp_var.get():
selected_languages.append("C++")
experience = experience_var.get()
result = f"Languages: {', '.join(selected_languages)}\nExperience: {experience}"
result_label.config(text=result)
root = tk.Tk()
root.title("Selection Widgets")
root.geometry("400x350")
# Checkboxes
tk.Label(root, text="Select Programming Languages:").pack(anchor=tk.W, padx=20, pady=5)
python_var = tk.BooleanVar()
java_var = tk.BooleanVar()
cpp_var = tk.BooleanVar()
tk.Checkbutton(root, text="Python", variable=python_var).pack(anchor=tk.W, padx=40)
tk.Checkbutton(root, text="Java", variable=java_var).pack(anchor=tk.W, padx=40)
tk.Checkbutton(root, text="C++", variable=cpp_var).pack(anchor=tk.W, padx=40)
# Radio buttons
tk.Label(root, text="Select Experience Level:").pack(anchor=tk.W, padx=20, pady=10)
experience_var = tk.StringVar(value="Beginner")
tk.Radiobutton(root, text="Beginner", variable=experience_var, value="Beginner").pack(anchor=tk.W, padx=40)
tk.Radiobutton(root, text="Intermediate", variable=experience_var, value="Intermediate").pack(anchor=tk.W, padx=40)
tk.Radiobutton(root, text="Advanced", variable=experience_var, value="Advanced").pack(anchor=tk.W, padx=40)
# Button and result
tk.Button(root, text="Show Selections", command=show_selections).pack(pady=20)
result_label = tk.Label(root, text="", justify=tk.LEFT)
result_label.pack(pady=10)
root.mainloop()
Listbox and Combobox
Selection from lists:
import tkinter as tk
from tkinter import ttk
def show_selected():
# Listbox selection
list_selection = listbox.curselection()
if list_selection:
selected_item = listbox.get(list_selection[0])
list_result.config(text=f"Listbox: {selected_item}")
# Combobox selection
combo_selection = combo_var.get()
combo_result.config(text=f"Combobox: {combo_selection}")
def add_to_list():
new_item = entry.get()
if new_item:
listbox.insert(tk.END, new_item)
entry.delete(0, tk.END)
root = tk.Tk()
root.title("List Widgets")
root.geometry("500x400")
# Listbox
tk.Label(root, text="Listbox:").pack(anchor=tk.W, padx=20, pady=5)
listbox = tk.Listbox(root, height=6, width=30)
listbox.pack(padx=20)
# Add some initial items
for item in ["Apple", "Banana", "Cherry", "Date", "Elderberry"]:
listbox.insert(tk.END, item)
# Entry and button to add items
entry_frame = tk.Frame(root)
entry_frame.pack(pady=10)
entry = tk.Entry(entry_frame, width=20)
entry.pack(side=tk.LEFT, padx=5)
tk.Button(entry_frame, text="Add Item", command=add_to_list).pack(side=tk.LEFT)
# Combobox
tk.Label(root, text="Combobox:").pack(anchor=tk.W, padx=20, pady=10)
combo_var = tk.StringVar()
combobox = ttk.Combobox(root, textvariable=combo_var, width=27)
combobox['values'] = ("Option 1", "Option 2", "Option 3", "Option 4", "Option 5")
combobox.current(0) # Set default selection
combobox.pack(padx=20)
# Show selections button
tk.Button(root, text="Show Selections", command=show_selected).pack(pady=20)
# Results
list_result = tk.Label(root, text="")
list_result.pack()
combo_result = tk.Label(root, text="")
combo_result.pack()
root.mainloop()
Menus and Menu Bars
Create application menus:
import tkinter as tk
from tkinter import messagebox, filedialog
def new_file():
text_area.delete(1.0, tk.END)
root.title("Untitled - Text Editor")
def open_file():
file_path = filedialog.askopenfilename(filetypes=[("Text files", "*.txt"), ("All files", "*.*")])
if file_path:
with open(file_path, 'r') as file:
content = file.read()
text_area.delete(1.0, tk.END)
text_area.insert(tk.END, content)
root.title(f"{file_path} - Text Editor")
def save_file():
file_path = filedialog.asksaveasfilename(defaultextension=".txt", filetypes=[("Text files", "*.txt"), ("All files", "*.*")])
if file_path:
content = text_area.get(1.0, tk.END)
with open(file_path, 'w') as file:
file.write(content)
root.title(f"{file_path} - Text Editor")
def about():
messagebox.showinfo("About", "Simple Text Editor\nBuilt with Tkinter")
def exit_app():
if messagebox.askyesno("Exit", "Do you want to exit?"):
root.quit()
root = tk.Tk()
root.title("Text Editor")
root.geometry("600x400")
# Create menu bar
menubar = tk.Menu(root)
root.config(menu=menubar)
# File menu
file_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="File", menu=file_menu)
file_menu.add_command(label="New", command=new_file, accelerator="Ctrl+N")
file_menu.add_command(label="Open", command=open_file, accelerator="Ctrl+O")
file_menu.add_command(label="Save", command=save_file, accelerator="Ctrl+S")
file_menu.add_separator()
file_menu.add_command(label="Exit", command=exit_app)
# Edit menu
edit_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="Edit", menu=edit_menu)
edit_menu.add_command(label="Cut", accelerator="Ctrl+X")
edit_menu.add_command(label="Copy", accelerator="Ctrl+C")
edit_menu.add_command(label="Paste", accelerator="Ctrl+V")
# Help menu
help_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="Help", menu=help_menu)
help_menu.add_command(label="About", command=about)
# Text area
text_area = tk.Text(root, wrap=tk.WORD, undo=True)
text_area.pack(fill=tk.BOTH, expand=True)
# Keyboard shortcuts
root.bind('', lambda e: new_file())
root.bind('', lambda e: open_file())
root.bind('', lambda e: save_file())
root.mainloop()
Dialog Boxes
Various dialog boxes for user interaction:
import tkinter as tk
from tkinter import messagebox, filedialog, colorchooser, simpledialog
def show_message_boxes():
# Info message
messagebox.showinfo("Info", "This is an information message")
# Warning message
messagebox.showwarning("Warning", "This is a warning message")
# Error message
messagebox.showerror("Error", "This is an error message")
# Question dialog
result = messagebox.askquestion("Question", "Do you want to continue?")
messagebox.showinfo("Answer", f"You answered: {result}")
# Yes/No dialog
result = messagebox.askyesno("Confirm", "Are you sure?")
messagebox.showinfo("Answer", f"You chose: {'Yes' if result else 'No'}")
def show_file_dialogs():
# Open file dialog
file_path = filedialog.askopenfilename(title="Select a file", filetypes=[("Text files", "*.txt"), ("All files", "*.*")])
if file_path:
messagebox.showinfo("Selected File", f"You selected: {file_path}")
# Save file dialog
save_path = filedialog.asksaveasfilename(title="Save file", defaultextension=".txt", filetypes=[("Text files", "*.txt")])
if save_path:
messagebox.showinfo("Save Location", f"File will be saved to: {save_path}")
def show_other_dialogs():
# Color chooser
color = colorchooser.askcolor(title="Choose color")
if color[1]:
messagebox.showinfo("Color", f"Selected color: {color[1]}")
# Simple input dialog
name = simpledialog.askstring("Input", "What's your name?")
if name:
messagebox.showinfo("Hello", f"Hello, {name}!")
# Integer input
age = simpledialog.askinteger("Input", "What's your age?", minvalue=0, maxvalue=150)
if age is not None:
messagebox.showinfo("Age", f"You are {age} years old")
root = tk.Tk()
root.title("Dialog Boxes Demo")
root.geometry("400x300")
tk.Label(root, text="Tkinter Dialog Boxes", font=("Arial", 16)).pack(pady=20)
tk.Button(root, text="Message Boxes", command=show_message_boxes).pack(pady=5)
tk.Button(root, text="File Dialogs", command=show_file_dialogs).pack(pady=5)
tk.Button(root, text="Other Dialogs", command=show_other_dialogs).pack(pady=5)
root.mainloop()
Canvas Widget
Drawing and graphics with Canvas:
import tkinter as tk
import random
def draw_random_shape():
canvas.delete("all") # Clear canvas
# Draw random shapes
for _ in range(10):
x1 = random.randint(0, 300)
y1 = random.randint(0, 200)
x2 = random.randint(x1, 400)
y2 = random.randint(y1, 300)
# Random color
color = f"#{random.randint(0, 0xFFFFFF):06x}"
# Random shape
shape_type = random.choice(["rectangle", "oval", "line"])
if shape_type == "rectangle":
canvas.create_rectangle(x1, y1, x2, y2, fill=color, outline="black")
elif shape_type == "oval":
canvas.create_oval(x1, y1, x2, y2, fill=color, outline="black")
else:
canvas.create_line(x1, y1, x2, y2, fill=color, width=2)
def draw_text():
canvas.create_text(200, 150, text="Hello Canvas!", font=("Arial", 20), fill="blue")
def clear_canvas():
canvas.delete("all")
root = tk.Tk()
root.title("Canvas Drawing")
root.geometry("500x400")
# Canvas widget
canvas = tk.Canvas(root, width=400, height=300, bg="white", relief=tk.SUNKEN, borderwidth=2)
canvas.pack(pady=20)
# Buttons
button_frame = tk.Frame(root)
button_frame.pack()
tk.Button(button_frame, text="Random Shapes", command=draw_random_shape).pack(side=tk.LEFT, padx=5)
tk.Button(button_frame, text="Draw Text", command=draw_text).pack(side=tk.LEFT, padx=5)
tk.Button(button_frame, text="Clear", command=clear_canvas).pack(side=tk.LEFT, padx=5)
# Mouse click event
def canvas_click(event):
x, y = event.x, event.y
canvas.create_oval(x-5, y-5, x+5, y+5, fill="red")
canvas.bind("", canvas_click)
root.mainloop()
Calculator Application
A complete calculator application:
import tkinter as tk
def button_click(number):
current = display.get()
display.delete(0, tk.END)
display.insert(0, current + str(number))
def button_clear():
display.delete(0, tk.END)
def button_equal():
try:
expression = display.get()
result = eval(expression)
display.delete(0, tk.END)
display.insert(0, result)
except:
display.delete(0, tk.END)
display.insert(0, "Error")
root = tk.Tk()
root.title("Calculator")
root.geometry("300x400")
root.resizable(False, False)
# Display
display = tk.Entry(root, width=25, font=("Arial", 20), justify=tk.RIGHT, borderwidth=5)
display.grid(row=0, column=0, columnspan=4, padx=10, pady=10)
# Button layout
buttons = [
('7', 1, 0), ('8', 1, 1), ('9', 1, 2), ('/', 1, 3),
('4', 2, 0), ('5', 2, 1), ('6', 2, 2), ('*', 2, 3),
('1', 3, 0), ('2', 3, 1), ('3', 3, 2), ('-', 3, 3),
('0', 4, 0), ('.', 4, 1), ('=', 4, 2), ('+', 4, 3),
]
# Create number and operator buttons
for (text, row, col) in buttons:
if text == '=':
btn = tk.Button(root, text=text, padx=20, pady=20, font=("Arial", 15), command=button_equal)
else:
btn = tk.Button(root, text=text, padx=20, pady=20, font=("Arial", 15),
command=lambda t=text: button_click(t) if t not in ['=', 'C'] else None)
btn.grid(row=row, column=col, sticky="nsew")
# Clear button
clear_btn = tk.Button(root, text='C', padx=20, pady=20, font=("Arial", 15), command=button_clear)
clear_btn.grid(row=5, column=0, columnspan=4, sticky="nsew")
# Configure grid weights for proper resizing
for i in range(6):
root.grid_rowconfigure(i, weight=1)
for i in range(4):
root.grid_columnconfigure(i, weight=1)
root.mainloop()
Best Practices
- Use descriptive variable names: Make your code readable
- Organize widgets in frames: Group related widgets together
- Use StringVar, IntVar, BooleanVar: For dynamic updates
- Handle exceptions properly: User input validation
- Use grid for complex layouts: More control than pack
- Separate UI and logic: Keep code organized
- Use styles and themes: Consistent appearance
- Test on different platforms: Tkinter behavior varies
- Use after() for periodic tasks: Instead of time.sleep()
- Destroy widgets properly: Memory management
Common Issues and Solutions
- Window not showing: Call mainloop() at the end
- Widgets not appearing: Use pack(), grid(), or place()
- Layout problems: Don't mix pack and grid in same container
- Threading issues: Use after() for GUI updates from threads
- Memory leaks: Properly destroy unused widgets
- Platform differences: Test on target platforms
- Performance issues: Avoid unnecessary updates
- Event handling: Use bind() for custom events
Tkinter provides a comprehensive set of tools for creating desktop applications. While it may not be as modern as some other GUI frameworks, it's included with Python and works well for most desktop application needs.