Языки программирования

Библиотека Tkinter: Разработка программы Paint

В этой статье мы разработаем простую «рисовалку» на Python. Программа будет примитивная, но мы потренируемся в создании макетов компоновки в tkinter, передаче аргументов в функцию-обработчик, а также применим анонимные функции.

Класс Paint

В нашей программе мы будем применять ООП и поэтому создаем класс и называем его «Paint». Листинг:
from tkinter import *
from tkinter.messagebox import *

# класс Paint
class Paint(Frame):
    def __init__(self, parent):
        Frame.__init__(self, parent)
        self.parent = parent


# выход из программы  
def close_win():
  if askyesno("Выход", "Вы уверены?"):
    root.destroy()

# вывод справки    
def about():
  showinfo("Demo Paint", "Простейшая рисовалка от сайта: https://it-black.ru")
  
# функция для создания главного окна
def main():
    global root
    root = Tk()
    root.geometry("800x600+300+300")
    app = Paint(root)
    m = Menu(root)
    root.config(menu=m)

    fm = Menu(m)
    m.add_cascade(label="Файл", menu=fm)
    fm.add_command(label="Выход", command=close_win)

    hm = Menu(m)
    m.add_cascade(label="Справка", menu=hm)
    hm.add_command(label="О программе", command=about)
    root.mainloop()

if __name__ == "__main__":
    main()
Данный код создает каркас для нашей будущей программы. Далее мы создаем для нашего класса функцию setUI, в которой будем задавать расположение всех кнопок, меток и самого холста — поля для рисования. Листинг:
def setUI(self):
  # Устанавливаем название окна
  self.parent.title("Demo Paint")
  # Размещаем активные элементы на родительском окне
  self.pack(fill=BOTH, expand=1)

  self.columnconfigure(6, weight=1)
  self.rowconfigure(2, weight=1)

  # Создаем холст с белым фоном
  self.canv = Canvas(self, bg="white")

  # Приклепляем канвас методом grid. Он будет находится в 3м ряду, первой колонке,
  # и будет занимать 7 колонок, задаем отступы по X и Y в 5 пикселей, и
  # заставляем растягиваться при растягивании всего окна

  self.canv.grid(row=2, column=0, columnspan=7, padx=5, pady=5, sticky=E + W + S + N)

  # создаем метку для кнопок изменения цвета кисти
  color_lab = Label(self, text="Цвет: ")

  # Устанавливаем созданную метку в первый ряд и первую колонку,
  # задаем горизонтальный отступ в 6 пикселей
  color_lab.grid(row=0, column=0, padx=6)

  # создание кнопки: установка текста кнопки, задание ширины кнопки (10 символов)
  red_btn = Button(self, text="Красный", width=10)

  # устанавливаем кнопку в первый ряд, вторая колонка
  red_btn.grid(row=0, column=1)

  # по аналогии создаем остальные кнопки
  green_btn = Button(self, text="Зеленый", width=10)
  green_btn.grid(row=0, column=2)

  blue_btn = Button(self, text="Синий", width=10)
  blue_btn.grid(row=0, column=3)

  black_btn = Button(self, text="черный", width=10)
  black_btn.grid(row=0, column=4)

  white_btn = Button(self, text="Белый", width=10)
  white_btn.grid(row=0, column=5)

  # Создаем метку для кнопок изменения размера кисти
  size_lab = Label(self, text="Размер кисти: ")
  size_lab.grid(row=1, column=0, padx=5)
  one_btn = Button(self, text="2x", width=10)
  one_btn.grid(row=1, column=1)

  two_btn = Button(self, text="5x", width=10)
  two_btn.grid(row=1, column=2)

  five_btn = Button(self, text="7x", width=10)
  five_btn.grid(row=1, column=3)

  seven_btn = Button(self, text="10x", width=10)
  seven_btn.grid(row=1, column=4)

  ten_btn = Button(self, text="20x", width=10)
  ten_btn.grid(row=1, column=5)

  twenty_btn = Button(self, text="50x", width=10)
  twenty_btn.grid(row=1, column=6, sticky=W)
В метод __init__ нужно добавить вызов этого метода:
self.setUI()

Разработка метода draw()

Разработаем метод для рисования на холсте. В метод __init__ нужно добавить параметры кисти по умолчанию:
self.brush_size = 10
self.brush_color = "red"
self.color = "red"
Листинг метода draw():
def draw(self, event):
        self.canv.create_oval(event.x - self.brush_size,
                              event.y - self.brush_size,
                              event.x + self.brush_size,
                              event.y + self.brush_size,
                              fill=self.color, outline=self.color)
Рисование осуществляется путем создания кругов на холсте: пользователь зажимает левую кнопку мыши и при движении мышью, по пути следования курсора будут отрисовываться круги.

Осталось только привязать к холсту обработку только что созданного метода. Код:
self.canv.bind("<B1-Motion>", self.draw)

Изменение цвета и размера кисти

Разработаем метод изменения цвета кисти:
def set_color(self, new_color):
  self.color = new_color
После этого в каждой кнопке верхнего ряда следует добавить код обработки нажатия этой кнопки по следующему шаблону:
red_btn = Button(self, text="Красный", width=10, command=lambda: self.set_color("red"))
red_btn.grid(row=0, column=1)
Код «command=lambda: self.set_color(«red»)» привязывает функцию с нужным нам аргументом к кнопке. Используем lambda-функцию т.к. без lambda функция вызовется сразу при создании кнопки, а не только при ее нажатии.

Рассмотрим метод изменения размера кисти:
def set_brush_size(self, new_size):
  self.brush_size = new_size
Модернизируем код каждой кнопки нижнего ряда по следующему шаблону:
one_btn = Button(self, text="2x", width=10, command=lambda: self.set_brush_size(2))
Осталось добавить кнопку «Очистить» для нашей программы. Данная кнопка будет очищать холст. Листинг:
clear_btn = Button(self, text="Очистить", width=10, command=lambda: self.canv.delete("all"))
clear_btn.grid(row=0, column=6, sticky=W)

Полный листинг программы

Разработка нашей программы закончилась. Соберем всю нашу программу в один файл и проверим на работоспособность. Листинг:
from tkinter import *
from tkinter.messagebox import *

# класс Paint
class Paint(Frame):
    def __init__(self, parent):
        Frame.__init__(self, parent)
        self.parent = parent
        # Параметры кисти по умолчанию
        self.brush_size = 10
        self.brush_color = "red"
        self.color = "red"
        # Устанавливаем компоненты UI
        self.setUI()

        # Метод рисования на холсте

    def draw(self, event):
        self.canv.create_oval(event.x - self.brush_size,
                              event.y - self.brush_size,
                              event.x + self.brush_size,
                              event.y + self.brush_size,
                              fill=self.color, outline=self.color)

        # Изменение цвета кисти

    def set_color(self, new_color):
        self.color = new_color

        # Изменение размера кисти

    def set_brush_size(self, new_size):
        self.brush_size = new_size

    def setUI(self):
        # Устанавливаем название окна
        self.parent.title("Demo Paint")
        # Размещаем активные элементы на родительском окне
        self.pack(fill=BOTH, expand=1)

        self.columnconfigure(6, weight=1)
        self.rowconfigure(2, weight=1)

        # Создаем холст с белым фоном
        self.canv = Canvas(self, bg="white")

        # Приклепляем канвас методом grid. Он будет находится в 3м ряду, первой колонке,
        # и будет занимать 7 колонок, задаем отступы по X и Y в 5 пикселей, и
        # заставляем растягиваться при растягивании всего окна

        self.canv.grid(row=2, column=0, columnspan=7, padx=5, pady=5, sticky=E + W + S + N)

        # задаем реакцию холста на нажатие левой кнопки мыши
        self.canv.bind("<B1-Motion>", self.draw)

        # создаем метку для кнопок изменения цвета кисти
        color_lab = Label(self, text="Цвет: ")

        # Устанавливаем созданную метку в первый ряд и первую колонку,
        # задаем горизонтальный отступ в 6 пикселей
        color_lab.grid(row=0, column=0, padx=6)

        # создание кнопки: установка текста кнопки, задание ширины кнопки (10 символов)
        red_btn = Button(self, text="Красный", width=10, command=lambda: self.set_color("red"))

        # устанавливаем кнопку в первый ряд, вторая колонка
        red_btn.grid(row=0, column=1)

        # по аналогии создаем остальные кнопки
        green_btn = Button(self, text="Зеленый", width=10, command=lambda: self.set_color("green"))
        green_btn.grid(row=0, column=2)

        blue_btn = Button(self, text="Синий", width=10, command=lambda: self.set_color("blue"))
        blue_btn.grid(row=0, column=3)

        black_btn = Button(self, text="черный", width=10, command=lambda: self.set_color("black"))
        black_btn.grid(row=0, column=4)

        white_btn = Button(self, text="Белый", width=10, command=lambda: self.set_color("white"))
        white_btn.grid(row=0, column=5)

        # Создаем метку для кнопок изменения размера кисти
        size_lab = Label(self, text="Размер кисти: ")
        size_lab.grid(row=1, column=0, padx=5)
        one_btn = Button(self, text="2x", width=10, command=lambda: self.set_brush_size(2))
        one_btn.grid(row=1, column=1)

        two_btn = Button(self, text="5x", width=10, command=lambda: self.set_brush_size(5))
        two_btn.grid(row=1, column=2)

        five_btn = Button(self, text="7x", width=10, command=lambda: self.set_brush_size(7))
        five_btn.grid(row=1, column=3)

        seven_btn = Button(self, text="10x", width=10, command=lambda: self.set_brush_size(10))
        seven_btn.grid(row=1, column=4)

        ten_btn = Button(self, text="20x", width=10, command=lambda: self.set_brush_size(20))
        ten_btn.grid(row=1, column=5)

        twenty_btn = Button(self, text="50x", width=10, command=lambda: self.set_brush_size(50))
        twenty_btn.grid(row=1, column=6, sticky=W)

        clear_btn = Button(self, text="Очистить", width=10, command=lambda: self.canv.delete("all"))
        clear_btn.grid(row=0, column=6, sticky=W)

# выход из программы  
def close_win():
  if askyesno("Выход", "Вы уверены?"):
    root.destroy()

# вывод справки    
def about():
  showinfo("Demo Paint", "Простейшая рисовалка от сайта: https://it-black.ru")
  
# функция для создания главного окна
def main():
    global root
    root = Tk()
    root.geometry("800x600+300+300")
    app = Paint(root)
    m = Menu(root)
    root.config(menu=m)

    fm = Menu(m)
    m.add_cascade(label="Файл", menu=fm)
    fm.add_command(label="Выход", command=close_win)

    hm = Menu(m)
    m.add_cascade(label="Справка", menu=hm)
    hm.add_command(label="О программе", command=about)
    root.mainloop()

if __name__ == "__main__":
    main()
Будет выведено:
Для полного понимания работы программы рекомендуется читать комментарии к коду. Программа получилась очень простая и поэтому мы рекомендуем самостоятельно доработать или переработать код данной программы. Пишите свои варианты в комментариях.
Библиотека Tkinter