Создаем Paint с нуля за вечер часть 1: окно, холст и простая кисть

12.12.2025 

Время прочтения: 20-25 минут.

Что нас ждёт

Начнём с самого простого: сделаем программу, которая вообще умеет рисовать линию мышкой. Пока нас не интересуют цвета, толщина кисти и пипетка — только белый лист и чёрный фломастер.
✅ Что сделаем: окно + холст + рисование мышью.
⏭️ Что будет дальше: выбор цвета, размер кисти, очистка, пипетка.

gif2 - 1

Создадим окно приложения

Представьте себе обычный лист бумаги на столе. Лист — это наш Canvas, специальный виджет Tkinter, на котором можно рисовать. Стол вокруг листа — это окно tk.Tk(). Сначала мы создадим «стол», потом положим на него «лист», а уже потом научим мышку оставлять следы на нём, как будто это маркер. Для начала нам нужно скачать и добавить модуль Tkinter, а также задать некоторые переменные. !!Добавить ссылку на наш гайд по скачке!!

import tkinter as tk
import time

# Цвета
green_color = "#00b259" # зеленый
orange_color = "#f15a31" # оранжевый
purple_color = "#bd56a0" # фиолетовый

Цвет в Python часто задают так: green_color="#00b259". Это HEX‑код (шестнадцатеричный формат): в нём “зашиты” доли красного, зелёного и синего — чтобы получить нужный цвет. Не переживайте: запоминать формулы не надо, достаточно знать, что это просто удобный способ выбрать любой оттенок.

Tkinter поможет нам при создании окна для игры. Команда root = tk.Tk() создаёт главное окно. Ему можно дать фон, чтобы белый холст красиво выделялся.

"""Создаём главное окно и всё, что на нём лежит."""

# Создаём главное окно программы
root = tk.Tk()  # главное окно
root.configure(bg=green_color)  # фон вокруг холста
root.title("Рисовалка (часть 1)")   

Результат на данном этапе: появляется окно с фоном, на котором скоро будет холст:

Photo1 - 1

Пустой холст

Теперь создадим пустой canvas — это будущий холст. Он делается через tk.Canvas(...): прямоугольная область, где мы задаём ширину, высоту и фон (то есть “цвет бумаги”). , размещаем холст внутри рамки и растягиваем его на всё доступное пространство.

"""Создаём холст для рисования и располагаем его в рамке."""
# Декоративная рамка вокруг холста, чтобы управлять его расположением отдельно
canvas_frame = tk.Frame(root)
canvas_frame.pack(side="top", fill="both", expand=True, padx=10, pady=10)
# Сам холст Canvas, на котором можно рисовать
canvas = tk.Canvas(
    canvas_frame, #располагаем его в нашей рамке
    width=900, # делаем базовые размеры холста 
    height=400,
    bg="white",  # фон – белый лист
)
canvas.pack(fill="both", expand=True) #чтобы наш холст растянулся на всё окно

Что тут важно в размещении canvas_frame.pack:

  • side="top" — рамка прижимается к верхней части окна.

  • expand=True и fill="both" — холст растягивается, если окно меняет размер (чтобы “лист” не был маленьким островком посреди экрана).

  • padx=10, pady=10 — отступы по краям, чтобы всё выглядело аккуратно и не “липло” к окну.

Вот получившийся холст: белая область внутри рамки — готовый “лист”:

photo2 - 1

Цикл игры — её сердце

Мы запустим программу и обработку событий “вручную”, чтобы почувствовать, как работает игровой цикл: программа постоянно крутится, обновляет окно и реагирует на действия пользователя.

Но если не поставить ограничение, цикл будет работать так быстро, как позволит компьютер — и вы получите не рисовалку, а тест на выживание вашего процессора. Поэтому добавляем маленькую паузу time.sleep(0.01) — 10 миллисекунд, чтобы контролировать частоту обновления.

Мы создадим переменную running = True, чтобы программа не завершалась сразу. Теперь после отрисовки интерфейса запускаем цикл: в каждом “кадре” обновляем Tkinter и обрабатываем события (мышь, закрытие окна и т. п.).

# Флаг для "игрового" цикла

running = True       # пока True — окно живёт

"""Игровой цикл: пока running == True, окно живёт и реагирует на события."""
while running:
    root.update()            # Обрабатываются события: клик мыши, движение, закрытие окна
    time.sleep(0.01)         # Небольшая пауза, чтобы не грузить процессор

Примерно так же работают и игры: постоянно крутясь в цикле, пересчитывают позиции объектов и перерисовывают кадр.

Мышь: нажал → веду → отпустил

С рисованием нам поможет Tkinter: он умеет вызывать наши функции, когда происходит событие (например, нажатие мышки). Для этого есть bind — “привязка”: мы говорим холсту, какую функцию вызывать на какое действие.

Перед запуском основного цикла привяжем:

  • нажали левую кнопку → on_mouse_down

  • ведём мышь с зажатой кнопкой → on_mouse_move

# Привязываем обработчики мыши:
# нажали кнопку, двигаем с зажатой кнопкой, отпустили кнопку
canvas.bind("<ButtonPress-1>", on_mouse_down)
canvas.bind("<B1-Motion>", on_mouse_move)

Tkinter передаёт в эти функции объект event . В нём лежат координаты мыши: event.x и event.y. По ним мы и будем рисовать. Линия на Canvas рисуется командой create_line(x1, y1, x2, y2, ...). : она добавляет на холст новый “кусочек линии”. Мы задаём координаты начала и концы, цвет и толщину. А для создания одной точки при нажатии мы используем create_oval(x1, y1, x2, y2, ...): в ней координаты используются для создания круга внутри заданной координатами квадратной рамки. Ещё сделаем закруглённые края и сглаживание, чтобы мазок выглядел приятнее (а не как след от табуретки) и сразу введём необходимые переменные.

# Зададим переменные  координат
last_x = None
last_y = None
# Параметры кисти
current_color = "#000000" # по умолчанию чёрный маркер
brush_size = 8 # толщина линии
def on_mouse_down(event):
    """Начинаем рисовать: запоминаем точку нажатия и ставим первую 'точку'."""
    global last_x, last_y # нужно для того чтобы мы меняли наши глобальные переменные, а не делали новые

    # запоминаем, где нажали
    last_x = event.x
    last_y = event.y

    # Рисуем совсем короткую линию длиной 1 пиксель, чтобы появился след
    canvas.create_oval(
        event.x - brush_size // 2 , event.y - brush_size // 2,     # левая верхняя точка
        event.x + brush_size // 2 , event.y + brush_size // 2,     # правая нижняя точка
        fill=current_color,   # цвет линии – текущий цвет кисти
    )

Идея простая: нажали мышь — запоминаем эту точку. Когда ведём мышь — каждый раз соединяем прошлую точку с новой короткой линией. Много маленьких отрезков подряд выглядят как плавный мазок. Отпустили кнопку — перестаём рисовать.

def on_mouse_move(event):
    """Продолжаем линию, если кнопка зажата и есть последняя точка."""
    global last_x, last_y

    if last_x is not None:
        # Рисуем отрезок от прошлой точки к новой
        canvas.create_line(
            last_x, last_y,         # точка начала линии
            event.x, event.y,       # точка конца линии
            fill=current_color,     # цвет
            width=brush_size,       # ширина
            capstyle="round",       # закругление краёв линии
            smooth=True             # легкое сглаживание
        )
        # Переносим "последнюю точку" в текущую позицию мыши
        last_x = event.x
        last_y = event.y 

Собираем всё вместе

Функций пока немного: мы просто рисуем чёрной кистью по белому листу. Но в следующей части это исправим.

gif3 - 1

import tkinter as tk
import time

def on_mouse_down(event):
    """Начинаем рисовать: запоминаем точку нажатия и ставим первую 'точку'."""
    global last_x, last_y

    last_x = event.x
    last_y = event.y

    # Рисуем совсем короткую линию длиной 1 пиксель, чтобы появился след
    canvas.create_oval(
        event.x - brush_size // 2 , event.y - brush_size // 2,     # левая верхняя точка
        event.x + brush_size // 2 , event.y + brush_size // 2,     # правая нижняя точка
        fill=current_color,   # цвет линии – текущий цвет кисти
    )


def on_mouse_move(event):
    """Продолжаем линию, если кнопка зажата и есть последняя точка."""
    global last_x, last_y

    if last_x is not None:
        # Рисуем отрезок от прошлой точки к новой
        canvas.create_line(
            last_x, last_y,         # точка начала линии
            event.x, event.y,       # точка конца линии
            fill=current_color,     # цвет
            width=brush_size,       # ширина
            capstyle="round",       # закругление краёв линии
            smooth=True             # легкое сглаживание
        )
        # Переносим "последнюю точку" в текущую позицию мыши
        last_x = event.x
        last_y = event.y

# Главное окно программы
root = None

# Параметры кисти
current_color = "#000000"   # чёрный цвет по умолчанию
brush_size = 8              # толщина кисти по умолчанию

# Цвета
green_color = "#00b259"    # зеленый
orange_color = "#f15a31"    # оранжевый
purple_color = "#bd56a0"    # фиолетовый
# Последняя точка, где была мышь при рисовании (None = не рисуем)
last_x = None
last_y = None

# Флаг для игрового цикла while
running = True
"""Создаёт окно и интерфейс."""
root = tk.Tk()     # Создаём главное окно программы
root.configure(bg=green_color)     # фон вокруг холста, чтобы белый "лист" выделялся

# Создаём все элементы интерфейса
"""Создаём рамку и холст для рисования."""
   
# Декоративная рамка вокруг холста, чтобы можно было просто контролировать его размер
canvas_frame = tk.Frame(root, bg=orange_color, bd=2, relief="ridge")
canvas_frame.pack(side="top", fill="both", expand=True, padx=10, pady=10)

# Сам холст Canvas, на котором можно рисовать
canvas = tk.Canvas(
    canvas_frame,
    width=900,
    height=400,
    bg="white",      # фон – белый лист
)
canvas.pack(fill="both", expand=True)

# Привязываем обработчики мыши:
# нажали кнопку, двигаем с зажатой кнопкой, отпустили кнопку
canvas.bind("<ButtonPress-1>", on_mouse_down)
canvas.bind("<B1-Motion>", on_mouse_move)
"""Игровой цикл: пока running == True, окно живёт и реагирует на события."""
while running:
    root.update()     # Обрабатываются события: клик мыши, движение, закрытие окна
    time.sleep(0.01)     # Небольшая пауза для контроля количества кадров

Пробное занятие
с репетитором по математике и программированию

Запишитесь сейчас бесплатно 
Записаться

Понравилась статья?

Подпишись на Телеграм школы, чтобы не пропустить новые статьи и новости
Telegram канал