575
def editShowWebPage(self, *ignore):
indexes = self.listBox.curselection()
if not indexes or len(indexes) > 1:
return index = indexes[0]
url = self.data[self.listBox.get(index)]
webbrowser.open_new_tab(url)
Когда пользователь вызывает этот метод, мы отыскиваем выбранную закладку и извлекаем соответствующий адрес URL из словаря self.da
ta
. Затем, с помощью функции webbrowser.open_new_tab() из модуля webbrowser
, мы открываем вебброузер, передавая ему указанный адрес
URL. Если перед этим вебброузер еще не был открыт, он будет автома
тически запущен.
application = tkinter.Tk()
path = os.path.join(os.path.dirname(__file__), "images/")
if sys.platform.startswith("win"):
icon = path + "bookmark.ico"
application.iconbitmap(icon, default=icon)
else:
application.iconbitmap("@" + path + "bookmark.xbm")
window = MainWindow(application)
application.protocol("WM_DELETE_WINDOW", window.fileQuit)
application.mainloop()
Последние строки программы похожи на последние строки в програм
ме interesttk.pyw, которая рассматривалась ранее, за исключением трех различий. Первое различие заключается в том, что когда пользо
ватель щелкнет на кнопке закрытия окна, в программе Bookmarks,
в отличие от программы Interest, будет вызван другой метод. Другое от
личие состоит в том, что в системе Windows метод iconbitmap() имеет дополнительный аргумент, позволяющий указывать пиктограмму по умолчанию, которая будет отображаться в заголовках всех окон прило
жения – в системах UNIX это делать необязательно, так как это проис
ходит автоматически. И последнее различие заключается в том, что мы определяем текст, отображаемый в заголовке окна приложения, внут
ри методов класса MainWindow, а не здесь. В программе Interest заголовок окна программы никогда не изменялся, поэтому было достаточно уста
новить его только один раз, но в программе Bookmark мы изменяем текст заголовка, включая в него имя текущего файла с закладками.
Теперь, когда мы рассмотрели реализацию класса главного окна и про
граммный код, выполняющий инициализацию программы и запус
кающий цикл обработки событий, можно обратить свое внимание на диалог AddEditForm.
576
Глава 13. Введение в программирование графического интерфейса
Создание собственного диалога
Диалог AddEditForm представляет собой средство, с помощью которого пользователь может добавлять и редактировать имена закладок и ад
реса URL. Окно диалога показано на рис. 13.6, где диалог использует
ся для редактирования существующей закладки (отсюда слово «Edit»
(правка) в заголовке окна). Тот же диалог может использоваться для добавления закладок. Сначала рассмотрим метод инициализации,
разбив его на четыре части.
class AddEditForm(tkinter.Toplevel):
def __init__(self, parent, name=None, url=None):
super(AddEditForm, self).__init__(parent)
self.parent = parent self.accepted = False self.transient(self.parent)
self.title("Bookmarks " + (
"Edit" if name is not None else "Add"))
self.nameVar = tkinter.StringVar()
if name is not None:
self.nameVar.set(name)
self.urlVar = tkinter.StringVar()
self.urlVar.set(url if url is not None else "http://")
Мы решили унаследовать класс tkinter.TopLevel – виджет, который создан, чтобы служить базовым классом для виджетов, которые будут играть роль окон верхнего уровня. (Как отмечалось ранее, мы могли бы использовать вызов super().__init__(parent), но так как этот прием уже не работает, мы вызываем функцию super(), явно передавая ей роди
тельский класс и объект self в виде аргументов.) Мы сохраняем ссылку на родительский виджет, создаем атрибут self.accepted и присваиваем ему значение False. Вызов метода transient() выполняется, чтобы про
информировать родительское окно, что данное окно всегда должно по
являться поверх родительского окна. В зависимости от того, было ли передано имя закладки и адрес URL, в заголовок окна записывается текст, отражающий выполняемую операцию – добавление или редак
тирование. Затем создаются два объекта tkinter.StringVar – для хране
ния имени закладки и адреса URL, которые инициализируются полу
ченными значениями, если диалог используется для редактирования.
Рис. 13.6. Диалог добавления/редактирования в программе Bookmarks
Программы с главным окном
577 frame = tkinter.Frame(self)
nameLabel = tkinter.Label(frame, text="Name:", underline=0)
nameEntry = tkinter.Entry(frame, textvariable=self.nameVar)
nameEntry.focus_set()
urlLabel = tkinter.Label(frame, text="URL:", underline=0)
urlEntry = tkinter.Entry(frame, textvariable=self.urlVar)
okButton = tkinter.Button(frame, text="OK", command=self.ok)
cancelButton = tkinter.Button(frame, text="Cancel",
command=self.close)
nameLabel.grid(row=0, column=0, sticky=tkinter.W, pady=3,
padx=3)
nameEntry.grid(row=0, column=1, columnspan=3,
sticky=tkinter.EW, pady=3, padx=3)
urlLabel.grid(row=1, column=0, sticky=tkinter.W, pady=3,
padx=3)
urlEntry.grid(row=1, column=1, columnspan=3,
sticky=tkinter.EW, pady=3, padx=3)
okButton.grid(row=2, column=2, sticky=tkinter.EW, pady=3,
padx=3)
cancelButton.grid(row=2, column=3, sticky=tkinter.EW, pady=3,
padx=3)
Виджеты создаются и располагаются в сетке, как показано на рис. 13.7.
Виджеты ввода текста имени и адреса URL ассоциированы с соответст
вующими переменными типа tkinter.StringVar, а две кнопки связаны с методами self.ok() и self.close(), которые будут показаны немного ниже.
frame.grid(row=0, column=0, sticky=tkinter.NSEW)
frame.columnconfigure(1, weight=1)
window = self.winfo_toplevel()
window.columnconfigure(0, weight=1)
Изменять размеры диалога имеет смысл только по горизонтали, поэто
му для второй
колонки рабочей области окна мы указали, что она до
пускает изменение размера по горизонтали, установив вес колонки равным 1. Это означает, что при изменении горизонтального размера рабочей области окна все дополнительное пространство будет отдавать
ся виджетам в колонке с порядковым номером 1 (виджетам ввода име
ни и адреса URL). Точно так же для колонки самого окна мы указали,
nameLabel nameEntry urlLabel urlEntry okButton cancelButton
Рис. 13.7. Схема расположения виджетов в окне диалога добавления/редактирования программы Bookmarks 578Глава 13. Введение в программирование графического интерфейса что она допускает изменение размера по горизонтали, установив ее вес равным 1. Если пользователь попытается изменить вертикальный раз
мер диалога, все виджеты сохранят свое положение относительно друг друга и сместятся в центр окна по вертикали. Но если пользователь по
пытается изменить горизонтальный размер диалога, виджеты ввода имени и адреса URL изменят свой горизонтальный размер, уменьшив
шись или растянувшись по горизонтали так, чтобы привести свои раз
меры в соответствие с доступным пространством.
self.bind("
", lambda *ignore: nameEntry.focus_set())
self.bind("", lambda *ignore: urlEntry.focus_set())
self.bind("", self.ok)
self.bind("", self.close)
self.protocol("WM_DELETE_WINDOW", self.close)
self.grab_set()
self.wait_window(self)
Ранее были созданы две метки,
Name: и URL:, показывающие, что име
ются две горячие комбинации клавиш
Alt+N и Alt+U, нажатие которых вызывает перемещение фокуса ввода в соответствующий виджет ввода текста. Чтобы подкрепить это конкретной реализацией, мы добавили необходимые привязки клавиш. Вместо прямого вызова метода fo
cus_set()
мы использовали лямбдафункции, чтобы иметь возмож
ность игнорировать объект события, который автоматически переда
ется в виде аргумента. Мы также предусмотрели стандартные привяз
ки клавиш (
Enter и Esc) для кнопок OK и Cancel.
Метод protocol() использован, чтобы определить, какой метод должен вызываться, когда пользователь попытается покинуть диалог щелч
ком на кнопке закрытия окна. Оба вызова grab_set() и wait_window() не
обходимы, чтобы сделать диалог модальным.
def ok(self, event=None):
self.name = self.nameVar.get()
self.url = self.urlVar.get()
self.accepted = True self.close()
Этот метод будет вызван, если пользователь щелкнет на кнопке
OK
(или нажмет клавишу
Enter). Он скопирует текст из ассоциированных переменных типа tkinter.StringVar в соответствующие переменные эк
земпляра (которые создаются только сейчас), в переменную self.ac
cepted будет записано значение True и будет вызван метод self.close(),
который закроет диалог.
def close(self, event=None):
self.parent.focus_set()
self.destroy()
Этот метод вызывается из метода self.ok(), а также когда пользователь щелкнет на кнопке закрытия окна или на кнопке
Cancel (или нажмет
В заключение579клавишу
Esc). Он передает фокус ввода родительскому окну и уничто
жает диалог. В данном случае слово «уничтожит» означает, что будут уничтожены только само окно и все виджеты, содержащиеся в нем, сам же экземпляр класса AddEditForm продолжит свое существование, так как вызывающая программа попрежнему имеет ссылку на него.
После закрытия диалога вызывающая программа проверит значение переменной accepted и, если оно равно True, извлечет имя и адрес URL,
которые были добавлены или отредактированы. Затем, как только по
ток выполнения покинет метод MainWindow.editAdd() или MainWindow.ed
itEdit()
, объект AddEditForm выйдет из области видимости и будет за
планирован для утилизации механизмом сборки мусора.
В заключениеВ этой главе вы получили некоторое представление о программирова
нии графического интерфейса с использованием библиотеки Tk. Ос
новное преимущество библиотеки Tk заключается в том, что она вхо
дит в комплект поставки Python как стандартный компонент. Но она имеет множество недостатков, из которых не самый последний заклю
чается в том, что эта старинная библиотека работает несколько иначе,
чем большинство более современных альтернатив.
Если вы плохо знакомы с принципами программирования графиче
ского интерфейса, запомните, что основными кроссплатформенными альтернативами библиотеке Tk являются библиотеки PyGTK, PyQt и wxPython, намного более простые в освоении и использовании, при
чем все они позволяют добиться лучших результатов при меньшем объеме программного кода. Более того, для всех этих альтернатив биб
лиотеке Tk
имеется и более качественная документация, учитываю
щая специфику языка Python, они содержат намного больше видже
тов с более привлекательным внешним видом и позволяют создавать собственные виджеты с нуля, обеспечивая возможность целиком и полностью контролировать их внешний вид и поведение.
Несмотря на то, что библиотеку Tk удобно использовать при создании очень маленьких программ или когда в распоряжении программиста имеется только стандартная библиотека Python, тем не менее во всех остальных случаях любая из кроссплатформенных библиотек будет лучшим выбором.
УпражненияДля выполнения первого упражнения необходимо будет скопировать и модифицировать программу Bookmarks, которая была продемонст
рирована в этой главе. Во втором упражнении будет предложено соз
дать программу с графическим интерфейсом с самого начала.
580
Глава 13. Введение в программирование графического интерфейса
1. Скопируйте программу bookmarkstk.pyw и модифицируйте ее так,
чтобы она могла импортировать и экспортировать файлы DBM, кото
рые используются программой bookmarks.py (созданной в главе 11).
Добавьте в меню
File два новых пункта – Import и Export. Не забудьте выполнить привязку горячих комбинаций клавиш для них (имейте в виду, что комбинация
Ctrl+E уже используется для пункта меню
Edit
→Edit). Кроме того, добавьте на панель инструментов соответст
вующие кнопки. Для этого в метод инициализации придется доба
вить порядка пяти строк программного кода.
Дополнительно потребуется создать два метода – fileImport() и fi
leExport()
, общий размер которых составляет немногим меньше
60 строк, включая обработку ошибок. При реализации операции импортирования вы сами можете решить, как выполнять импорти
рование – объединять импортируемые закладки с существующими или замещать их. Реализация сама по себе несложная, но требует некоторого внимания. Решение (в котором импортируемые заклад
ки объединяются с существующими) приводится в файле book
markstk_ans.py
2. В главе 12 мы видели, как создавать и использовать регулярные выражения для поиска соответствий в тексте. Создайте программу с графическим интерфейсом в виде диалога, которая могла бы ис
пользоваться для ввода и тестирования регулярных выражений,
как показано на рис. 13.8.
Рис. 13.8. Окно программы Regex
Упражнения
581Вам потребуется ознакомиться с документацией к модулю re, так как программа должна вести себя корректно как при вводе регу
лярных выражений, содержащих ошибки, так и при
выполнении итераций по группам, так как в большинстве случаев в регулярных выражениях количество сохраняющих групп меньше, чем меток,
предусмотренных для отображения их содержимого. Обеспечьте в программе полную поддержку клавиатуры, включая переход в виджеты ввода текста с использованием комбинаций
Alt+R и Alt+T,
управление флажками с использованием комбинаций
Alt+I и Alt+D,
завершение программы с использованием комбинаций
Ctrl+Q и Esc и пересчет результатов при нажатии и отпускании клавиш в любом из виджетов ввода текста, а также при изменении состояния любо
го из флажков.
Программа не очень сложная, хотя над программным кодом, ото
бражающим совпадения и номера групп (и имена, там где они ука
заны), придется немного поломать голову. Решение приводится в файле
regextk.py, содержащем примерно сто сорок строк про
граммного кода.
ЭпилогЕсли вы прочитали хотя бы первые шесть глав и либо выполнили уп
ражнения, либо написали несколько своих собственных программ на языке Python 3, вы уже обладаете неплохим фундаментом, на основе которого сможете расширять свой дальнейший опыт и навыки в соот
ветствии со своими потребностями – Python не будет сковывать ваше продвижение!
Для углубления знаний и улучшения навыков программирования на языке Python, если вы прочитали только первые шесть глав, вам обя
зательно нужно ознакомиться с материалом главы 7 и прочитать хотя бы часть главы 8 – в частности, раздел, где описываются менеджеры контекста и инструкция with.
Стоит иметь в виду, что процесс разработки с чистого листа не дает ни
чего,
кроме чувства гордости и расширения знаний, так как необходи
мость в этом при использовании языка Python возникает крайне ред
ко. Мы уже упоминали стандартную библиотеку и каталог пакетов Py
thon Package Index,
pypi.python.org/pypi, содержащих огромный объем функциональных возможностей. Кроме того, значительное число со
ветов, подсказок и идей предлагается в справочнике рецептов «Python
Cookbook» по адресу
code.activestate.com/recipes/langs/python/, хотя на момент написания этой книги он был ориентирован на использова
ние Python 2.
Помимо всего прочего, существует возможность создавать модули для языка Python на других языках программирования (на любых, кото
рые могут экспортировать функции C, что доступно во многих язы
ках). Эти модули могут разрабатываться для совместной работы с язы
ком Python посредством C API языка Python. С помощью модуля ctypes из программного кода на языке Python можно получить доступ к функ
циональным возможностям, заключенным в библиотеках совместного использования (библиотеки DLL в Windows), наших собственных или полученных от сторонних разработчиков, что дает нам практически неограниченный доступ к огромному количеству функциональных возможностей, которые можно получить в Интернете благодаря умени
ям и великодушию программистов, создающих свободные программ
ные продукты.
Эпилог
583
Если у вас появится желание поучаствовать в жизни сообщества Py
thon, можно начать с сайта www.python.org/community, где вы найдете массу справочных материалов и списки рассылки по интересам.
Алфавитный указательСпециальные символы@ (оператор декоратора), 289
_ (символ подчеркивания), 70
!= (оператор неравенства), 284, 440
% (оператор деления по модулю/остаток от деления), 74
& (оператор битовое И), 76, 147, 155
&= (инструкция присваивания, комбинированная с операцией битовое И), 147
[] (оператор индексирования, доступа к элементам последовательности, извлечения среза), 89, 131, 132, 136,
139, 140, 307, 315, 344
* (оператор умножения, дублирования строк, распаковки последователь
ностей, в инструкции from ... import),
74, 131, 133, 136, 164, 231, 493
** (оператор возведения в степень, распаковки отображений), 74, 211
*= (инструкция присваивания, комбинированная с оператором умножения), 131, 136
+ (оператор сложения, конкатенации),
74, 131, 136, 164
+= (инструкция присваивания, комбинированная с оператором сложения, оператор расширения), 131,
136, 171
(оператор вычитания, отрицания), 74,
147
|=
(инструкция присваивания, комбинированная с операцией битовое
ИСКЛЮЧАЮЩЕЕ ИЛИ), 148
= (инструкция присваивания, комбинированная с операцией вычитания), 147
/ (оператор деления), 74
// (оператор целочисленного деления с усечением дробной части), 74
< (оператор «меньше чем»), 172, 440
<< (оператор «сдвиг влево»), 76
<= (оператор «меньше или равно»), 147,
440
= (оператор связывания имени, создания ссылки на объект и присваивания), 30,
173
== (оператор равенства), 284, 440
^ (битовый оператор ИСКЛЮЧАЮЩЕЕ
ИЛИ), 76, 148
| (битовый оператор ИЛИ), 148
(битовый оператор НЕ), 76
> (оператор «больше чем»), 440
>= (оператор «больше или равно»), 147,
440
>> (оператор «сдвиг вправо»), 76