События

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

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

Типы событий

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

Передача события в метод bind()

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

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

  • <Button-1> – клик левой кнопкой мыши

  • <Button-2> – клик средней кнопкой мыши

  • <Button-3> – клик правой кнопкой мыши

  • <Double-Button-1> – двойной клик левой кнопкой мыши

  • <Motion> – движение мыши

  • и т. д.

Пример:

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>, т. е. перемещение мыши.

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

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

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

from tkinter import *
 
def exitWin(event):
    root.destroy()
 
def inLabel(event):
    t = ent.get()
    lbl.configure(text = t)
 
def selectAll(event):
    root.after(10, select_all, event.widget)
def select_all(widget):
    widget.selection_range(0, END)
    widget.icursor(END) # курсор в конец
 
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>',inLabel)
ent.bind('<Control-a>',selectAll)
root.bind('<Control-q>',exitWin)
 
root.mainloop()

Пример программы, обрабатывающей события

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

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

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

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

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

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

GUI программы

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

Комментарии

Практическая работа.
from tkinter import *
 
def change1(event):
    t.config(width=int(e1.get()))
    t.config(height=int(e2.get()))
def change2():
    t.config(width=int(e1.get()))
    t.config(height=int(e2.get()))
def change_bg(event, b):
    t.config(bg=b)
 
root=Tk()
e1=Entry(width=5)
e2=Entry(width=5)
b=Button(text="Изменить", command=change2)
b.bind('<Return>', change1)
t=Text()
t.bind('<FocusIn>', lambda event, b="white": change_bg(event,b))
t.bind('<FocusOut>', lambda event, b="lightgrey": change_bg(event,b))
e1.grid(row=0, column=0, sticky=E)
e2.grid(row=1, column=0, sticky=E)
b.grid(row=0, column=1, rowspan=2, sticky=W)
t.grid(row=2, column=0, columnspan=2)
root.mainloop()

Где-то переделал и доработал
from tkinter import *
 
def change(event):
        try:
                t['width']=e1.get()
                t['height']=e2.get()
        except:
                t.insert(0.0, 'Uncorrect value\n')
 
def change2(event, b):
        t['bg']=b
 
root=Tk()
 
f0=Frame()
f0.pack()
f=Frame(f0)
f.pack(side=LEFT)
f1=Frame(f0)
f1.pack(side=LEFT)
 
e1=Entry(f, width=4)
e1.pack()
e2=Entry(f, width=4)
e2.pack()
b=Button(f1, width=10, text='Change')
b.bind('<Return>', change)
b.bind('<Button-1>', change)
b.pack(side=LEFT)
 
t=Text(width=20, height=10, bg='lightgrey')
t.bind('<FocusIn>', lambda event, b='white': change2(event, b))
t.bind('<FocusOut>', lambda event, b='lightgrey': change2(event, b))
t.pack()
 
root.mainloop()

from tkinter import  *
 
def bg_fin(event):
    text['bg'] = 'white'
def bg_fout(event):
    text['bg'] = 'lightgray'
def tw_change(event):
    a = e1.get()
    b = e2.get()
    if a and b != '':
        try:
            int(a) and int(b)
            text.delete(1.0, END)
            text['width'] = a
            text['height'] = b
        except:
            text.delete(1.0,END)
            text.insert(1.0,'Введите только цифры')
    else:
        text.delete(1.0, END)
        text.insert(0.0 , 'Введите два значение')
root= Tk()
fr1 = Frame(root)
fr2 = Frame(fr1)
fr1.pack(side = TOP)
fr2.pack(side = RIGHT)
 
e1 =Entry(fr1,width = 5)
e2 = Entry(fr1,width = 5)
b =Button(fr2,text = 'Изменить')
text = Text(bg = 'lightgray')
 
e1.pack(pady = 2)
e2.pack(pady = 2)
b.pack()
text.pack()
text.bind('<FocusIn>', bg_fin)
text.bind('<FocusOut>', bg_fout)
b.bind('<Button-1>', tw_change)
e1.bind('<Return>',tw_change)
e2.bind('<Return>',tw_change)
 
root.mainloop()

Ответ на от Павел

from tkinter import *
 
root = Tk()
 
e1=Entry()
e1.bind('<Return>', lambda event : text.config(width=int(e1.get())))
e2=Entry()
e2.bind('<Return>', lambda event : text.config(height=int(e2.get())))
b=Button(text='Change to',command=lambda : text.config(width=int(e1.get()),height=int(e2.get())))
text=Text(bg='lightgrey',width=30,height=20)
text.bind('<FocusIn>', lambda event, b="white": text.config(bg=b))
e1.grid(row=0, column=0, sticky=E)
e2.grid(row=1, column=0, sticky=E)
b.grid(row=0, column=1, rowspan=2)
text.grid(row=2, column=0, columnspan=2)
 
root.mainloop()

Я может глупый вопрос задам, но как вернуть переменную из функции, если функция эта записана в ".bind"? Например, здесь в примере есть строка "root.bind('', move)", так вот, если бы функция "move" могла в данном случае возвращать "x" и "y", то как их достать оттуда?

Подскажите, пожалуйста: пишу программу, в которой есть 21 кнопка с изображением. Одинаковые. Игра Баше, если что. Надо, чтоб при нажатии на кнопку изображение менялось и она отключалась. У меня либо вообще ничего не работает, либо применяется ко всем кнопкам сразу.
 def click():
    Button.config(image=image2)
....
p1 = Button(root, image=image)
p1.place(x=z1+10, y=10)
p1.bind('<Button-1>',click)
 
p2 = Button(root, image=image)
p2.place(x=z1+10, y=10)
p2.bind('<Button-1>',click)
 
</code>
Это не работает, так как не видит, к какой конкретно кнопке примениться.
 
<code>
import tkinter as tk
 
 
def _on_button_click(button):
    button.config(image=get_next_image(), state="disabled")
    button2.config(image=get_next_image(), state="disabled")
 
def next_image_generator():
    photo_list = [
        tk.PhotoImage(file="pal.gif"),
        tk.PhotoImage(file="pal2.gif"),
 
    ]
 
    while True:
        yield from photo_list
 
 
NEXT_IMAGE = next_image_generator()
 
def get_next_image():
    return next(NEXT_IMAGE)
 
 
root = tk.Tk()
root.geometry('200x200')
 
button = tk.Button(root, text="ClickMe!", image=get_next_image())
button.config(command=lambda: _on_button_click(button))
button.pack()
 
button2 = tk.Button(root, text="ClickMe!", image=get_next_image())
button2.config(command=lambda: _on_button_click(button))
button2.pack()
 
root.mainloop()
А это применяется ко всем кнопкам сразу.

Тупо списал лямбду с прошлого урока, не до конца понял как это работает, но вроде все пашет)
from tkinter import *
def resize():
    try:
        a,b=entry1.get(),entry2.get()
        text.configure(width=a,height=b)
    except:
        return
def changeColor(event,color):
    text['bg']=color
root = Tk()
frame1=Frame(root);frame1.pack()
frame2=Frame(root);frame2.pack()
but=Button(frame1,width=15,height=2,text='change',command=resize);but.pack(side=RIGHT)
entry1=Entry(frame1,width=4);entry1.pack(side=TOP)
entry2=Entry(frame1,width=4);entry2.pack(side=BOTTOM)
text=Text(frame2,width=30,height=10,bg='lightgrey');text.pack()
text.bind('<FocusIn>',lambda event,c='white':changeColor(event,c))
text.bind('<FocusOut>',lambda event,c='lightgrey':changeColor(event,c))
root.mainloop()
<python>

№1
from tkinter import *
 
def change(widget):
	text.configure(width = entry_1.get(), height = entry_2.get())
 
def text_with_focus(widget):
	text.configure(bg = 'white')
 
def text_without_focus(widget):
	text.configure(bg = 'lightgrey')
 
root = Tk()
entry_1 = Entry()
entry_2 = Entry()
button = Button(text = 'Change')
text = Text()
 
entry_1.pack(side = TOP)
entry_2.pack(side = TOP)
button.pack(side = TOP)
text.pack(side = BOTTOM)
 
button.bind('<Button-1>', change)
button.bind('<Return>', change)
 
text.bind('<FocusIn>', text_with_focus)
text.bind('<FocusOut>', text_without_focus)
 
root.mainloop()
№2
from tkinter import *
 
def change(widget):
	text.configure(width = entry_1.get(), height = entry_2.get())
 
def change_color(widget, color):
	text.configure(bg = color)
 
root = Tk()
entry_1 = Entry()
entry_2 = Entry()
button = Button(text = 'Change')
text = Text()
 
entry_1.pack(side = TOP)
entry_2.pack(side = TOP)
button.pack(side = TOP)
text.pack()
 
button.bind('<Button-1>', change)
button.bind('<Return>', change)
 
text.bind('<FocusIn>', lambda event, color = 'white': change_color(event, color))
text.bind('<FocusOut>', lambda event, color = 'lightgrey': change_color(event, color))
 
root.mainloop()