- 事件(event):是指点击、按键等操作,在tkinter中,event是一个类,当某个事件发生时,生成一个event对象,不同类型的事件生成具有不同属性的event对象。
- 事件处理(event handler):是指在捕获到事件后,程序自动执行的操作,是回调函数(recall function)。
- 事件绑定(event binding):是当一个事件发生时程序能够做出响应。tkinter提供三种绑定方式:实例绑定bind(将某个事件处理绑定到某个组件上)、类绑定bind_class(将某个事件处理绑定到某类组件上)、应用绑定bind_all(将某个事件处理绑定到所有组件上)。
from Tkinter import *
root = Tk()
def eventHandler(event):
print((event.x, event.y))
canvas=Canvas(root, width=100, height=100)
root.bind("<Button-1>", eventHandler)
root.pack()
root.mainloop()
一、事件(event)
(1)事件格式:
在Tkinter中,事件的描述格式为:<[modifier-]-type[-detail]>,其中:
- modifier:事件修饰符。如:Alt、Shit组合键和Double事件。
- type:事件类型。如:按键(Key)、鼠标(Button/Motion/Enter/Leave/Relase)、Configure等。
- detail:事件细节。如:鼠标左键(1)、鼠标中键(2)、鼠标右键(3)。
☆☆☆注意大小写!!!
事件类型 | 事件格式 | 事件解释 |
鼠标事件 | <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> | 当组件变为可视状态时 |
(2)事件对象:
一个具体事件如<Button-1>是事件类(event class)的一个实例,事件类中设定了众多属性,其中部分属性是通用的,另一部分属性属于特定事件类型的,常用属性如下:
属性 | 属性说明 | 适用事件类型 |
.char | 如果按键事件产生通用ASCII字符,这个字符将赋值给event.char。(特殊ASCII字符,如delete等不属于该属性) | <KeyPress> <KeyRelease>等按键事件 |
.keysym | 如果按键事件产生特殊ASCII字符,这个字符将赋值给event.keysym。 | <KeyPress> <KeyRelease>等按键事件 |
.x | 鼠标当前位置横坐标,相对于组件左上角 | |
.y | 鼠标当前位置纵坐标,相对于组件左上角 | |
.x_root | 鼠标当前位置横坐标,相对于屏幕左上角 | |
.y_root | 鼠标当前位置纵坐标,相对于屏幕左上角 | |
.width | 组件大小发生改变后的宽度 | <Configure> |
.height | 组件大小发生改变后的高度 | <Configure> |
.type | 事件类型 | ALL |
二、基于事件的动画(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)