Как создать простое приложение Paint на Python с Tkinter и Pillow
Важно: все исходные блоки кода сохранены и работоспособны в стандартной среде Python 3 при установленных пакетах tk и pillow.
Коротко о том, что вам понадобится
- Python 3.7+ (рекомендуется 3.8+). Если у вас Linux, убедитесь, что установлены системные пакеты для tkinter. На Windows и macOS Tkinter обычно поставляется вместе с Python.
- Пакеты: Tkinter (стандарт), Pillow.
Установка:
pip install tk pillowИзображение интерфейса

Краткое описание приложения
Это простое приложение Paint позволяет выбирать цвет кисти, менять размер, стирать, очищать холст, менять фон и сохранять изображение. Оно подходит как учебный проект, прототип для детских приложений и для быстрых аннотаций.
Главные модули и идеи
- Tkinter — стандартная библиотека для построения оконных приложений в Python. Виджеты: Label, Button, Canvas, Scale и т.д.
- Pillow (форк PIL) — для захвата экрана и манипуляций с изображениями.
- Canvas в Tkinter рисует примитивы (oval, line, rectangle, text, image). Мы используем create_oval для имитации мазка кисти.
Исходная структура кода приложения
Ниже приведена основная структура класса DrawApp и ключевые методы. Блоки кода сохранены в исходном виде и работают как концепт.
import tkinter as tk
from tkinter.ttk import Scale
from tkinter import colorchooser, filedialog, messagebox
import PIL.ImageGrab as ImageGrab
class DrawApp:
def __init__(self, root):
self.root = root
self.root.title("Kids' Paint App")
self.root.attributes("-fullscreen", True)
self.pointer = "black"
self.erase = "white"
self.setup_widgets()Далее мы настраиваем виджеты, палитру, кнопки и канву:
def setup_widgets(self):
self.title_label = tk.Label(
self.root,
text="Kids' Paint App",
font=("Comic Sans MS", 30),
bg="lightblue",
fg="purple",
)
self.title_label.pack(fill=tk.X, pady=10)
self.color_frame = tk.LabelFrame(
self.root,
text="Colors",
font=("Comic Sans MS", 15),
bd=5,
relief=tk.RIDGE,
bg="white",
)
self.color_frame.place(x=10, y=80, width=90, height=180)Палитра цветов и кнопки управления:
colors = [
"blue",
"red",
"green",
"orange",
"violet",
"black",
"yellow",
"purple",
"pink",
"gold",
"brown",
"indigo",
]
i, j = 0, 0
for color in colors:
tk.Button(
self.color_frame,
bg=color,
bd=2,
relief=tk.RIDGE,
width=3,
command=lambda col=color: self.select_color(col),
).grid(row=i, column=j, padx=2, pady=2)
i += 1
if i == 4:
i = 0
j = 1Кнопки «Ластик», «Очистить», «Сохранить» и «Фон», а также слайдер размера:
self.eraser_btn = tk.Button(
self.root,
text="Eraser",
bd=4,
bg="white",
command=self.eraser,
width=9,
relief=tk.RIDGE,
font=("Comic Sans MS", 12),
)
self.eraser_btn.place(x=10, y=310)
self.clear_screen_btn = tk.Button(
self.root,
text="Clear Screen",
bd=4,
bg="white",
command=self.clear_screen,
width=12,
relief=tk.RIDGE,
font=("Comic Sans MS", 12),
)
self.clear_screen_btn.place(x=10, y=370)
self.save_as_btn = tk.Button(
self.root,
text="Save Drawing",
bd=4,
bg="white",
command=self.save_as,
width=12,
relief=tk.RIDGE,
font=("Comic Sans MS", 12),
)
self.save_as_btn.place(x=10, y=430)
self.bg_btn = tk.Button(
self.root,
text="Background",
bd=4,
bg="white",
command=self.canvas_color,
width=12,
relief=tk.RIDGE,
font=("Comic Sans MS", 12),
)
self.bg_btn.place(x=10, y=490)
self.pointer_frame = tk.LabelFrame(
self.root,
text="Size",
bd=5,
bg="white",
font=("Comic Sans MS", 15, "bold"),
relief=tk.RIDGE,
)Слайдер размера и привязка мыши к функции рисования:
self.pointer_frame.place(x=10, y=580, height=150, width=70)
self.pointer_size = Scale(
self.pointer_frame, orient=tk.VERTICAL, from_=48, to=1, length=120
)
self.pointer_size.set(1)
self.pointer_size.grid(row=0, column=1, padx=15)
self.canvas = tk.Canvas(
self.root, bg="white", bd=5, relief=tk.GROOVE, height=650, width=1300
)
self.canvas.place(x=160, y=120, anchor="nw")
self.canvas.bind("", self.paint) Как работает рисование
Метод paint рисует маленькие овалы по координатам курсора, имитируя мазок кисти. Здесь вы можете менять форму — рисовать линии, прямоугольники или полигоны.
def paint(self, event):
x1, y1 = (event.x - 2), (event.y - 2)
x2, y2 = (event.x + 2), (event.y + 2)
self.canvas.create_oval(
x1,
y1,
x2,
y2,
fill=self.pointer,
outline=self.pointer,
width=self.pointer_size.get(),
)Функции выбора цвета, ластика и очистки холста:
def select_color(self, col):
self.pointer = col
def eraser(self):
self.pointer = self.erase
def clear_screen(self):
self.canvas.delete("all")Изменение фонового цвета холста (цветовой диалог возвращает RGB и HEX):
def canvas_color(self):
color = colorchooser.askcolor()
if color:
self.canvas.configure(background=color[1])
self.erase = color[1]Сохранение изображения: захват экрана + обрезка области холста и запись файла.
def save_as(self):
file_path = filedialog.asksaveasfilename(
defaultextension=".jpg", filetypes=[("Image files", "*.jpg")]
)
if file_path:
try:
y = 148
x = 200
y1 = 978
x1 = 1840
ImageGrab.grab().crop((x, y, x1, y1)).save(file_path)
messagebox.showinfo("Save Drawing", "Image file saved successfully!")
except Exception as e:
messagebox.showerror("Error", f"Failed to save the image file: {e}")Запуск приложения:
if __name__ == "__main__":
root = tk.Tk()
app = DrawApp(root)
root.mainloop()Тестирование: как проверить функциональность
После запуска вы увидите палитру, кнопки, слайдер и холст.

Демонстрация рисования разными цветами:

Увеличьте размер ластика и потестируйте его работу:

Изменение фона и очистка холста:

Сохранение результата:

Как улучшить приложение — практические идеи
Ниже — список конкретных улучшений и краткие указания по реализации.
- Форма кисти: добавить выбор между круглой и плоской кистью, рисование линиями (create_line) с параметрами smooth.
- Непрозрачность (alpha): сохранить временный слой в Pillow и смешивать, либо рисовать примитивы с предварительно вычисленным цветом с альфой (Tkinter в чистом виде не поддерживает альфу для canvas-примитивов — нужна отрисовка в Pillow и обновление изображения на canvas).
- Отмена/Повтор (Undo/Redo): хранить стек операций (списки id объектов на холсте) и по операции удалять/восстанавливать объекты.
- Добавление текста и стикеров: использовать create_text и create_image; для изображений — поддерживать загрузку PNG с прозрачностью через PhotoImage.
- Масштабирование и переворот изображений: использовать Pillow (resize, transpose) и обновлять изображение на canvas.
Пример: реализация Undo простым стеком
- При каждой операции рисования накапливайте id добавленных примитивов в группу и пушьте группу в стек undo.
- Для undo извлекайте последнюю группу и удаляйте все id из неё.
- Для redo храните второй стек, куда перемещаете группы при undo.
Это даёт базовую историю действий без полного снимка холста.
Когда такой подход не подходит
- Для профессиональных графических редакторов с поддержкой слоёв, слоёв с blend-mode и миллионными кистями лучше использовать специализированные движки и библиотеки (например, PyQt/PySide с QPainter или интеграция с C++-библиотеками).
- Для сенсорных приложений на мобильных платформах стоит рассмотреть Kivy или нативные SDK.
Альтернативные подходы и инструменты
- PyQt / PySide: более мощный набор виджетов и расширенные возможности рендеринга.
- Kivy: подходит для мобильных приложений и сенсорного ввода, поддерживает мультитач.
- Web-приложение: HTML5 canvas + JS — удобно для кроссплатформенного распространения.
Ментальные модели и эвристики для разработки графических редакторов
- Модель слоёв: представьте холст как стек слоёв; каждая операция рисования — изменение верхнего слоя.
- Политика сохранения: экономьте память, сохраняя диффы операций, если важна история undo.
- Производительность: рисуйте только изменённые области (dirty rectangles), когда холст становится большим.
Критерии приёмки
- Приложение открывается и не падает при базовых действиях: рисование, смена цвета, стирание, очистка и сохранение изображения.
- Сохранённый файл открывается в стандартных просмотрщиках и содержит ожидаемую область холста.
- Размер кисти изменяется корректно, цвет и фон применяются моментально.
- Undo/Redo (если реализовано) корректно откатывают/возвращают последние операции.
Чек-листы по ролям
Разработчик:
- Запустить приложение на целевой платформе.
- Покрыть edge-case: нулевой или очень большой размер кисти.
- Обработать отказ сохранения (нет прав записи).
Тестировщик:
- Проверить корректность обрезки при сохранении (координаты x,y,x1,y1).
- Проверить работу colorchooser на разных ОС.
- Тестировать производительность при плотном рисовании.
Дизайнер/UX:
- Проверить читаемость кнопок и меток при разных DPI.
- Убедиться, что элементы управления доступны детям (если это детское приложение).
Тестовые случаи и приёмочные критерии
- Рисование: удерживая левую кнопку и двигая мышью, на холсте появляются мазки соответствующего цвета и размера.
- Ластик: при нажатии на Eraser холст стирается цветом фона; ширина ластика соответствует ползунку.
- Очистка: Clear Screen удаляет все объекты с холста.
- Сохранение: при выборе пути файл создаётся, размер и содержимое соответствуют видимой области холста.
- Смена фона: Background открывает диалог цвета и изменяет цвет холста и поведение ластика.
Практические советы по локализации и совместимости (для русскоязычных пользователей)
- Шрифты: для русских текстов избегайте шрифтов, не содержащих кириллицу. Comic Sans MS содержит кириллицу, но для профессионального интерфейса лучше использовать системные шрифты (например, Arial, Tahoma) или локализованные семейства.
- Кодировка: убедитесь, что файлы с ресурсами сохранены в UTF-8.
- Путь сохранения: на Windows пути могут содержать кириллицу; filedialog.asksaveasfilename корректно возвращает такие пути при использовании UTF-8.
Безопасность и приватность
- Сохранение изображений выполняется локально; не отправляйте пользовательские рисунки на удалённые серверы без явного согласия.
- При вызове filedialog всегда проверяйте возвращаемое значение и обработайте возможные исключения записи на диск.
Примеры расширений: кодовые сниппеты
Пример: рисование сглаженных линий вместо множества овалов (упрощённо):
# внутри класса DrawApp
self.last_x, self.last_y = None, None
self.canvas.bind('', self.on_button_down)
self.canvas.bind('', self.draw_line)
def on_button_down(self, event):
self.last_x, self.last_y = event.x, event.y
def draw_line(self, event):
if self.last_x is not None and self.last_y is not None:
self.canvas.create_line(self.last_x, self.last_y, event.x, event.y,
fill=self.pointer, width=self.pointer_size.get(), capstyle=tk.ROUND, smooth=True)
self.last_x, self.last_y = event.x, event.y Этот подход даёт более плавный мазок и меньше объектов на холсте.
Mini‑методология разработки — быстрый план проекта (микро- roadmap)
- Прототип: реализовать рисование, палитру и сохранение.
- Стабилизация: обработка ошибок, тесты на разных ОС.
- Удобство: добавить undo/redo, выбор кисти, прозрачность.
- Производительность: оптимизация отрисовки и уменьшение количества объектов.
- Локализация: перевести UI, проверить шрифты и кодировки.
Краткий справочник терминов
- Canvas — виджет для рисования примитивов.
- Pillow — библиотека для обработки изображений в Python.
- ImageGrab — класс Pillow для захвата экрана.
- B1-Motion — событие «левая кнопка мыши + движение».
Резюме
- Простое приложение Paint на Tkinter — отличный учебный проект, позволяющий изучить GUI, обработку событий и работу с изображениями через Pillow.
- Основные функции: выбор цвета, масштаб кисти, ластик, очистка, смена фона, сохранение.
- Для продвинутого функционала (слои, альфа, производительность) рассмотрите использование Pillow для рендеринга и/или переход на PyQt/Kivy.
Дополнительные идеи: интеграция с мультитачем, экспорт в PNG с прозрачностью, плагины для кистей и пресетов.
Похожие материалы
Убрать рекламу в мобильных играх на iPhone и iPad
Как посмотреть подписчиков на YouTube
Как обновить драйверы AMD Radeon в Windows 11
Как использовать старый ПК как домашний медиа‑сервер
Защита от зеркалирования смартфона