Другим способом может быть реализация общих методов в классе и наследование их при необходимости. Такие классы обычно называют подмешиваемыми (mixin), потому что их методы «подмешиваются» в другие классы. Подмешиваемые классы служат своего рода пакетами полезных во многих случаях инструментов, оформленных в виде методов. Эта идея близка к импортированию модулей, однако подмешиваемые классы могут обращаться к конкретному экземпляру, self, используя состояние конкретного объекта и унаследованные методы. Сценарий в примере 10.2 демонстрирует, как это делается.
Пример 10.2. PP4E\Gui\Tools\guimixin.py
############################################################################## класс, “подмешиваемый” во фреймы: реализует общие методы вызова стандартных диалогов, запуска программ, простых инструментов отображения текста и так далее; метод quit требует, чтобы этот класс подмешивался к классу Frame (или его производным) ##############################################################################
from tkinter import *
from tkinter.messagebox import *
from tkinter.filedialog import *
from PP4E.Gui.Tour.scrolledtext import ScrolledText # или tkinter.scrolledtext from PP4E.launchmodes import PortableLauncher, System # или используйте модуль # multiprocessing
class GuiMixin:
def infobox(self, title, text, *args): # используются стандартные диалоги return showinfo(title, text) # *args для обратной совместимости
def errorbox(self, text):
showerror(‘Error!’, text)
def question(self, title, text, *args):
return askyesno(title, text) # вернет True или False
def notdone(self):
showerror(‘Not implemented’, ‘Option not available’)
def quit(self):
ans = self.question(‘Verify quit’, ‘Are you sure you want to quit?’)
if ans:
Frame.quit(self) # нерекурсивный вызов quit!
def help(self): # переопределите более
self.infobox(‘RTFM’, ‘See figure 1…’) # подходящим
def selectOpenFile(self, file=””, dir=”.”): # испол—ся стандартные диалоги return askopenfilename(initialdir=dir, initialfile=file)
def selectSaveFile(self, file=””, dir=”.”):
return asksaveasfilename(initialfile=file, initialdir=dir)
def clone(self, args=()): # необязательные аргументы конструктора
new = Toplevel() # создать новую версию
myclass = self.__class__ # объект класса экземпляра (самого низшего) myclass(new, *args) # прикрепить экземпляр к новому окну
def spawn(self, pycmdline, wait=False):
if not wait: # запустить новый процесс
PortableLauncher(pycmdline, pycmdline)() # запустить программу else:
System(pycmdline, pycmdline)() # ждать ее завершения
def browser(self, filename):
new = Toplevel() # создать новое окно
view = ScrolledText(new, file=filename) # Text с полосой прокрутки view.text.config(height=30, width=85) # настроить Text во фрейме view.text.config(font=(‘courier’, 10, ‘normal’)) # моноширинный шрифт
new.title(“Text Viewer”) # атрибуты менеджера окон
new.iconname(“browser”) # текст из файла будет
# вставлен автоматически
def browser(self, filename): # на случай, если импортирован
new = Toplevel() # модуль tkinter.scrolledtext
text = ScrolledText(new, height=30, width=85)
text.config(font=(‘courier’, 10, ‘normal’))
text.pack(expand=YES, fill=BOTH)
new.title(“Text Viewer”)
new.iconname(“browser”)
text.insert(‘0.0’, open(filename, ‘r’).read() )
if __name__ == ‘__main__’:
class TestMixin(GuiMixin, Frame): # автономный тест def __init__(self, parent=None):
Frame.__init__(self, parent) self.pack()
Button(self, text=’quit’, command=self.quit).pack(fill=X) Button(self, text=’help’, command=self.help).pack(fill=X) Button(self, text=’clone’, command=self.clone).pack(fill=X) Button(self, text=’spawn’, command=self.other).pack(fill=X) def other(self):
self.spawn(‘guimixin.py’) # запустить себя в отдельном процессе
TestMixin().mainloop()
Хотя пример 10.2 и ориентирован на графические интерфейсы, кроме этого он иллюстрирует архитектурные идеи. Класс GuiMixin реализует обычные операции со стандартными интерфейсами, которые не подвержены влиянию возможных изменений в реализации. На деле реализации некоторых методов этого класса все-таки изменились — при переходе от первого ко второму изданию этой книги вызовы функций из устаревшего модуля Dialog были заменены вызовами новых стандартных диалогов Tk; в четвертом издании изменился компонент для просмотра содержимого текстовых файлов и он теперь использует другой класс текстового виджета с прокруткой. Так как интерфейс класса в примере скрывает подобные детали, отпадает необходимость изменять использующие его программы, чтобы сделать доступными в них новые приемы.
В данном виде класс GuiMixin предоставляет методы для вызова стандартных диалогов, клонирования окон, запуска программ, просмотра текстовых файлов и так далее. Позднее, если обнаружится, что одни и те же методы приходится писать снова и снова, их можно будет добавить в такой подмешиваемый класс, и они немедленно станут доступны везде, где импортируется и внедряется этот класс. Более того, методы класса GuiMixin можно наследовать и использовать в существующем виде либо переопределять в подклассах. Таковы естественные преимущества классов перед функциями.
Здесь есть несколько тонкостей, которые следует отметить особо:
• Метод quit выполняет отчасти ту же задачу, что и кнопка многократного использования Quitter в предыдущих главах. Так как в подмешиваемых классах могут определяться большие библиотеки многократно используемых методов, они обеспечивают более мощный способ упаковки многократно используемых компонентов, чем отдельные классы. При правильном применении подмешиваемый класс может дать значительно больше, чем обработчик единственной кнопки.
• Метод clone создает новый экземпляр самого нижнего в иерархии класса, который подмешивает класс GuiMixin, в новом окне верхнего уровня (self. class — это объект класса, из которого был создан
экземпляр). Предполагается, что конструктор класса не требует никаких других аргументов, кроме ссылки на родительский контейнер. Он открывает новый независимый экземпляр окна (и передает конструктору любые дополнительные аргументы).
• Метод browser открывает в новом окне объект ScrolledText, который мы создали в главе 9, и заполняет его текстом из файла, который нужно просмотреть. Как отмечалось в предыдущей главе, существует также стандартный виджет ScrolledText, находящийся в модуле tkinter.scrolledtext, но он имеет иной интерфейс, не загружает содержимое файла автоматически и, возможно, будет объявлен устаревшим (хотя этого не происходит уже многие годы). Для справки в класс включена реализация метода, использующая этот виджет.
• Метод spawn запускает программу на языке Python в новом процессе и либо ждет его завершения, либо нет (в зависимости от аргумента wait, со значением по умолчанию False — обычно графический интерфейс не должен ждать завершения дочерней программы). Этот метод прост потому, что тонкости запуска скрыты в модуле launchmodes, представленном в конце главы 5. Класс GuiMixin способствует применению и сам применяет на практике приемы повторного использования программного кода.
Назначение класса GuiMixin состоит в том, чтобы служить библиотекой многократно используемых инструментальных методов, и как самостоятельный класс он, в сущности, бесполезен. В действительности для использования его нужно подмешивать в классы, наследующие класс Frame: метод quit предполагает, что он смешивается с классом Frame, а метод clone предполагает, что он смешивается с классом виджета. Чтобы удовлетворить этим ограничениям, находящаяся в конце реализация самотестирования объединяет класс GuiMixin с виджетом Frame.
На рис. 10.1 изображена картина, которая возникает при самотестировании после щелчка на кнопках clone и spawn, а затем на кнопке help в одной из трех копий окна. Поскольку щелчок на кнопке spawn запускает отдельный процесс, окно, созданное таким способом, остается на экране после закрытия всех остальных окон, а его закрытие не оказывает влияния на другие окна. Окно, созданное щелчком на кнопке clone, напротив, закрывается при закрытии главного окна, однако щелчок на кнопке X в копии окна закрывает только это окно. Не забудьте включить путь к каталогу PP4E в переменную окружения PYTHONPATH, чтобы обеспечить возможность импортирования пакетов в этом и в последующих примерах.
![]() |
|
![]() |
|||
![]() |
|||
Мы снова встретимся с классом GuiMixin в роли подмешиваемого класса в последующих примерах — в конце концов, в этом весь смысл повторного использования кода. Хотя функции часто бывают полезными, тем не менее поддержка наследования классами, возможность доступа к информации в экземпляре и обеспечение дополнительной организационной структуры оказываются особенно полезными при создании графических интерфейсов. Например, если многие методы класса Gui— Mixin можно было бы заменить простыми функциями, то методы clone и quit — нет. В следующем разделе рассматриваются еще более широкие возможности подмешиваемых классов.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011