Наши партнеры








Книги по Linux (с отзывами читателей)

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

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