События

Обычно, чтобы приложение с графическим интерфейсом что-то делало, должны происходить те или иные события, чаще всего представляющие собой воздействие человека на элементы GUI.

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

При вызове метода bind событие передается в качестве первого аргумента.

Название события заключается в кавычки, а также в угловые скобки < и >. События описывается с помощью зарезервированных последовательностей ключевых слов.

Часто используемые события, производимые мышью:

Пример:

from tkinter import *
 
 
def b1(event):
    root.title("Левая кнопка мыши")
 
 
def b3(event):
    root.title("Правая кнопка мыши")
 
 
def move(event):
    x = event.x
    y = event.y
    s = "Движение мышью {}x{}".format(x, y)
    root.title(s)
 
 
root = Tk()
root.minsize(width=500, height=400)
 
root.bind('<Button-1>', b1)
root.bind('<Button-3>', b3)
root.bind('<Motion>', move)
 
root.mainloop()

В этой программе меняется надпись в заголовке главного окна в зависимости от того, двигается мышь, щелкают левой или правой кнопкой.

Событие (Event) – это один из объектов tkinter. У событий есть атрибуты, как и у многих других объектов. В примере в функции move извлекаются значения атрибутов x и y объекта event, в которых хранятся координаты местоположения курсора мыши в пределах виджета, по отношению к которому было сгенерировано событие. В данном случае виджетом является главное окно, а событием – <Motion>, т. е. перемещение мыши.

В программе ниже выводится информация об экземпляре Event и некоторым его свойствам. Все атрибуты можно посмотреть с помощью команды dir(event). У разных событий они одни и те же, меняются только значения. Для тех или иных событий часть атрибутов не имеет смысла, такие свойства имеют значения по умолчанию.

В примере хотя обрабатывается событие нажатия клавиши клавиатуры, в поля x, y, x_root, y_root сохраняются координаты положения на экране курсора мыши.

from tkinter import *
 
 
def event_info(event):
    print(type(event))
    print(event)
    print(event.time)
    print(event.x_root)
    print(event.y_root)
 
 
root = Tk()
root.bind('a', event_info)
root.mainloop()

Пример выполнения программы:

<class 'tkinter.Event'>
<KeyPress event state=Mod2 keysym=a keycode=38 
                             char='a' x=9 y=7>
8379853
37
92

Для событий с клавиатуры буквенные клавиши можно записывать без угловых скобок (например, 'a').

Для неалфавитных клавиш существуют специальные зарезервированные слова. Например, <Return> - нажатие клавиши Enter, <space> - пробел. (Заметим, что есть событие <Enter>, которое не имеет отношения к нажатию клавиши Enter, а происходит, когда курсор заходит в пределы виджета.)

Рассмотрим программу:

from tkinter import *
 
 
def enter_leave(event):
    if str(event.type) == 'Enter':
        event.widget['text'] = 'In'
    elif str(event.type) == 'Leave':
        event.widget['text'] = 'Out'
 
 
root = Tk()
 
lab1 = Label(width=20, height=3, bg='white')
lab1.pack()
lab1.bind('<Enter>', enter_leave)
lab1.bind('<Leave>', enter_leave)
 
lab2 = Label(width=20, height=3, bg='black',
             fg='white')
lab2.pack()
lab2.bind('<Enter>', enter_leave)
lab2.bind('<Leave>', enter_leave)
root.mainloop()

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

Функция, в зависимости от того, по отношению к какому виджету было зафиксировано событие, изменяет свойства только этого виджета. Как изменяет, зависит от произошедшего события.

Свойство event.widget содержит ссылку на виджет, сгенерировавший событие. Свойство event.type описывает, что это было за событие.

Вернемся к событиям клавиатуры. Сочетания клавиш пишутся через тире. В случае использования так называемого модификатора, он указывается первым, детали на третьем месте. Например, <Shift-Up> - одновременное нажатие клавиш Shift и стрелки вверх, <Control-B1-Motion> – движение мышью с зажатой левой кнопкой и клавишей Ctrl.

from tkinter import *
 
 
def exit_win(event):
    root.destroy()
 
 
def to_label(event):
    t = ent.get()
    lbl.configure(text=t)
 
 
def select_all(event):
 
    def select_all2(widget):
        widget.selection_range(0, END)
        widget.icursor(END)  # курсор в конец
 
    root.after(10, select_all2, event.widget)
 
 
root = Tk()
 
ent = Entry(width=40)
ent.focus_set()
ent.pack()
lbl = Label(height=3, fg='orange',
            bg='darkgreen', font="Verdana 24")
lbl.pack(fill=X)
 
ent.bind('<Return>', to_label)
ent.bind('<Control-a>', select_all)
root.bind('<Control-q>', exit_win)
 
root.mainloop()

Здесь сочетание клавиш Ctrl+a выделяет текст в поле. Без root.after() выделение не работает. Метод after выполняет функцию, указанную во втором аргументе, через промежуток времени, указанный в первом аргументе. В третьем аргументе передается значение атрибута widget объекта event. В данном случае им будет поле ent. Именно оно будет передано как аргумент в функцию select_all2 и присвоено параметру widget.

Практическая работа

Напишите программу по описанию. Размеры многострочного текстового поля определяются значениями, введенными в однострочные текстовые поля. Изменение размера происходит при нажатии мышью на кнопку, а также при нажатии клавиши Enter.

Цвет фона экземпляра Text светлосерый (lightgrey), когда поле не в фокусе, и белый, когда имеет фокус.

Событие получения фокуса обозначается как <FocusIn>, потери – как <FocusOut>.

Для справки: фокус перемещается по виджетам при нажатии Tab, Ctrl+Tab, Shift+Tab, а также при клике по ним мышью (к кнопкам последнее не относится).

Курс с примерами решений практических работ: android-приложение, pdf-версия.

Tkinter. Программирование GUI на Python