Библиотека сайта 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 иллюстрирует работу переменной, в результате чего мы получаем "электронные часы":
Напоследок важное замечание: не гонитесь за использованием как можно большего числа различных штучек, так как удобство интерфейса зависит не от этого.