Tkinter:事件绑定

正如我们此前提到的,一个 Tkinter 应用程序大部分时间花费在事件循环中(通过 mainloop() 方法进入)。事件可以有各种来源:包括用户触发的鼠标和键盘操作和窗口管理器触发的重绘事件(在多数情况下是由用户间接引起的)。

Tkinter 提供一个强大的机制可以让你自由地处理事件,对于每个组件来说,你可以通过 bind() 方法将函数或方法绑定到具体的事件上。

widget.bind(event, handler)
当被触发的事件满足该组件绑定的事件时,Tkinter 就会带着事件对象(Event)去调用 handler() 方法。

这儿有几个例子,大家请随意感受下:

第一个是关于鼠标按键的演示:

# 捕获点击鼠标的位置

import tkinter as tk

root = tk.Tk()

def callback(event):
print("点击位置:", event.x, event.y)

frame = tk.Frame(root, width = 200, height = 200)
frame.bind("<Button-1>", callback)
frame.pack()

root.mainloop()


在上边这个例子中,我们使用 Frame 组件的 bind() 方法将鼠标点击事件(<Button-1>)和我们自定义的 callback() 方法绑定起来。那么运行后的结果是——当你在点击鼠标左键的时候,IDLE 会相应的将鼠标的位置显示出来(如上图)。

第二个是关于键盘按键的演示:

只有当组件获得焦点的时候才能接收键盘事件(Key),下边例子中我们用 focus_set() 获得焦点,当你你可以设置 Frame 的 takefocus 选项为 True,然后使用 Tab 将焦点转移上来。

# 捕获键盘事件

import tkinter as tk

root = tk.Tk()

def callback(event):
print("点击的键盘字符为:", event.char)

frame = tk.Frame(root, width = 200, height = 200)
frame.bind("<Key>", callback)
frame.focus_set()
frame.pack()

root.mainloop()


最后我们再用一个例子展示捕获鼠标在组件上的运动轨迹,这里需要关注的是 <Motion> 事件:

# 捕获鼠标在组件上的运动轨迹

import tkinter as tk

root = tk.Tk()

def callback(event):
print("当前位置为:", event.x, event.y)

frame = tk.Frame(root, width = 200, height = 200)
frame.bind("<Motion>", callback)
frame.pack()

root.mainloop()


下方主要内容:

事件序列
type
modifier
Event 对象
Key names


事件序列

Tkinter 使用一种称为事件序列的机制来允许用户定义事件,用户需使用 bind() 方法将具体的事件序列与自定义的方法相绑定。事件序列是以字符串的形式表示的,可以表示一个或多个相关联的事件(如果是多个事件,那么对应的方法只有在满足所有事件的前提下才会被调用)。

事件序列使用以下语法描述:

<modifier-type-detail>

事件序列是包含在尖括号(<...>)中
type 部分的内容是最重要的,它通常用于描述普通的事件类型,例如鼠标点击或键盘按键点击(详见下方)。
modifier 部分的内容是可选的,它通常用于描述组合键,例如 Ctrl + c,Shift + 鼠标左键点击(详见下方)。
detail 部分的内容是可选的,它通常用于描述具体的按键,例如 Button-1 表示鼠标左键。
举几个例子帮助大家消化:

事件序列 含义
<Button-1> 用户点击鼠标左键
<KeyPress-H> 用户点击 H 按键
<Control-Shift-KeyPress-H> 用户同时点击 Ctrl + Shift + H

事件类型    事件格式    事件解释
鼠标事件    <Button-1>    鼠标点击(1-左键,2-中键,3-右键)
<Double-Button-1>    鼠标双击(1-左键,2-中键,3-右键)
<B1-Motion>    鼠标拖动(1-左键,2-中键,3-右键)
<ButtonRelease-1>    鼠标按下之后释放(1-左键,2-中键,3-右键)
<Enter>    鼠标进入控件范围(widget),不是键盘按键
<Leave>    鼠标离开控件范围(widget)
键盘事件    <Key>/<KeyPress>    任意键盘按键(键值会以char的格式放入event对象)
<Return>

<Cancel>

<BackSpace>

<Tab>

<Shift_L>

<Control_L>

<Alt_L>

<Home>

<Left>

<Up>

<Down>

<Right>

<Delete>

<F1>

<F2> 

对应键盘按键
组件事件    <Configure>    如果widget的大小发生改变,新的大小(width和height)会打包到event发往handler。
<Activate>    当组件从不可用变为可用
<Deactivate>    当组件从可用变为不可用
<Destroy>    当组件被销毁时
<Expose>    当组件从被遮挡状态变为暴露状态
<Map>    当组件由隐藏状态变为显示状态
<Unmap>    当组件由显示状态变为隐藏状态
<FocusIn>    当组件获得焦点时
<FocusOut>    
当组件失去焦点时

<Property>    当组件属性发生改变时
<Visibility>    当组件变为可视状态时
————————————————
版权声明:本文为CSDN博主「xiaozhimonica」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xiaozhimonica/article/details/87873048
View Code


type

type 含义
Activate 当组件的状态从“未激活”变为“激活”的时候触发该事件
Button 1. 当用户点击鼠标按键的时候触发该事件
2. detail 部分指定具体哪个按键:<Button-1>鼠标左键,<Button-2>鼠标中键,<Button-3>鼠标右键,<Button-4>滚轮上滚(Linux),<Button-5>滚轮下滚(Linux)
ButtonRelease 1. 当用户释放鼠标按键的时候触发该事
2. 在大多数情况下,比 Button 要更好用,因为如果当用户不小心按下鼠标,用户可以将鼠标移出组件再释放鼠标,从而避免不小心触发事件
Configure 当组件的尺寸发生改变的时候触发该事件
Deactivate 当组件的状态从“激活”变为“未激活”的时候触发该事件
Destroy 当组件被销毁的时候触发该事件
Enter 1. 当鼠标指针进入组件的时候触发该事件
2. 注意:不是指用户按下回车键
Expose 当窗口或组件的某部分不再被覆盖的时候触发该事件
FocusIn 1. 当组件获得焦点的时候触发该事件
2. 用户可以用 Tab 键将焦点转移到该组件上(需要该组件的 takefocus 选项为 True)
3. 你也可以调用 focus_set() 方法使该组件获得焦点(见上方例子)
FocusOut 当组件失去焦点的时候触发该事件
KeyPress 1. 当用户按下键盘按键的时候触发该事件
2. detail 可以指定具体的按键,例如 <KeyPress-H>表示当大写字母 H 被按下的时候触发该事件
3. KeyPress 可以简写为 Key
KeyRelease 当用户释放键盘按键的时候触发该事件
Leave 当鼠标指针离开组件的时候触发该事件
Map 1. 当组件被映射的时候触发该事件
2. 意思是在应用程序中显示该组件的时候,例如调用 grid() 方法
Motion 当鼠标在组件内移动的整个过程均触发该事件
MouseWheel 1. 当鼠标滚轮滚动的时候触发该事件
2. 目前该事件仅支持 Windows 和 Mac 系统,Linux 系统请参考 Button
Unmap 1. 当组件被取消映射的时候触发该事件
2. 意思是在应用程序中不再显示该组件的时候,例如调用 grid_remove() 方法
Visibility 当应用程序至少有一部分在屏幕中是可见的时候触发该事件


modifier

在事件序列中,modifier 部分的内容可以是以下这些:

modifier 含义
Alt 当按下 Alt 按键的时候
Any 1. 表示任何类型的按键被按下的时候
2. 例如 <Any-KeyPress> 表示当用户按下任何按键时触发事件
Control 当按下 Ctrl 按键的时候
Double 1. 当后续两个事件被连续触发的时候
2. 例如 <Double-Button-1> 表示当用户双击鼠标左键时触发事件
Lock 当打开大写字母锁定键(CapsLock)的时候
Shift 当按下 Shift 按键的时候
Triple 跟 Double 类似,当后续三个事件被连续触发的时候


Event 对象

当 Tkinter 去回调你定义的函数的时候,都会带着 Event 对象(作为参数)去调用,Event 对象以下这些属性你可以使用:

属性 含义
widget 产生该事件的组件
x, y 当前的鼠标位置坐标(相对于窗口左上角,像素为单位)
x_root, y_root 当前的鼠标位置坐标(相对于屏幕左上角,像素为单位)
char 按键对应的字符(键盘事件专属)
keysym 按键名,见下方 Key names(键盘事件专属)
keycode 按键码,见下方 Key names(键盘事件专属)
num 按钮数字(鼠标事件专属)
width, height 组件的新尺寸(Configure 事件专属)
type 该事件类型


Key names

当事件为 <Key>,<KeyPress>,<KeyRelease> 的时候,detail 可以通过设定具体的按键名(keysym)来筛选。例如 <Key-H> 表示按下键盘上的大写字母 H 时候触发事件,<Key-Tab> 表示按下键盘上的 Tab 按键的时候触发事件。

下表列举了键盘所有特殊按键的 keysym 和 keycode:

(下边按键码是对应美国标准 101 键盘的“Latin-1”字符集,键盘标准不同对应的按键码不同,但按键名是一样的)

按键名(keysym) 按键码(keycode) 代表的按键
Alt_L 64 左边的 Alt 按键
Alt_R 113 右边的 Alt 按键
BackSpace 22 Backspace(退格)按键
Cancel 110 break 按键
Caps_Lock 66 CapsLock(大写字母锁定)按键
Control_L 37 左边的 Ctrl 按键
Control_R 109 右边的 Ctrl 按键
Delete 107 Delete 按键
Down 104 ↓ 按键
End 103 End 按键
Escape 9 Esc 按键
Execute 111 SysReq 按键
F1 67 F1 按键
F2 68 F2 按键
F3 69 F3 按键
F4 70 F4 按键
F5 71 F5 按键
F6 72 F6 按键
F7 73 F7 按键
F8 74 F8 按键
F9 75 F9 按键
F10 76 F10 按键
F11 77 F11 按键
F12 96 F12 按键
Home 97 Home 按键
Insert 106 Insert 按键
Left 100 ← 按键
Linefeed 54 Linefeed(Ctrl + J)
KP_0 90 小键盘数字 0
KP_1 87 小键盘数字 1
KP_2 88 小键盘数字 2
KP_3 89 小键盘数字 3
KP_4 83 小键盘数字 4
KP_5 84 小键盘数字 5
KP_6 85 小键盘数字 6
KP_7 79 小键盘数字 7
KP_8 80 小键盘数字 8
KP_9 81 小键盘数字 9
KP_Add 86 小键盘的 + 按键
KP_Begin 84 小键盘的中间按键(5)
KP_Decimal 91 小键盘的点按键(.)
KP_Delete 91 小键盘的删除键
KP_Divide 112 小键盘的 / 按键
KP_Down 88 小键盘的 ↓ 按键
KP_End 87 小键盘的 End 按键
KP_Enter 108 小键盘的 Enter 按键
KP_Home 79 小键盘的 Home 按键
KP_Insert 90 小键盘的 Insert 按键
KP_Left 83 小键盘的 ← 按键
KP_Multiply 63 小键盘的 * 按键
KP_Next 89 小键盘的 PageDown 按键
KP_Prior 81 小键盘的 PageUp 按键
KP_Right 85 小键盘的 → 按键
KP_Subtract 82 小键盘的 - 按键
KP_Up 80 小键盘的 ↑ 按键
Next 105 PageDown 按键
Num_Lock 77 NumLock(数字锁定)按键
Pause 110 Pause(暂停)按键
Print 111 PrintScrn(打印屏幕)按键
Prior 99 PageUp 按键
Return 36 Enter(回车)按键
Right 102 → 按键
Scroll_Lock 78 ScrollLock 按键
Shift_L 50 左边的 Shift 按键
Shift_R 62 右边的 Shift 按键
Tab 23 Tab(制表)按键
Up 98 ↑ 按键

 

基于事件的动画(animation)

(1)模型-视图-控制器(MVC,Model、View and Controller)

        MVC是事件循环运行动画的三个核心内容:

Controller: 当某个事件发生时,tkinter调用适当的(自定义的)事件处理函数(event handler function)或控制器(controller)。
Model: 当某个事件发生后,事件处理函数/控制器会修改模型,这个模型就是存储了底层动画数据的地方,即:这个模型代表动画的状态,初始定义由初始化函数给出。
View: 当事件处理结束后, Tkinterhui 会调用自定义的函数(redrawAll)更新视图,即利用模型内相关数据重新设定画布/视图。
(2)MVC必须遵循如下因果关系:(C→M→V)

控制器更新模型(C→M)
视图需使用来自模型的数据(M→V)
控制器不能直接影响视图(C→V(×))
视图不能修改模型(V→M(×))
(3)一个例子:

圆圈随按键位置改变
输出键盘键值

# Basic Animation Framework
 
from tkinter import *
 
####################################
# customize these functions
####################################
# Set up the model data with init
# init is called once, at the beginning of the program
# data is a Struct, which can be given new data values using data.name = value
# data will be shared across all animation functions- it's aliased!
def init(data):
    # data comes preset with width and height, from the run function
    data.circleSize = min(data.width, data.height) / 10
    data.circleX = data.width/2
    data.circleY = data.height/2
    data.charText = ""
    data.keysymText = ""
 
# Track and respond to mouse clicks
# The event variable holds all of the data captured by the event loop
# For mousePressed, this is event.x and event.y, the position where
# the mouse was clicked
def mousePressed(event, data):
    data.circleX = event.x
    data.circleY = event.y
 
# Track and respond to key presses
# The event variable holds all of the data captured by the event loop
# For keyPressed, this is event.char and event.keysym
# event.char holds the direct key that was pressed, "a", "3", "@", etc.
# event.keysym holds special names for certain keys non-alphanumeric keys
# for example, "space", "BackSpace", "parenleft", "exclam"
def keyPressed(event, data):
    data.charText = event.char
    data.keysymText = event.keysym
 
# Draw graphics normally with redrawAll
# Main difference: the data struct contains helpful information to assist drawing
# Also, the canvas will get cleared and this will be called again
# constantly by the event loop.
def redrawAll(canvas, data):
    canvas.create_oval(data.circleX - data.circleSize, 
                       data.circleY - data.circleSize,
                       data.circleX + data.circleSize,
                       data.circleY + data.circleSize)
    if data.charText != "":
        canvas.create_text(data.width/10, data.height/3,
                           text="char: " + data.charText)
    if data.keysymText != "":
        canvas.create_text(data.width/10, data.height*2/3, 
                           text="keysym: " + data.keysymText)
 
####################################
# use the run function as-is
####################################
 
def run(width=300, height=300):
    def redrawAllWrapper(canvas, data):
        canvas.delete(ALL)
        canvas.create_rectangle(0, 0, data.width, data.height,
                                fill='white', width=0)
        redrawAll(canvas, data)
        canvas.update()    
 
    def mousePressedWrapper(event, canvas, data):
        mousePressed(event, data)
        redrawAllWrapper(canvas, data)
 
    def keyPressedWrapper(event, canvas, data):
        keyPressed(event, data)
        redrawAllWrapper(canvas, data)
 
    # Set up data and call init
    class Struct(object): pass
    data = Struct()
    data.width = width
    data.height = height
    root = Tk()
    init(data)
 
    # create the root and the canvas
    canvas = Canvas(root, width=data.width, height=data.height)
    canvas.pack()
 
    # set up events
    root.bind("<Button-1>", lambda event:
                            mousePressedWrapper(event, canvas, data))
    root.bind("<Key>", lambda event:
                            keyPressedWrapper(event, canvas, data))
    redrawAll(canvas, data)
 
    # and launch the app
    root.mainloop()  # blocks until window is closed
    print("bye!")
 
run(400, 200)
圆圈随按键位置改变

 

https://blog.csdn.net/xiaozhimonica/article/details/87873048

https://blog.csdn.net/qq_41556318/article/details/85274368

posted @ 2021-03-03 14:24  云long  阅读(952)  评论(0编辑  收藏  举报