Основы графических интерфейсов
Как будет показано далее в этой книге, программистам, использующим язык Python, доступно множество разнообразных инструментов создания графических интерфейсов: tkinter, wxPython, PyQt, PythonCard, Dabo и многие другие. Из них в составе Python поставляется только tkinter, который де-факто считается стандартным инструментом.
tkinter — это легковесный инструмент, который прекрасно интегрируется с языками сценариев, такими как Python. Его легко использовать для реализации простых графических интерфейсов, а дополнительные расширения к нему и применение приемов объектно-ориентированного программирования позволяют без особых затрат реализовать более сложные интерфейсы. Кроме того, реализация графического интерфейса на базе tkinter способна без каких-либо модификаций работать в Windows, Linux/Unix и Macintosh — достаточно просто перенести файлы с исходными текстами на компьютер, где предполагается использовать программу с графическим интерфейсом. В tkinter отсутствуют разнообразные «бантики и рюшечки», имеющиеся в более развитых инструментах, таких как wxPython или PyQt, но это же является основной причиной его относительной простоты, что делает его идеальным инструментом для тех, кто только начинает создавать программы с графическим интерфейсом.
Инструмент tkinter разработан для использования в сценариях, поэтому программирование графических интерфейсов с его применением организовано с достаточной очевидностью. Далее в этой книге мы поближе познакомимся со всеми его понятиями и возможностями. А в качестве первого примера рассмотрим программу, использующую tkinter, которая содержит всего несколько строк программного кода, как видно из примера 1.23.
Пример 1.23. PP4E\Preview\tkinter001.py
from tkinter import *
Label(text=’Spam’).pack()
mainloop()
Импортировав модуль tkinter (на самом деле, в Python 3 — пакет модулей), мы получаем возможность обращаться к различным экранным конструкциям (или «виджетам»), таким как Label; методам менеджера геометрии, таким как pack; предварительно установленным комплектам настроек виджетов, таким как TOP и RIGHT, определяющим край для выравнивания компонентов и используемых при вызове метода pack; и к функции mainloop, запускающей цикл обработки событий.
Это не самый полезный сценарий с графическим интерфейсом из когда- либо созданных, но он демонстрирует основы использования tkinter и создает полнофункциональное окно, как показано на рис. 1.1, — всего тремя строками программного кода. Изображение окна, как и всех других графических интерфейсов в этой книге, было получено в Windows 7. Окно действует одинаково и на других платформах (таких как Mac OS X, Linux и в более старых версиях Windows), но при этом имеет внешний вид, характерный для той платформы, на которой запускается сценарий.
Рис. 1.1. Окно сценария tkinter001.py
Вы можете запустить этот сценарий из среды IDLE, из командной строки или щелчком на ярлыке — то есть точно так же, как любой другой сценарий на языке Python. Сам инструмент tkinter является стандартной частью Python и работает, что называется, «из коробки», на всех платформах, включая Windows. Однако в некоторых операционных системах может потребоваться выполнить дополнительные шаги по установке и настройке (подробнее об этом будет рассказываться далее в книге).
Совсем немного усилий требуется приложить, чтобы создать графический интерфейс, откликающийся на действия пользователя: сценарий в примере 1.24 реализует графический интерфейс с кнопкой, щелчок на которой приводит к вызову функции reply.
Пример 1.24. PP4E\Preview\ tkinter101.py
from tkinter import *
from tkinter.messagebox import showinfo
def reply():
showinfo(title=’popup’, message=’Button pressed!’)
window = Tk()
button = Button(window, text=’press’, command=reply)
button.pack()
window.mainloop()
Этот пример также достаточно прост. Он явно создает главное окно Tk приложения, которое будет служить контейнером для кнопки, и воспроизводит на экране простое окно, как показано на рис. 1.2 (при создании нового виджета в модуле tkinter принято передавать контейнерные элементы в первом аргументе; который по умолчанию ссылается на главное окно). Но на этот раз при каждом щелчке на кнопке с надписью press программа будет откликаться вызовом программного кода, который выводит диалог, как показано на рис. 1.3.
Рис. 1.2. Главное окно сценария tkinter101.py
Обратите внимание, что окно диалога выглядит в Windows 7 (на платформе, где сделан снимок с экрана) именно так, как и должно выглядеть. Повторюсь, что модуль tkinter обеспечивает внешний вид создаваемых элементов, характерный для той платформы, на которой запускается сценарий. Мы можем изменять самые разные аспекты графического интерфейса (например, цвет и шрифт, текст и ярлык в заголовке окна, помещать на кнопки изображения вместо текста), но одно из преимуществ использования модуля tkinter — в том, что нам необходимо будет изменять лишь те параметры, которые требуется изменить.
Рис. 1.3. Типичное окно диалога, созданное сценарием tkinter101.py
ООП при разработке графических интерфейсов
Все примеры реализации графического интерфейса, представленные до сих пор, имели вид самостоятельных сценариев, включающих функцию обработки событий. В крупных программах часто более полезно бывает оформить программный код, создающий графический интерфейс, в виде подкласса виджета Frame из библиотеки tkinter — контейнера для других виджетов. В примере 1.25 приводится переработанная версия предыдущего сценария с одной кнопкой, в которой графический интерфейс реализован в виде класса.
Пример 1.25. PP4E\Preview\tkinter102.py
from tkinter import *
from tkinter.messagebox import showinfo
class MyGui(Frame):
def __init__(self, parent=None): Frame.__init__(self, parent) button = Button(self, text=’press’, command=self.reply) button.pack()
def reply(self): showinfo(title=’popup’, message=’Button pressed!’)
if __name__ == ‘__main__’: window = MyGui() window.pack() window.mainloop()
Обработчик событий от кнопки — это связанный метод self. reply, то есть объект, хранящий в себе значение self и ссылку на метод reply. Данный пример воспроизводит то же самое окно и диалог, что и сценарий в примере 1.24 (рис. 1.2 и 1.3). Но теперь графический интерфейс реализован как подкласс класса Frame и потому автоматически становится присоединяемым компонентом — то есть мы сможем добавить все виджеты, создаваемые этим классом, как единый пакет в любой другой графический интерфейс; достаточно просто присоединить экземпляр этого класса к графическому интерфейсу. Как это делается, показано в примере 1.26.
Пример 1.26. PP4E\Preview\attachgui.py
from tkinter import * from tkinter102 import MyGui
# главное окно приложения mainwin = Tk()
Label(mainwin, text=__name__).pack()
# окно диалога
popup = Toplevel()
Label(popup, text=’Attach’).pack(side=LEFT)
MyGui(popup).pack(side=RIGHT) # присоединить виджеты
mainwin.mainloop()
Этот сценарий присоединяет наш графический интерфейс с одной кнопкой к другому окну popup типа Toplevel, которое передается импортированному приложению через вызов конструктора, как родительский компонент (кроме того, вы получаете доступ к главному окну Tk — как будет показано позже, вы всегда сможете получить к нему доступ, независимо от того, создается оно явно или нет). На этот раз наш пакет виджетов, содержащий единственную кнопку, присоединяется к правому краю контейнера. Если запустить этот пример, вы увидите картину, изображенную на рис. 1.4, где кнопка с надписью press — это наш подкласс класса Frame.
Рис. 1.4. Присоединение интерфейсных элементов
Кроме того, так как наш графический интерфейс MyGui оформлен в виде класса, мы получаем возможность настраивать его за счет использования механизма наследования — достаточно просто определить подкласс, реализующий необходимые отличия. Например, можно изменить реализацию метода reply, чтобы он делал что-то особенное, как показано в примере 1.27.
Пример 1.27. PP4E\Preview\customizegui.py
from tkinter import mainloop
from tkinter.messagebox import showinfo
from tkinter102 import MyGui
class CustomGui(MyGui): # наследует метод __init__
def reply(self): # замещает метод reply
showinfo(title=’popup’, message=’Ouch!’)
if __name__ == ‘__main__’:
CustomGui().pack() mainloop()
Если запустить этот сценарий, он создаст то же главное окно с кнопкой, что и оригинальный класс MyGui. Но щелчок на кнопке сгенерирует иной ответ, как показано на рис. 1.5, потому что будет вызвана другая версия метода reply.
Несмотря на свою простоту, эти графические интерфейсы иллюстрируют несколько важных идей. Как будет показано далее в этой книге, использование приемов ООП, таких как наследование и присоединение в данных примерах, позволяет повторно использовать пакеты виджетов в наших программах — калькуляторы, текстовые редакторы и подобные им интерфейсы легко могут настраиваться и добавляться в дру-
Рис. 1.5. Изменение графического интерфейса
гие графические интерфейсы как компоненты, если они реализованы в виде классов. Кроме того, подклассы виджетов способны обеспечить единство внешнего вида или стандартизованное поведение всех своих экземпляров, что в иных терминах может быть названо стилями и темами графического интерфейса. Это обычный побочный продукт Python и ООП.
Получение ввода от пользователя
В примере 1.28 приводится заключительный пример вводного сценария, демонстрирующий, как получить ввод пользователя с помощью виджета Entry и вывести его в диалоге. Использованная здесь инструкция lambda откладывает вызов функции reply до момента, когда ввод пользователя можно будет передать дальше, — это типичный прием программирования при работе с модулем tkinter. Без инструкции lambda функция reply была бы вызвана в момент создания кнопки, а не в момент щелчка на ней (мы могли бы использовать глобальную переменную ent внутри функции reply, но это делает функцию менее универсальной). Кроме того, этот пример демонстрирует, как изменить ярлык и текст в заголовке окна верхнего уровня. В данном случае файл ярлыка находится в том же каталоге, что и сценарий (если в вашей системе вызов метода iconbitmap терпит неудачу, попробуйте закомментировать этот вызов — к сожалению, в разных платформах работа с ярлыками выполняется по-разному).
Пример 1.28. PP4E\Preview\tkinter103.py
from tkinter import *
from tkinter.messagebox import showinfo
showinfo(title=’Reply’, message=’Hello %s!’ % name)
top = Tk()
top.title(‘Echo’)
top.iconbitmap(‘py-blue-trans-out.ico’)
Label(top, text=”Enter your name:”).pack(side=TOP)
ent = Entry(top)
ent.pack(side=TOP)
btn = Button(top, text=”Submit”, command=(lambda: reply(ent.get()))) btn.pack(side=LEFT)
top.mainloop()
В этом примере к главному окну Tk присоединяются всего три виджета. Далее вы узнаете, как использовать вложенные контейнеры Frame виджетов для достижения различных схем размещения этих трех виджетов. На рис. 1.6 изображены главное окно и окно диалога, появляющееся после щелчка на кнопке Submit. Нечто похожее мы увидим далее в этой главе, но реализованное на языке разметки HTML — для отображения в веб-броузере.
Рис. 1.6. Получение ввода пользователя
Программный код, представленный выше, демонстрирует множество особенностей программирования графических интерфейсов, но модуль tkinter обладает намного более широкими возможностями, чем можно было бы заключить из этих примеров. В модуле tkinter реализованы более 20 виджетов и еще множество способов дать пользователю возможность вводить данные, включая элементы ввода многострочного текста, «холсты» для рисования, раскрывающиеся меню, радиокнопки и флажки, полосы прокрутки, а также механизмы управления размещением виджетов и обработки событий. Помимо модуля tkinter в состав стандартной библиотеки языка Python входят также расширения, такие как PMW, и инструменты Tix и ttk, которые добавляют дополнительные виджеты для использования в графических интерфейсах, построенных на базе tkinter, и позволяющие придать интерфейсу еще более профессиональный внешний вид. Чтобы в общих чертах продемонстрировать имеющиеся возможности, давайте задействуем модуль tkinter для создания интерфейса к нашей базе данных.
Графический интерфейс к хранилищу
Первое, что необходимо сделать для нашего приложения баз данных, — это создать графический интерфейс для просмотра хранящихся данных (форму с именами и значениями полей) и реализовать способ извлечения записей по ключу. Также было бы полезно иметь возможность изменять значения полей в записях и добавлять новые записи, заполняя пустую форму. Для простоты мы реализуем единый графический интерфейс, позволяющий решать все эти задачи. На рис. 1.7 изображено окно, которое мы создадим, отображенное в Windows 7, с содержимым записи, полученной по ключу sue (здесь снова используется хранилище в том состоянии, в каком мы его оставили в последний раз). Данная запись в действительности является экземпляром нашего класса, сохраненным в файле хранилища, но пользователю это должно быть безразлично.
Рис. 1.7. Главное окно сценария peoplegui.py
Реализация графического интерфейса
Пример 1.29. PP4E\Preview\peoplegui.py
Реализация графического интерфейса для просмотра и изменения экземпляров класса, хранящихся в хранилище;
хранилище находится на том же компьютере, где выполняется сценарий в виде одного или более локальных файлов;
from tkinter import *
from tkinter.messagebox import showerror
import shelve
shelvename = ‘class-shelve’
fieldnames = (‘name’, ‘age’, ‘job’, ‘pay’)
def makeWidgets(): global entries window = Tk() window.title(‘People Shelve’) form = Frame(window) form.pack() entries = {}
for (ix, label) in enumerate((‘key’,) + fieldnames):
lab = Label(form, text=label)
ent = Entry(form)
lab.grid(row=ix, column=0)
ent.grid(row=ix, column=1) entries[label] = ent
Button(window, text=”Fetch”, command=fetchRecord).pack(side=LEFT) Button(window, text=”Update”, command=updateRecord).pack(side=LEFT) Button(window, text=”Quit”, command=window.quit).pack(side=RIGHT) return window
def fetchRecord():
key = entries[‘key’].get()
try:
record = db[key] # извлечь запись по ключу, отобразить в форме except:
showerror(title=’Error’, message=’No such key!’) else: for field in fieldnames:
entries[field].delete(0, END)
entries[field].insert(0, repr(getattr(record, field)))
def updateRecord():
key = entries[‘key’].get()
if key in db:
record = db[key] # изменяется существующая запись
else:
from person import Person # создать/сохранить новую запись
record = Person(name=’?’, age=’?’) # eval: строки должны # заключаться в кавычки for field in fieldnames:
setattr(record, field, eval(entries[field].get())) db[key] = record
db = shelve.open(shelvename)
window = makeWidgets()
window.mainloop()
db.close() # в эту точку программа попадает при щелчке на кнопке Quit # или при закрытии окна
Для размещения надписей и полей ввода в этом сценарии вместо метода pack используется метод grid. Как мы увидим далее, этот метод располагает виджеты по столбцам и строкам сетки, что обеспечивает более естественное для форм выравнивание надписей и полей ввода по горизонтали. Далее мы также увидим, что неплохого размещения виджетов на форме можно добиться и с помощью метода pack, добавив вложенные контейнеры для размещения виджетов по горизонтали и надписи фиксированной длины. Пока графический интерфейс никак не обрабатывает изменение размеров окна (для этого необходимы параметры настройки, которые мы будем исследовать позже), тем не менее объем программного кода реализации такой возможности при использовании любого из методов, grid или pack, будет примерно один и тот же.
Обратите внимание, что в конце сценария сначала открывается хранилище, как глобальная переменная, а затем запускается графический интерфейс — хранилище остается открытым на протяжении всего времени работы графического интерфейса (функция mainloop возвращает управление только после закрытия главного окна). Как будет показано в следующем разделе, такое удержание хранилища в открытом состоянии существенно отличает графический интерфейс от веб-интерфейса, где каждая операция обычно является автономной программой. Обратите также внимание, что использование глобальных переменных делает программный код более простым, но непригодным для использования вне контекста нашей базы данных; подробнее об этом мы поговорим ниже.
Пользование графическим интерфейсом
Построенный нами графический интерфейс достаточно прост, но он позволяет просматривать и изменять содержимое файла хранилища без ввода программного кода. Чтобы извлечь запись из хранилища и отобразить ее в графическом интерфейсе, необходимо ввести ключ в поле key (ключ) и щелкнуть на кнопке Fetch (Извлечь). Чтобы изменить запись, необходимо изменить содержимое полей записи после ее извлечения из хранилища и щелкнуть на кнопке Update (Изменить) — значения из полей ввода будут сохранены в базе данных. А чтобы добавить новую запись, необходимо заполнить все поля ввода новыми значениями
и щелкнуть на кнопке Update (Изменить) — в хранилище будет добавлена новая запись с указанным ключом и значениями полей.
Другими словами, поля ввода служат одновременно и для отображения, и для ввода. На рис. 1.8 изображена форма после добавления новой записи (щелчком на кнопке Update (Изменить)), а на рис. 1.9 — диалог с сообщением об ошибке, когда пользователь попытался извлечь запись с ключом, отсутствующим в хранилище.
Рис. 1.8. Интерфейс peoplegui.py после добавления нового объекта
Рис. 1.9. Диалог peoplegui.py с сообщением об ошибке
Обратите внимание, что для отображения значений полей, извлеченных из хранилища, здесь также используется функция repr, а для преобразования значений полей в объекты Python, перед тем как они будут записаны в хранилище, — функция eval. Как уже упоминалось выше, это потенциально опасно, так как открывает возможность ввести в поля ввода злонамеренный программный код, но мы пока не будем касаться этой проблемы.
Однако имейте в виду, что при такой реализации строковые значения, вводимые в поля ввода, должны заключаться в кавычки, так как содержимое всех полей ввода, кроме поля key (ключ), интерпретируется как программный код на языке Python. Фактически, чтобы определить новое значение, в поле ввода можно ввести произвольное выражение на языке Python. Например, если в поле name (имя) ввести выражение “Tom”*3, после щелчка на кнопке Update (Изменить) в записи будет сохранено имя TomTomTom. Чтобы убедиться в этом — извлеките запись из хранилища.
Несмотря на наличие графического интерфейса, позволяющего просматривать и изменять записи, мы по-прежнему можем проконтролировать свои действия, открыв и проверив файл хранилища в интерактивном режиме или запустив сценарий, такой как dump_db_classes. py, представленный в примере 1.19. Не забывайте: несмотря на то, что теперь мы можем просматривать записи с помощью графического интерфейса, сама база данных является файлом хранилища, содержащим объекты Python экземпляров классов, поэтому обращаться к ним может любой программный код на языке Python. Ниже приводятся результаты запуска сценария dump_db_classes.py после изменения существующих и добавления новых объектов с помощью графического интерфейса:
…\PP4E\Preview> python dump_db_classes.py sue =>
Sue Jones 50000.0 bill => bill 9999 nobody =>
John Doh None tomtom =>
Tom Tom 40000 tom =>
Tom Doe 90000 bob =>
Bob Smith 30000 peg => 1 4
Smith Doe
Пути усовершенствования
Построенный нами графический интерфейс справляется со своей задачей, тем не менее в него можно внести множество разных усовершенствований:
• На настоящий момент графический интерфейс представляет собой набор функций, использующих глобальный список полей (entries) ввода и глобальное хранилище (db). Вместо этого мы могли бы передать db в вызов функции makeWidgets и организовать передачу обоих этих объектов обработчикам событий в виде аргументов, воспользовавшись приемом с инструкцией lambda из предыдущего раздела. Хотя для таких маленьких сценариев это и не так важно, стоит иметь в виду, что явное определение подобных внешних зависимостей делает программный код более простым для понимания и повторного использования в других контекстах.
• Этот графический интерфейс можно было бы реализовать в виде класса, чтобы обеспечить поддержку возможности специализации и присоединения к другим графическим интерфейсам (глобальные переменные в этом случае могли бы стать атрибутами экземпляра), хотя повторное использование столь специфического интерфейса маловероятно.
• Полезнее было бы передавать функциям в виде параметра кортеж fieldnames, чтобы в будущем их можно было использовать с другими типами записей. Программный код в конце сценария также можно было бы оформить в виде функции, принимающей имя файла хранилища, а в функцию updateRecord можно было бы передавать функцию, создающую новую запись, чтобы она могла сохранять не только экземпляры класса Person. Эти усовершенствования выходят далеко за рамки данного краткого обзора, но их реализация была бы для вас неплохим упражнением. Позднее я познакомлю вас с еще одним дополнительным примером, входящим в комплект примеров к книге, PyForm, в котором используется иной подход к созданию универсальных форм ввода.
• Чтобы сделать этот графический интерфейс более дружественным по отношению к пользователю, можно было бы добавить окно со списком всех ключей, имеющихся в базе данных, и тем самым упростить просмотр содержимого базы данных. Полезно было бы предусмотреть проверку данных перед сохранением, а кроме того, легко можно было бы добавить клавиши Delete (Удалить) и Clear (Очистить). Тот факт, что введенные данные интерпретируются как программный код на языке Python, может доставить массу беспокойств — реализация простейшей схемы ввода могла бы повысить безопасность. (Я не буду явно предлагать реализовать эти усовершенствования в качестве самостоятельного упражнения, но это было бы полезно.)
• Мы могли бы также реализовать поддержку изменения размеров окна (как мы узнаем позднее, виджеты могут растягиваться и сжиматься вместе с окном) и предоставить возможность вызова методов, которыми обладают сохраняемые экземпляры классов (в том смысле, что графический интерфейс позволяет изменить значение поля pay, но не позволяет вызвать метод giveRaise).
• Если бы мы планировали распространять этот графический интерфейс, мы могли бы упаковать его в самостоятельную выполняемую программу — скомпилированный двоичный файл (frozen binary) — с использованием сторонних инструментов, таких как Py2Exe, PyInstaller и других (дополнительную информацию ищите в Интернете). Такие программы можно запускать, не устанавливая Python на компьютер клиента, потому что интерпретатор байт-кода включается непосредственно в выполняемый файл.
Я оставлю все эти расширения для дальнейшего обдумывания и вернусь к некоторым из них далее в этой книге.
Два примечания, прежде чем двинуться дальше. Во-первых, я должен упомянуть, что программистам на языке Python доступно множество пакетов создания графических интерфейсов. Например, если вам потребуется реализовать графический интерфейс, состоящий не только из простых окон, вы сможете воспользоваться виджетом Canvas из библиотеки tkinter, поддерживающим возможность создания произвольной графики. Сторонние расширения, такие как Blender, OpenGL, VPython, PIL, VTK, Maya и PyGame, предоставляют еще более совершенные инструменты создания графических изображений, визуализации и воспроизведения анимационных эффектов для использования в сценариях на языке Python. Кроме того, возможности модуля tkinter могут быть расширены с помощью библиотек виджетов PMW, Tix и ttk, упоминавшихся ранее. Описание библиотек Tix и ttk вы найдете в руководстве по стандартной библиотеке Python, а также попробуйте поискать сторонние графические расширения на сайте PyPI или в Интернете.
Из уважения к поклонникам других инструментов создания графических интерфейсов, таких как wxPython и PyQt, я должен заметить, что существуют и другие средства разработки графических интерфейсов, выбор которых иногда зависит от личных предпочтений. Модуль tkinter был продемонстрирован здесь потому, что он является достаточно зрелым, надежным, полностью открытым, хорошо документированным, эффективно поддерживаемым и легковесным инструментом, входящим в состав стандартной библиотеки Python. По большому счету, он является стандартом для построения переносимых графических интерфейсов на языке Python.
Другие инструменты создания графических интерфейсов для сценариев на языке Python обладают своими достоинствами и недостатками, которые будут обсуждаться далее в книге. Например, за использование более богатых наборов виджетов приходится платить некоторым усложнением программного кода. Библиотека wxPython, к примеру, обладает более богатыми возможностями, но она значительно сложнее в использовании. Однако другие инструменты в значительной степени являются лишь вариациями на ту же тему — изучив один инструмент создания графического интерфейса, вы легко и просто овладеете и другими. Поэтому в этой книге мы не будем рассматривать примеры применения разных инструментов, а сосредоточимся на том, чтобы полностью освоить один инструмент.
Хотя программы с традиционными графическими интерфейсами, построенными, например, с помощью tkinter, при необходимости могут поддерживать доступ из сети, обычно они выполняются на единственном, отдельном компьютере. Иногда даже веб-страницы считаются разновидностью графического интерфейса, но, чтобы составить свое собственное мнение, вам необходимо прочитать следующий и последний раздел этой главы.
На досуге…
Конечно, библиотека обладает гораздо более широкими возможностями, чем было продемонстрировано в этом предварительном обзоре, и мы подробно будем знакомиться с ними далее в этой книге. В качестве еще одного небольшого примера, для демонстрации некоторых дополнительных возможностей библиотеки tkinter, ниже приводится сценарий fungui.py. В этом сценарии используется модуль random из библиотеки Python — для организации выбора из списка; конструктор Toplevel — для создания нового независимого окна; и функция обратного вызова after — для повторного вызова метода через указанное количество миллисекунд:
from tkinter import * import random fontsize = 30
colors = [‘red’, ‘green’, ‘blue’, ‘yellow’, ‘orange’, ‘cyan’, ‘purple’]
def onSpam():
popup = Toplevel()
color = random.choice(colors)
Label(popup, text=’Popup’, bg=’black’, fg=color).pack(fill=BOTH) mainLabel.config(fg=color)
def onFlip():
mainLabel.config(fg=random.choice(colors)) main.after(250, onFlip)
def onGrow(): global fontsize fontsize += 5 mainLabel.config(font=(‘arial’, fontsize, ‘italic’)) main.after(100, onGrow)
main = Tk()
mainLabel = Label(main, text=’Fun Gui!’, relief=RAISED) mainLabel.config(font=(‘arial’, fontsize, ‘italic’),
fg=’cyan’,bg=’navy’)
mainLabel.pack(side=TOP, expand=YES, fill=BOTH)
Запустите этот сценарий, чтобы посмотреть, как он работает. Он создает главное окно с надписью внутри и тремя кнопками — щелчок на первой кнопке приводит к появлению нового окна с меткой, цвет которой выбирается случайным образом. Щелчок на двух других кнопках приводит к запуску независимых циклов вызовов методов обработчиков по таймеру, один из которых постоянно изменяет цвет надписи в главном окне, а другой постепенно увеличивает размер главного окна и шрифта надписи в нем. Но будьте внимательны, когда будете щелкать на последней кнопке, — изменение размеров выполняется со скоростью 10 раз в секунду, поэтому не упустите возможность закрыть окно, пока оно не убежало от вас. Эй, я же предупредил вас!
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011