Если вам необходимо добавить простой графический интерфейс для взаимодействия с пользователем к существующему сценарию командной строки (например, диалог выбора файла), это можно сделать, настроив виджеты и вызвав функцию mainloop из программы командной строки, когда это будет необходимо. По сути, этот прием добавляет поддержку графического интерфейса в программу, не имеющую постоянного главного окна. Проблема в том, что функция mainloop не возвращает управление, пока главное окно не будет закрыто пользователем (или не будет вызван метод quit). Поэтому вы не сможете получить данные, введенные пользователем, из виджетов, уже уничтоженных к моменту возврата из функции mainloop. Чтобы обойти эту проблему, достаточно просто сохранить ввод пользователя в объекте Python: объект останется существовать после уничтожения графического интерфейса. Пример 10.22 демонстрирует один из способов реализации этой идеи на языке Python.
Пример 10.22. PP4E\Gui\Tools\mainloopdemo.py
демонстрирует запуск двух отдельных циклов mainloop; каждый из них возвращает управление после того как главное окно будет закрыто; ввод пользователя сохраняется в объекте Python перед тем, как графический интерфейс будет закрыт; обычно в программах с графическим интерфейсом настройка виджетов и вызов mainloop выполняется всего один раз, а вся их логика распределена по обработчикам событий; в этом демонстрационном примере вызовы функции mainloop производятся для обеспечения модальных взаимодействий с пользователем из программы командной строки; демонстрирует один из способов добавления графического интерфейса к существующим сценариям командной строки без реорганизации программного кода;
from tkinter import *
from tkinter.filedialog import askopenfilename, asksaveasfilename
class Demo(Frame):
def __init__(self,parent=None):
Frame.__init__(self,parent)
self.pack()
Label(self, text =”Basic demos”).pack()
Button(self, text=’open’, command=self.openfile).pack(fill=BOTH)
Button(self, text=’save’, command=self.savefile).pack(fill=BOTH) self.open_name = self.save_name = “”
def openfile(self): # сохранить результаты пользователя
self.open_name = askopenfilename() # указать параметры диалога здесь
def savefile(self):
self.save_name = asksaveasfilename(initialdir=’C:\\Python31’)
if __name__ == “__main__”:
# вывести окно
print(‘popup1…’)
mydialog = Demo() # присоединить фрейм к окну Tk() по умолчанию
mydialog.mainloop() # отобразить; вернуться после закрытия окна
print(mydialog.open_name) # имена сохраняются в объекте, когда окно уже print(mydialog.save_name) # будет закрыто
# Раздел программы без графического интерфейса, использующей mydialog
# отобразить окно еще раз
print(‘popup2…’)
mydialog = Demo() # повторно создать виджеты
mydialog.mainloop() # повторно отобразить окно print(mydialog.open_name) # в объекте будут сохранены новые значения print(mydialog.save_name)
# Раздел программы без графического интерфейса,
# где снова используется mydialog print(‘ending…’)
Эта программа дважды конструирует и отображает простое окно с двумя кнопками, как показано на рис. 10.13, нажатие которых вызывает появление диалогов выбора файла. Вывод программы, который производится при закрытии окна графического интерфейса, выглядит примерно, как показано ниже:
C:\…\PP4E\Gui\Tools> mainloopdemo.py
popup1…
C:/Users/mark/Stuff/Books/4E/PP4E/dev/Examples/PP4E/Gui/Tools/widgets.py
C:/Python31/python.exe
popup2…
C:/Users/mark/Stuff/Books/4E/PP4E/dev/Examples/PP4E/Gui/Tools/guimixin.py
C:/Python31/Lib/tkinter/__init__.py
ending…
Рис. 10.13. Окно, которое выводится программой командной строки
Обратите внимание, что в этой программе функция mainloop вызывается дважды, для организации двух модальных взаимодействий с пользователем из сценария командной строки, не имеющего графического интерфейса. Нет ничего криминального в том, что функция mainloop вызывается более одного раза, но при этом сценарию приходится заново создавать виджеты перед очередным вызовом, потому что они уничтожаются после предыдущего вызова mainloop (виджеты уничтожаются внутри библиотеки Tk, даже если соответствующие им объекты Python продолжают существование). Напомню, что такая реализация графического интерфейса не соответствует ожиданиям пользователей, в сравнении с традиционными графическими интерфейсами, — создается впечатление, что окна появляются из ниоткуда, — но это самый быстрый способ добавить графический интерфейс без глубокой реорганизации программного кода.
Обратите внимание, что данный пример отличается от случая использования вложенных (рекурсивных) вызовов функции mainloop для реализации модальных диалогов, с которым мы столкнулись в главе 8. При таком подходе вложенные вызовы mainloop возвращают управление, когда вызывается метод quit диалога, но при этом продолжается выполнение объемлющего вызова mainloop, и мы остаемся в сфере программирования, управляемого событиями. Сценарий в примере 10.22, напротив, производит два независимых вызова функции mainloop, дважды вступая и выходя из модели, управляемой событиями.
Наконец, обратите внимание, что такая схема подходит только для ситуаций, когда не требуется выполнять какие-либо операции, не связанные с графическим интерфейсом, пока окно остается открытым, потому что на период выполнения mainloop основной поток управления сценария остается неактивным и блокируется. Вы не сможете, например, применить этот подход для добавления графического интерфейса к утилитам, подобным тем, что используются в модуле guiStreams, представленном выше в этой главе, предназначенном для передачи функций взаимодействия с пользователем из сценариев командной строки в графический интерфейс. Классы GuiInput и GuiOutput в том примере предполагают, что где-то уже был произведен вызов mainloop (в конце концов, они опираются на использование графического интерфейса). Но как только будет вызвана функция mainloop, чтобы вывести окна, вы не сможете вернуть управление обычному программному коду сценария командной строки, чтобы взаимодействовать с пользователем или с графическим интерфейсом, пока этот графический интерфейс не будет закрыт и функция mainloop не вернет управление. Таким образом, эти классы могут использоваться только в контексте программ, полностью опирающихся на графический интерфейс.
Но на самом деле это неестественный способ использования библиотеки tkinter. Сценарий в примере 10.22 действует только потому, что графический интерфейс может взаимодействовать с пользователем совершенно независимо, — сценарий может позволить себе отдать управление функции mainloop из библиотеки tkinter и ждать результатов. Эта схема непригодна, когда требуется выполнять программный код, не имеющий отношения к графическому интерфейсу, в то время, когда окно остается открытым. Из-за этих ограничений в большинстве графических интерфейсов вам придется использовать модель главное- окно-плюс-обработчики-событий — обработчики вызываются в ответ на действия пользователя, пока окно графического интерфейса остается открытым. При таком подходе ваш программный код может действовать, пока окно остается открытым. Например, смотрите представленный ранее в этой главе способ запуска сценариев командной строки архивирования и разархивирования из графического интерфейса, с выводом результатов в графическом интерфейсе, — технически эти сценарии запускаются из обработчиков событий графического интерфейса, а их вывод перенаправляется в виджет.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011