Крупные графические интерфейсы часто строятся как подклассы Frame с реализацией обработчиков событий в виде методов. Благодаря такой структуре возникает естественное место для хранения информации между событиями: атрибуты экземпляров классов. Она позволяет также специализировать графические интерфейсы за счет переопределения методов в новых подклассах и прикреплять их к более крупным структурам интерфейса, чтобы повторно использовать в качестве общих компонентов. Например, текстовый редактор с графическим интерфейсом, реализованный как подкласс Frame, можно прикреплять к любому числу других графических интерфейсов и настраивать с их помощью — при правильном использовании такой текстовый редактор можно встроить в любой интерфейс пользователя, где требуются средства редактирования текста.
С таким текстовым редактором в виде компонента мы встретимся в главе 11. А пока проиллюстрируем идею простым примером 7.20. Сценарий gui6.py создает окно, изображенное на рис. 7.20.
Пример 7.20. PP4E\Gui\Intro\gui6.py
from tkinter import *
class Hello(Frame): # расширенная версия класса Frame
def __init__(self, parent=None):
Frame.__init__(self, parent) # вызвать метод __init__ суперкласса self.pack() self.data = 42
self.make_widgets() # прикрепить виджеты к себе
def make_widgets(self):
widget = Button(self, text=’Hello frame world!’, command=self.message) widget.pack(side=LEFT)
def message(self): self.data += 1 print(‘Hello frame world %s!’ % self.data)
if __name__ == ‘__main__’: Hello().mainloop()
Рис. 7.20. Нестандартный фрейм в действии
Этот сценарий выводит окно с единственной кнопкой. При ее нажатии вызывается связанный метод self.message, который снова выводит сообщение в stdout. Ниже показаны сообщения, выведенные после четырехкратного нажатия кнопки — обратите внимание, что атрибут self.data (в данном случае простой счетчик) сохраняет информацию о состоянии между нажатиями:
C:\…\PP4E\Gui\Intro> python gui6.py
Hello frame world 43!
Hello frame world 44!
Hello frame world 45!
Hello frame world 46
Это может показаться окольным методом отображения кнопки типа Button (в примерах 7.10, 7.11 и 7.12 то же достигалось меньшим числом строк). Но класс Hello предоставляет контейнерную организационную структуру для построения графического интерфейса. В примерах, предшествовавших предыдущему разделу, графические интерфейсы создавались с применением процедурного подхода: мы вызывали конструкторы виджетов, как если бы они были функциями, и связывали виджеты воедино, указывая родителя при вызове конструктора. Не было никакого представления о внешнем контексте, помимо глобальной области видимости файла модуля, содержащего вызовы методов виджетов. Такой подход годится для простых графических интерфейсов, но при построении структур более крупных интерфейсов служит причиной хрупкости кода.
При создании подкласса, наследующего класс Frame, как было продемонстрировано, этот класс становится охватывающим контекстом графического интерфейса:
• Виджеты добавляются путем прикрепления объектов к self, экземпляру подкласса контейнера Frame (например, Button).
• Обработчики событий регистрируются как связанные методы объекта self, вследствие чего вызовы направляются обратно в реализацию класса (например, self.message).
• Информация о состоянии сохраняется между событиями путем присвоения атрибутам объекта self и доступна всем обработчикам событий в классе (например, self.data).
• Легко можно создать несколько экземпляров такого компонента графического интерфейса, даже внутри одного и того же процесса, потому что каждый экземпляр класса является отдельным пространством имен.
• Классы обладают естественной возможностью настройки благодаря возможности наследования и композиции.
В некотором смысле графические интерфейсы целиком становятся специализированными объектами Frame с расширениями, соответствующими их применению. Классы также позволяют реализовать протоколы построения виджетов (например, метод make_widgets в данном случае), выполнять стандартные задачи настройки (например, установка параметров настройки менеджера окон) и так далее. Одним словом, подклассы Frame дают простой способ организации совокупностей объектов других классов виджетов.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011