Как создать простое приложение Paint на Python с Tkinter и Pillow

Простое приложение Paint — один из самых распространённых видов программного обеспечения на настольных компьютерах. Оно позволяет рисовать без страха ошибиться, быстро выборать любой цвет и мгновенно менять толщину мазков. Такое приложение удобно для создания логотипов, концептов интерфейса или для аннотаций на диаграммах.
В этой статье вы узнаете, как собрать работающее приложение Paint на Python, как устроены основные части кода, какие возможности можно добавить и как тестировать и распространять программу.
Что потребуется для работы
- Python 3.6+ (рекомендуется последняя стабильная версия).
- Модули Tkinter (обычно поставляется с Python) и Pillow.
Pояснение: Tkinter — стандартная библиотека для создания оконных приложений в Python. Pillow — форк PIL, библиотека для работы с изображениями.
Установка (в терминале):
pip install tk pillowВажно: на некоторых системах (особенно на Linux) пакет tk может устанавливаться через менеджер пакетов системы (apt, dnf и т. п.). Если import tkinter не работает, установите системный пакет, например, sudo apt install python3-tk.
Структура приложения и основной класс
Приложение организовано как класс DrawApp, который инициализирует окно, настраивает элементы управления и канву для рисования. Ниже — сокращённый и отформатированный пример кода, полностью рабочий при правильной вставке в файл .py.
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()В этом фрагменте мы:
- задаём заголовок окна;
- разворачиваем окно во весь экран (можно убрать для оконного режима);
- устанавливаем текущий цвет рисования и цвет ластика;
- вызываем метод setup_widgets, который создаёт интерфейс.
Виджеты интерфейса: панель цветов, кнопки, слайдер и канва
Метод 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")Выбор фона происходит через стандартный диалог colorchooser. Метод возвращает кортеж (RGB, hex). При выборе фона ластик подстраивается под новый цвет фона.
def canvas_color(self):
color = colorchooser.askcolor()
if color:
self.canvas.configure(background=color[1])
self.erase = color[1]Сохранение изображения с помощью Pillow
Для сохранения используется PIL.ImageGrab: делается снимок экрана, затем он обрезается по координатам, соответствующим канве. Координаты в примере подобраны для определённого разрешения — их нужно подогнать под ваше окно.
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}")Совет: вместо глобального ImageGrab можно экспортировать содержимое канвы в postscript и затем конвертировать через Pillow, что часто даёт более предсказуемый результат и не зависит от координат экрана.
Запуск приложения
В самом конце создаём экземпляр Tk и запускаем цикл событий:
if __name__ == "__main__":
root = tk.Tk()
app = DrawApp(root)
root.mainloop()Тестирование встроенных функций
При запуске вы увидите приложение с палитрой, четырьмя кнопками, слайдером и холстом для рисования:
- Выберите цвет и рисуйте левой кнопкой мыши.
- Нажмите «Eraser» и измените размер ластика, передвинув слайдер вверх.
- Кнопка «Clear Screen» очищает холст. Кнопка «Background» открывает палитру для смены фона.
- Сохранение рисунка вызывает диалог выбора файла и сохраняет изображение.
Как можно улучшить приложение
Ниже — идеи и рекомендации, которые расширят функциональность и надёжность приложения.
- Добавить выбор формы кисти: круглая, квадратная, линия. Используйте create_rectangle, create_line или create_polygon.
- Добавить прозрачность кисти (алфа) с композитингом через Pillow при сохранении.
- Реализовать слои: хранить объекты в отдельных группах, рендерить их по очереди, сохранять как объединённое изображение.
- Добавить Undo/Redo: ведите стек действий и откатывайте/восстанавливайте последние команды.
- Поддержка штампов и стикеров: загрузка PNG с прозрачностью и вставка на канву.
- Инструменты трансформации: масштабирование и поворот объектов; используйте Pillow для изменения изображений.
Альтернативные подходы к реализации
- PyQt/PySide: даёт более современный вид UI и сильные графические возможности (QPainter). Подходит для более сложных интерфейсов.
- Kivy: кроссплатформенный фреймворк, удобен для сенсорных экранов и мобильных устройств.
- Веб-версия: HTML5 canvas + Flask/Django на бэкенде. Позволяет легко поделиться приложением через браузер.
- Использовать Cairo/Skia для аппаратного рендеринга и более точного управления векторной графикой.
Когда выбирать: для простых настольных утилит — Tkinter; для коммерческого продукта с насыщенной графикой — PyQt/PySide или нативная графика.
Ментальные модели и эвристики при проектировании UX
- «Путь к цели» — минимизируйте количество кликов до действия (цвет → кисть → рисовать).
- «Обратимость» — всегда делайте Undo удобным и быстрым.
- «Предсказуемость» — показ текущего цвета и размера кисти рядом с курсором или в интерфейсе.
- «Доступность» — клавиатурные сокращения и крупные кнопки для детей и тач-экранов.
Мини‑методология разработки (быстрая итерация)
- Прототип: запустить базовый canvas + рисование «точками».
- Добавить селектор цвета и размер кисти.
- Реализовать базовые кнопки (clear, save, background).
- Тестировать UX на целевой аудитории (дети/взрослые дизайнеры).
- Добавлять слои и Undo, затем оптимизировать сохранение и экспорт.
Роль‑ориентированные чеклисты
Для разработчика:
- Проверить корректность импорта tkinter и pillow.
- Обеспечить обработку исключений при сохранении.
- Настроить дебаг логирование для координат канвы.
Для дизайнера:
- Подобрать читаемую цветовую палитру.
- Оценить размеры кнопок и шрифты для целевой аудитории.
Для тестировщика:
- Протестировать рисование при разной скорости перемещения мыши.
- Проверить сохранение на разных разрешениях экрана.
- Проверить работу диалогов выбора файлов и цвета.
Критерии приёмки
- Приложение запускается без ошибок на целевой системе.
- Можно рисовать и изменять толщину линии.
- Цвета палитры применяются корректно.
- Сохранение создаёт корректный файл изображения.
Тестовые сценарии и примеры приёмки
- Нарисовать несколько разноцветных линий, сохранить файл, открыть его во внешнем просмотре — изображение соответствует.
- Изменить фон на контрастный цвет и проверить, что ластик стирает до этого цвета.
- Быстро проводить мышью по холсту — не должно быть пропусков в линии.
- Отмена/возврат действий (если реализовано) — проверка стеков.
Совместимость и рекомендации по платформам
- Windows: ImageGrab работает стабильно.
- macOS: ImageGrab требует разрешений на запись экрана; запросите у пользователя разрешение.
- Linux: ImageGrab может не работать на всех окружениях; используйте экспорт в postscript из Canvas и конвертацию через Pillow.
Совет: при кроссплатформенной поддержке реализуйте два способа сохранения и выбирайте подходящий в рантайме.
Безопасность и приватность
- При сохранении файлов используйте filedialog.asksaveasfilename, чтобы пользователь сам указал путь.
- Не отправляйте снимки экрана на сторонние сервисы без явного согласия.
- Для корпоративного использования проверьте требования к хранению и передаче изображений (GDPR/локальные правила).
Когда этот подход не подходит
- Если вам нужен векторный редактор со слоями и трансформациями — лучше использовать инструменты, ориентированные на вектор (SVG, Cairo, Skia).
- Для мобильных приложений предпочтительнее Kivy или нативные SDK.
Быстрый чек-лист улучшений и приоритеты (Impact × Effort)
- Undo/Redo — высокий эффект, средняя сложность.
- Слои — высокий эффект, высокая сложность.
- Поддержка прозрачности и PNG — средний эффект, низкая сложность.
- Выбор формы кисти — средний эффект, низкая сложность.
Короткое руководство по миграции на PyQt (если нужно)
- Заменить tkinter.Canvas на QWidget с перехватом paintEvent.
- Использовать QPainter для рисования линий и управления слоями.
- Для сохранения использовать QImage.save или экспорт в Pillow при необходимости.
Итог и дальнейшие шаги
Важно: начните с минимального набора функций и добавляйте улучшения по мере обратной связи. Тестируйте сохранение на целевых платформах и убедитесь в корректной обработке диалогов.
Ключевые направления развития: поддержка Undo, слоёв, различных типов кистей и экспорт в более формальные форматы (PNG с прозрачностью, SVG для векторной графики).
Завершая, напомню: код в статье — рабочая отправная точка. Подгоните координаты сохранения, адаптируйте интерфейс под размер окна и добавляйте функции, которые важны именно для вашей аудитории.
Похожие материалы
Как изменить голос Alexa и настройки речи
Установить VirtualBox и создать VM на Linux
Как пожаловаться на пользователя в Snapchat
Связывание заметок в Apple Notes на iPhone
Лазерная гравировка по стеклу — практическое руководство