Tkinter Canvas Drawing
Deep dive · part of Python Tkinter
The Canvas widget draws lines, polygons, ovals, text, images and arbitrary shapes. Items get integer IDs so you can move, recolour or delete them later. Great for charts, games and diagrams.
Canvas is a drawable surface: create_rectangle, create_line, create_oval, create_text, create_image return item ids for later coords, itemconfig, and delete. Ideal for charts, simple games, and diagram editors without external graphics libs.
Double buffering for animation may use Canvas.postscript or offscreen techniques; for smooth games consider pygame; Canvas suffices for static and light interactive UIs.
Production code combines this topic with logging, tests, and clear module boundaries so refactors stay safe when requirements grow.
Item ids are integers; coords lists update geometry with coords(id, *xy).
tag_bind attaches events to groups of items sharing tags.
scrollregion and xscrollcommand enable pan/zoom on large diagrams.
create_line with smooth option approximates curves.
PhotoImage supports GIF/PNG on screen—reference must persist in Python variable.
bbox('all') computes bounding box for fit-to-view.
Click-drag drawing stores last pointer position in a list or instance var, binding
Performance: thousands of items slow redraw—simplify geometry or use matplotlib for heavy plots embedded via FigureCanvasTkAgg.
Export via postscript for print; for PNG use PIL or render paths offscreen for high-DPI charts.
Tag related canvas items so move and delete operations affect whole diagram components together.
Read the parent tutorial on pythondeck.com for runnable snippets, then reproduce them locally in a virtual environment with pinned dependency versions matching your deployment target.
When pairing with teammates, agree on one idiomatic pattern per concern—mixed styles in one repo slow reviews and invite subtle integration bugs during merges.
Garbage-collecting PhotoImage when no Python reference—image disappears.
Forgetting to update scrollregion after adding items off-screen.
Mixing float coords inconsistently causing blurry lines on HiDPI.
Blocking mainloop in draw handlers.
Keep item ids in dict keyed by model object for updates.
Use tags for selectable groups in diagram editors.
Set canvas width/height or expand=True in grid for resize.
Factor chart drawing into redraw(data) called on resize events.
Re-read the examples below with these ideas in mind; change variable names and inputs to match your own project.
The program below demonstrates bar chart. Read the comments on each line, run the code, then change names or values to see how the output shifts.
# Example: Bar chart
# Run in the REPL or save as a .py file and execute with python.
import tkinter as tk
root = tk.Tk(); root.title("Chart")
W, H = 300, 200
c = tk.Canvas(root, width=W, height=H, bg="white")
c.pack()
values = [40, 90, 120, 60, 150]
bw = W // (len(values) * 2)
for i, v in enumerate(values):
x0 = (2*i + 1) * bw
c.create_rectangle(x0, H - v, x0 + bw, H, fill="#2b4a73")
c.create_text(x0 + bw/2, H - v - 8, text=str(v), font=("Arial", 9))
root.mainloop()
This sample walks through click to draw in a small, runnable script. Paste it into the REPL or save it as a .py file before you continue to the next block.
# Example: Click to draw
# Run in the REPL or save as a .py file and execute with python.
import tkinter as tk
root = tk.Tk()
c = tk.Canvas(root, width=400, height=300, bg="white")
c.pack()
last = [None]
def on_click(e):
if last[0]:
c.create_line(*last[0], e.x, e.y, width=2)
last[0] = (e.x, e.y)
c.bind("<Button-1>", on_click)
root.mainloop()
Here is a hands-on illustration of bar chart. Follow the inline comments first; only then execute the snippet and compare the result with what you expected.
# Canvas draws vector shapes with create_* methods
import tkinter as tk # tk
root = tk.Tk(); root.title("Chart") # window
c = tk.Canvas(root, width=260, height=160, bg="white") # drawing surface
c.pack() # layout
vals = [40, 90, 60] # bar heights
bw = 40 # bar width
for i, v in enumerate(vals): # each bar
x0 = 20 + i * (bw + 20) # left x
c.create_rectangle(x0, 150 - v, x0 + bw, 150, fill="#2b4a73") # bar
c.create_text(x0 + bw/2, 150 - v - 8, text=str(v)) # label
print("canvas bars drawn") # no mainloop
The program below demonstrates click draw. Read the comments on each line, run the code, then change names or values to see how the output shifts.
# Bind mouse events to update canvas items
import tkinter as tk # tkinter
root = tk.Tk() # window
c = tk.Canvas(root, width=300, height=200, bg="white") # canvas
c.pack() # show
last = [None] # mutable last point
def on_click(e): # mouse handler
if last[0]: c.create_line(*last[0], e.x, e.y, width=2) # segment
last[0] = (e.x, e.y) # remember point
c.bind("<Button-1>", on_click) # left click
print("click handler bound") # done
Related deep dives on Python Tkinter: