ГЛАВА 15. РАЗРАБОТКА ПРИЛОЖЕНИЙ С ГРАФИЧЕСКИМ ИНТЕРФЕЙСОМ

15.1. Основы работы с модулем tkinter

Язык Python позволяет создавать приложения с графическим интерфейсом, для этого используются различные графические библиотеки. Остановимся на рассмотрении стандартной (входит в стандартный комплект Python) графической библиотеки tkinter.

Первым делом при работе с tkinter необходимо создать главное (корневое) окно, в котором размещаются остальные графические элементы – виджеты. Существуют различные виджеты46 на все случаи жизни: для ввода текста, вывода текста, выпадающее меню и пр. Некоторые виджеты (фреймы) используются для группировки других виджетов внутри себя. Есть специальный виджет кнопка, при нажатии на который происходят некоторые события (события можно обрабатывать).

Схематично главное окно с набором виджетов изображено на следующей схеме:

В отдельном файле (mytk1.py, но не с именем tkinter.py!) выполним следующую простейшую программу для отображения главного окна:

# Подключаем модуль, содержащий методы для работы с графикой
import tkinter
# Создаем главное (корневое) окно,
# в переменную window записываем ссылку на объект класса Tk
window = tkinter.Tk()
# Задаем обработчик событий для корневого окна
window.mainloop()

Результат выполнения программы:

Появилось полноценное окно, которое можно свернуть, растянуть или закрыть! И это только три строчки кода!

Графические (оконные) приложения отличаются от консольных (без оконных) наличием обработки событий. Для консольных приложений, с которыми мы работали ранее, не требовалось определять, какую кнопку мыши и в какой момент времени нажал пользователь программы. В оконных приложениях важно нажатие мыши, т.к. от этого зависит, например, какой пункт меню выберет пользователь.

Слева на схеме показан алгоритм работы консольной программы, справа – программы с графическим интерфейсом:

Следующий пример демонстрирует создание виджета Label:

import tkinter
window = tkinter.Tk()
# Создаем объект-виджет класса Label в корневом окне window
# text – параметр для задания отображаемого текста
label = tkinter.Label(window, text=»Это текст в окне!»)
# Отображаем виджет с помощью менеджера pack
label.pack()
window.mainloop()

Результат работы программы:

Следующий пример демонстрирует размещение виджетов во фрейме:

import tkinter
window = tkinter.Tk()
# Создаем фрейм в главном окне
frame = tkinter.Frame(window)
frame.pack()
# Создаем виджеты и помещаем их во фрейме frame
first = tkinter.Label(frame, text=’First label’)
# Отображаем виджет с помощью менеджера pack
first.pack()
second = tkinter.Label(frame, text=’Second label’)
second.pack()
third = tkinter.Label(frame, text=’Third label’)
third.pack()
window.mainloop()

Пример выполнения программы:

Можно изменять параметры фрейма в момент создания объекта:

import tkinter
window = tkinter.Tk()
frame = tkinter.Frame(window)
frame.pack()
# Можем изменять параметры фрейма:
frame2 = tkinter.Frame(window, borderwidth=4, relief=tkinter.GROOVE)
frame2.pack()
# Размещаем виджет в первом фрейме (frame)
first = tkinter.Label(frame, text=’First label’)
first.pack()
# Размещаем виджеты во втором фрейме (frame2)
second = tkinter.Label(frame2, text=’Second label’)
second.pack()
third = tkinter.Label(frame2, text=’Third label’)
third.pack()
window.mainloop()

В следующем примере для отображения в виджете Label содержимого переменной, используется переменная data класса StringVar (из модуля tkinter). В дальнейшем из примеров станет понятнее, почему в tkinter используются переменные собственного класса .

import tkinter
window = tkinter.Tk()
# Создаем объект класса StringVar и присваиваем указатель на него data
# (создаем строковую переменную, с которой умеет работать tkinter)
data = tkinter.StringVar()
# Метод set класса StringVar позволяет изменить содержимое переменной:
data.set(‘Данные в окне’)
# textvariable присваиваем ссылку на строковый объект из переменной data label = tkinter.Label(window, textvariable=data)

label.pack()
window.mainloop()

Результат выполнения программы:

15.2. Шаблон «Модель-вид-контроллер» на примере модуля tkinter

Следующий пример показывает, каким образом использовать виджет (Entry) для ввода данных:

import tkinter
window = tkinter.Tk()
frame = tkinter.Frame(window)
frame.pack()
var = tkinter.StringVar()
# Обновление содержимого переменной происходит в режиме реального времени
label = tkinter.Label(frame, textvariable=var)
label.pack()
# Пробуем набрать текст в появившемся поле для ввода
entry = tkinter.Entry(frame, textvariable=var)
entry.pack()
window.mainloop()

Запустим программу и попробуем набрать произвольный текст:

Видим, что текст, который мы набираем, мгновенно отображается в окне. Дело в том, что виджеты Label и Entry используют для вывода и ввода текста соответственно одну и ту же переменную var класса StringVar. Подобная схема работы оконного приложения укладывается в универсальный шаблон (паттерн), который называется «Модель-видконтроллер» (Model-View-Controller или MVC) .

В общем виде под моделью (Model) понимают способ хранения данных, т.е. как данные хранятся (например, в переменной какого класса). Вид (View) служит для отображения данных. Контроллер (Controller) отвечает за обработку данных.

Следующая схема показывает связь всех компонентов модели MVC:

Интересная особенность MVC в том, что в случае изменения контроллером данных (как это было в предыдущем примере с изменением переменной var), «посылается сигнал» виду об отображении измененной переменной (перерисовке окна), отсюда получается обновление текста в режиме реального времени.

Следующий пример демонстрирует возможности обработки событий при нажатии на кнопку (виджет Button):

import tkinter
# Контроллер: функция вызывается в момент нажатия на кнопку
def click():
# метод get возвращает текущее значение counter
# метод set устанавливает новое значение counter
counter.set(counter.get() + 1)
window = tkinter.Tk()
# Модель: создаем объект класса IntVar
counter = tkinter.IntVar()
# Обнуляем созданный объект с помощью метода set
counter.set(0)
frame = tkinter.Frame(window)
frame.pack()
# Создаем кнопку и указываем обработчик (функция click) при нажатии на нее
button = tkinter.Button(frame, text=’Click’, command=click)
button.pack()
# Вид: в реальном времени обновляется содержимое виджета Label
label = tkinter.Label(frame, textvariable=counter)
label.pack()
window.mainloop()

Результат выполнения программы:

Более сложный пример с двумя кнопками и двумя обработчиками событий (click_up, click_down):

import tkinter
window = tkinter.Tk()
# Модель:
counter = tkinter.IntVar()
counter.set(0)
# Два контроллера:
def click_up():
counter.set(counter.get() + 1)
def click_down():
counter.set(counter.get() — 1)
# Вид:
frame = tkinter.Frame(window)
frame.pack()
button = tkinter.Button(frame, text=’Up’, command=click_up)
button.pack()
button = tkinter.Button(frame, text=’Down’, command=click_down)
button.pack()
label = tkinter.Label(frame, textvariable=counter)
label.pack()
window.mainloop()

Результат работы программы:

Изменение параметров по умолчанию при работе с tkinter

Tkinter позволяет изменять параметры виджетов в момент их создания:

import tkinter
window = tkinter.Tk()
# Создаем кнопку, изменяем шрифт с помощью кортежа
button = tkinter.Button(window, text=’Hello’,
font=(‘Courier’, 14, ‘bold italic’))
button.pack()
window.mainloop()

Результат выполнения программы:

В следующем примере изменяются параметры виджета Label:

import tkinter
window = tkinter.Tk()
# Изменяем фон, цвет текста:
button = tkinter.Label(window, text=’Hello’, bg=’green’, fg=’white’)
button.pack()
window.mainloop()

Результат выполнения программы:

Менеджер расположения (геометрии) pack тоже имеет параметры:

import tkinter
window = tkinter.Tk()
frame = tkinter.Frame(window)
frame.pack()
label = tkinter.Label(frame, text=’Name’)
# Выравнивание по левому краю
label.pack(side=’left’)
entry = tkinter.Entry(frame)
entry.pack(side=’left’)
window.mainloop()

Результат выполнения программы: