Библиотека сайта rus-linux.net
Р.Сузи. Спецкурс "Язык Python и его приложения"
Лекция 10. Создание приложений с графическим интерфейсом
Для Python существует множество библиотек для создания графического интерфейса пользователя (GUI). Здесь будет рассмотрена только одна -- Tkinter. Это привязка Python к Tcl/Tk. Она поставляется вместе с Python. Для построения графических интерфейсов частенько используют построители (Builders), но мы рассмотрим "ручной" вариант GUI-программирования: он поможет лучше уяснить принципы работы графических приложений.
Почти все современные графические интерфейсы общего назначения строятся по модели WIMP - Window, Icon, Menu, Pointer (окно, иконка, меню, курсор [обычно мыши]). Внутри окон рисуются элементы графического интерфейса, именуемые "виджетами" (widget - штучка).
Вот лишь некоторые виджеты из Tcl/Tk:
Toplevel- Окно верхнего уровня (корневой виджет)
Button- Кнопка. Простая кнопка для выполнения команды и других действий
Canvas- Рисунок. Может использоваться для вывода графических примитивов, например, для построения графиков
Checkbutton- Флажок. Кнопка, имеющая два состояния, при нажатии изменяет состояние на противоположное
Entry- Поле ввода текста
Frame- Рамка. Содержит в себе другие визуальные компоненты
Label- Этикетка. Показывает некоторый текст или графическое изображение
Listbox- Список. Показывает список, из которых пользователь может выделить один или несколько элементов
Menu- Меню. Служит для организации всплывающих (popup) и ниспадающих (pulldown) меню
Menubutton- Кнопка-меню. Используется для организации pulldown-меню
Message- Сообщение. Аналогично
Label, но позволяет заворачивать длинные строки и легко меняет свой размер Radiobutton- Переключатель. Представляет одно из альтернативных значений некоторой переменной. Обычно действует в группе. При нажатии на одну из кнопок, все остальные кнопки автоматически "отскакивают", совсем как кнопки выбора волны на радиоприемнике
Scale- Шкала. Позволяет задать числовое значение путем перемещения движка
Scrollbar- Полоса прокрутки. Может использоваться вместе с некоторыми другими компонентами для их прокрутки
Text- Форматированный текст. Позволяет показывать, редактировать и форматировать текст с использованием различных стилей, а также внедрять в текст рисунки и окна.
Примеры виджетов (порождены этой программой)

Рассмотрим подробнее следующую программу:
from Tkinter import *
import math
def Square(radius):
return math.pi * radius**2
def calculate_square():
radius = float(radius_entry.get())
try:
square = "%11.3f" % Square(radius)
except:
square = "?"
square_label.configure(text=square)
root = Tk()
root.title("Площадь круга")
frame = Frame(root)
frame.pack()
radius_entry = Entry(frame, width=10)
radius_entry.grid(row=0, column=0)
square_label = Label(frame, text="?")
square_label.grid(row=0, column=1)
eval_button = Button(frame, text="Calculate", width=10,
command=calculate_square)
eval_button.grid(row=1, column=0)
exit_button = Button(frame, text="Exit", width=10,
command=root.destroy)
exit_button.grid(row=1, column=1)
root.mainloop()
Результаты работы этой программы:

Заметьте в примере выше способ вызова функции для обработки
собятия нажатие на кнопку: он задается именованным
аргументом command. Аналогично кнопка
exit_button выполняет закрытие окна (и всего приложения),
так как ей задан метод destroy объекта root.
Окно графического приложения состоит из вложенных друг в друга виджетов. Для примера выше дерево виджетов выглядит так:

Расположением виджетов управляют менеджеры расположения. В Tkinter их три разновидности:
pack- "Упаковывает" виджеты друг к другу так, чтобы они "тяготели" к определенным краям. Самый простой из менеджеров расположения, но достаточен для многих задач.
grid- Располагает виджеты в ячейках прямоугольной сетки (почти как в электронной таблице).
place- Располагает подчиненные виджеты относительно виджета-хозяина, в абсолютных или относительных координатах.
В основном GUI-приложения состоят из двух фаз: (I) фазы генерации интерфейса и (II) фазы цикла обработки событий. На фазе генерации интерфейса каждый виджет создается (при этом возможно задание некоторых аргументов); прикрепляется одним из указанных трех методов к своему хозяину. Виджеты появляются на экране только в результате прикрепления к уже прикрепленным виджетам. Для запуска фазы II используется метод mainloop() Toplevel-виджета. На второй фазе изменение свойств виджетов производится с помощью метода configure().
Каждый виджет может обрабатывать возникающие в нем события: движения курсора мыши, ввод с клавиатуры и т.п. Основные события в Tkinter:
KeyPress- нажатие клавиши на клавиатуре
KeyRelease- отпускание клавиши на клавиатуре
ButtonPress- нажатие кнопки мыши
ButtonRelease- отпускание кнопки мыши
Motion- движение мыши (в рамках компетенции виджета)
Enter- вхождение курсора в пределы виджета
Leave- выход курсора за пределы виджета
MouseWheel- кручение колесика мыши
Visibility- изменение видимости окна
FocusIn- окно получает фокус
FocusOut- окно теряет фокус
Reparent- изменение родителя окна
Destroy- уничтожение окна
Activate- окно становится активным
Deactivate- окно становится неактивным
Описание события задается строкой (часть элементов может быть опущена):
<модификаторы-тип-детализация>
Например,
- "<ButtonPress-3>" или "<3>" -- нажатие правой кнопки мыши
- "<Double-Button-1>" -- двойной щелчок левой кнопки мыши
- "<Shift-Double-Button-1>" -- двойной щелчок левой кнопки мыши при нажатой клавише Shift
- "<space>" -- нажатие клавиши Пробел
- "k" -- нажатие клавиши с символом "k"
Пример программы, в которой текстовое поле перехватывает движения мыши и пишет атрибуты события (event):
from Tkinter import *
tk = Tk()
txt = Text(tk)
txt.pack()
def give_info(event):
# event.widget - виджет, в котором произошло событие
txt.delete("1.0", END)
items = event.__dict__.items()
items.sort()
for k, v in items:
txt.insert(END, "%s: %s\n" % (k, v))
txt.bind("<Motion>", give_info)
tk.mainloop()
Результат:

Конечно, кажды виджет имеет большое количество всевозможных параметров, которые можно задавать и затем изменять. (Например, так мы изменяли текст в этикетке: <cpde>square_label.configure(text=square).
Для удобства связи между виджетами применяются специальные
переменные (они бывают четырех типов:
BooleanVar,
StringVar,
FloatVar,
IntegerVar -- по представляемым ими типам.
Особенность этих переменных в том, что использующие их виджеты могут изменять свое состояние в зависимости от этих переменных и, наоборот, переменные могут изменять состояние в зависимости от изменения состояния виджетов под действием пользователя графического интерфейса.
Следующая программа dclock.py иллюстрирует работу переменной, в результате чего мы получаем "электронные часы":

Напоследок важное замечание: не гонитесь за использованием как можно большего числа различных штучек, так как удобство интерфейса зависит не от этого.
