Графика в Python: рисование на Tkinter Canvas

Графика в Python: рисование на Tkinter Canvas Полезное

Canvas — это виджет библиотеки Tkinter, на котором программа на Python рисует 2D-графику: линии, фигуры, текст и изображения. Tkinter — стандартная кроссплатформенная библиотека графического интерфейса, она входит в поставку Python, ставить ее отдельно обычно не нужно.

Разберем по шагам: как создать окно и холст, как устроена система координат, как рисовать примитивы, задавать цвета, делать простую анимацию и реагировать на мышь. Каждый пример — законченный и запускаемый на обычном рабочем столе.

Важно про запуск: примерам нужна графическая среда (экран, оконный менеджер). В headless-окружении без дисплея вызов Tk() завершится ошибкой вида no display name and no $DISPLAY environment variable — при этом сам код синтаксически корректен, запускать его надо в графической системе.

Минимальный рабочий пример

Начнем с целого работающего окна, а потом разберем строки.

import tkinter as tk

root = tk.Tk()                       # главное окно приложения
root.title("Графика на Canvas")

canvas = tk.Canvas(root, width=400, height=300, bg="white")
canvas.pack()                        # размещаем холст в окне

# круг задается рамкой (150, 100)-(250, 200), центр примерно в (200, 150)
canvas.create_oval(150, 100, 250, 200, fill="red", outline="black", width=2)

root.mainloop()                      # запускаем цикл обработки событий

Что произойдет: откроется окно 400×300 px с белым фоном, в центре — красный круг с черным контуром толщиной 2 px. Окно живет, пока работает mainloop(); закрыть его можно крестиком.

Разберем ключевые строки:
— tk.Tk() создает главное окно приложения.
— tk.Canvas(...) создает холст: width и height в пикселях, bg — цвет фона.
— canvas.pack() размещает холст в окне (о менеджерах размещения — ниже).
— create_oval(...) рисует эллипс или круг: fill — заливка, outline — контур.
— mainloop() запускает цикл событий; без него окно откроется и сразу закроется.

Разместить виджет в окне можно тремя менеджерами: pack() укладывает блоки друг за другом, grid() расставляет по сетке строк и столбцов, place() ставит по точным координатам. Для холста хватает pack(); смешивать pack и grid в одном контейнере нельзя.

Система координат холста

Начало координат (0, 0) находится в левом ВЕРХНЕМ углу холста. Ось X растет вправо, ось Y растет ВНИЗ — в отличие от школьной геометрии, где Y идет вверх. Для холста 400×300 точка (400, 300) — это правый нижний угол.

Все методы рисования принимают координаты в пикселях от этого угла. Прямоугольник и овал задаются рамкой из двух точек: левый верхний угол (x1, y1) и правый нижний (x2, y2). Держать эту рамку в голове важнее всего — большинство ошибок новичка именно в перепутанных углах.

Примитивы: линии, фигуры, текст

Один холст, на нем все базовые фигуры сразу.

import tkinter as tk

root = tk.Tk()
canvas = tk.Canvas(root, width=400, height=300, bg="white")
canvas.pack()

# 1. Отрезок (20,20)-(380,20); пунктир: штрих 4 px, пропуск 2 px
canvas.create_line(20, 20, 380, 20, fill="blue", width=2, dash=(4, 2))

# 2. Ломаная по трем точкам - лишние пары координат продолжают линию
canvas.create_line(20, 60, 200, 260, 380, 60, fill="green", width=2)

# 3. Прямоугольник по рамке: угол (40,90) и угол (160,180)
canvas.create_rectangle(40, 90, 160, 180, fill="#ffd54f", outline="black")

# 4. Овал, вписанный в рамку (220,90)-(360,180)
canvas.create_oval(220, 90, 360, 180, fill="orange", outline="red", width=2)

# 5. Многоугольник (треугольник) по трем вершинам, замыкается сам
canvas.create_polygon(200, 200, 160, 280, 240, 280, fill="purple")

# 6. Текст; по умолчанию точка (200,40) - его центр
canvas.create_text(200, 40, text="Canvas", font=("Arial", 16), fill="black")

root.mainloop()

Что нарисуется на белом холсте:
— сверху — синяя пунктирная горизонтальная линия;
— зеленая V-образная ломаная из трех точек;
— слева — желтый прямоугольник с черным контуром;
— справа — оранжевый овал с красным контуром;
— снизу по центру — фиолетовый треугольник;
— вверху по центру — надпись «Canvas».

Коротко про методы:
— create_line(x1, y1, x2, y2, ...) — отрезок; если передать больше пар координат, получится ломаная. Опция dash=(4, 2) дает пунктир: штрих 4 px, пропуск 2 px.
— create_rectangle(x1, y1, x2, y2, ...) — прямоугольник по рамке из двух углов.
— create_oval(x1, y1, x2, y2, ...) — эллипс, вписанный в рамку; если рамка квадратная, выйдет круг.
— create_polygon(...) — многоугольник по списку вершин, контур замыкается автоматически.
— create_text(x, y, text=...) — текст; по умолчанию точка (x, y) — его центр (сдвинуть привязку можно опцией anchor).

Дуга — это часть овала, для нее отдельный метод:

canvas.create_arc(50, 50, 250, 250, start=0, extent=120, fill="skyblue")

Здесь start — начальный угол в градусах (отсчет против часовой стрелки от направления «на 3 часа»), extent — на сколько градусов раскрыть дугу. Пример даст голубой сектор («кусок пирога») на 120 градусов.

Цвета

Цвет задается двумя способами — именем или HEX-кодом.

canvas.create_rectangle(10, 10, 90, 90, fill="tomato")      # имя цвета
canvas.create_rectangle(110, 10, 190, 90, fill="#2196f3")   # HEX #RRGGBB

Имена цветов — это набор предопределенных названий (red, white, tomato, skyblue и другие). Запись #RRGGBB задает интенсивность красного, зеленого и синего двумя шестнадцатеричными разрядами каждый: #2196f3 — оттенок синего. Заливку контролирует fill, цвет и толщину контура — outline и width.

Анимация: move и after

Анимация в Tkinter — это сдвиг фигуры маленькими шагами через равные промежутки времени. Работают два метода:
— canvas.move(item, dx, dy) сдвигает фигуру с идентификатором item на dx по X и dy по Y;
— root.after(ms, func) планирует однократный вызов функции func через ms миллисекунд.

Чтобы движение продолжалось, функция в конце снова планирует саму себя через after — получается цикл на событийной модели. Обычный while тут не подойдет: он заблокировал бы интерфейс, и окно перестало бы отвечать.

import tkinter as tk

root = tk.Tk()
canvas = tk.Canvas(root, width=400, height=200, bg="white")
canvas.pack()

ball = canvas.create_oval(0, 80, 40, 120, fill="red")  # id фигуры
dx = 5

def move_ball():
    global dx
    x1, y1, x2, y2 = canvas.coords(ball)   # текущая рамка фигуры
    if x2 >= 400 or x1 <= 0:                # у правой или левой стенки
        dx = -dx                           # разворачиваем направление
    canvas.move(ball, dx, 0)               # сдвиг на dx по X
    root.after(30, move_ball)              # повторить через 30 мс

move_ball()
root.mainloop()

Что произойдет: красный шар едет вправо на 5 px за кадр, кадр раз в 30 мс (около 33 обновлений в секунду), а у правой и левой стенки меняет направление и отскакивает. Метод canvas.coords(item) возвращает текущие координаты рамки — по ним и проверяем столкновение.

Такой подход хорош для несложной анимации и мини-игр. Для крупных 2D-игр с множеством объектов и физикой удобнее Pygame — он для этого и создан.

Обработка событий мыши

Реакцию на мышь подключают методом bind: он связывает событие с функцией-обработчиком. Обработчик получает объект события, у которого есть поля x и y — координаты курсора внутри холста.

import tkinter as tk

root = tk.Tk()
canvas = tk.Canvas(root, width=400, height=300, bg="white")
canvas.pack()

def draw_dot(event):
    r = 5
    # event.x, event.y - точка клика внутри холста
    canvas.create_oval(event.x - r, event.y - r,
                       event.x + r, event.y + r, fill="black")

canvas.bind("<Button-1>", draw_dot)   # левая кнопка мыши
root.mainloop()

Что произойдет: каждый щелчок левой кнопкой ставит черную точку (круг радиусом 5 px) там, где был курсор. Частые обозначения событий: <Button-1> — левая кнопка, <Button-3> — правая, <B1-Motion> — движение с зажатой левой кнопкой (удобно для рисования от руки), <Motion> — просто перемещение курсора.

Выводы

  • Canvas — виджет Tkinter для 2D-графики; Tkinter входит в стандартную поставку Python, обычно устанавливать его не нужно.
  • Начало координат — в левом верхнем углу, ось Y направлена вниз; прямоугольник и овал задаются рамкой из двух противоположных углов.
  • Примитивы рисуют методами create_line, create_rectangle, create_oval, create_polygon, create_text и create_arc; цвет задают именем или HEX-кодом.
  • Анимация строится на паре move + after (событийный цикл), а реакция на мышь — на bind и координатах event.x / event.y.
  • Для схем, визуализаций и мини-игр Canvas достаточно; для крупных 2D-игр с физикой берут Pygame.

Где применяется / связь с практикой

Canvas пригодится для несложных инструментов: диаграмм и графиков, схем, простых редакторов фигур, обучающих визуализаций, небольших игр. Это удобный способ закрепить основы Python — функции, переменные, работу с событиями — на видимом результате, а не на абстрактных задачах.

Освойте тему на практике

Если хотите освоить язык системно, от синтаксиса до прикладных задач, посмотрите курс Python Basic. Оценить формат и уровень занятий без оплаты можно на открытых уроках.

Смежные темы: 10 полезных библиотек для Python, Потоки (threads) в Python.

FAQ

Нужно ли устанавливать Tkinter отдельно? На большинстве сборок он идет вместе с Python. Проверить можно командой python -m tkinter — откроется тестовое окно. В некоторых дистрибутивах Linux модуль ставится отдельным пакетом (например, python3-tk в Debian и Ubuntu).

Можно ли сохранить рисунок с холста в PNG? Штатно Canvas экспортирует только в PostScript через canvas.postscript(...). Для PNG обычно рисуют то же самое параллельно в изображении с помощью библиотеки Pillow и сохраняют его.

Почему фигура не двигается, хотя я поменял ее координаты в переменных? Пересчет обычных переменных сам по себе ничего не перерисовывает. Двигать уже нарисованную фигуру нужно по ее идентификатору — методом canvas.move(item, dx, dy) или canvas.coords(item, ...).

OTUS Журнал
Скидка 5% 14-20 сентября на курсы (popup)