星级说明:★ 了解 · ★★ 理解 · ★★★ 掌握 · ★★★★ 重点必会。
bind 基本用法想让控件"响应鼠标、键盘",就要用到 bind(绑定)。最常见的写法:
# 附录B 示例:把事件绑定到类里的方法(语法已校验通过)
import tkinter as tk
class App:
def __init__(self, root):
self.root = root
self.label = tk.Label(root, text="点我或按键")
self.label.pack(padx=20, pady=20)
# 绑定鼠标左键单击:回调 must 能接收 event 参数
self.label.bind("<Button-1>", self.on_click)
# 绑定回车键(绑在窗口 root 上,全局生效)
self.root.bind("<Return>", self.on_enter)
def on_click(self, event): # 第一个参数是 self,第二个是 event
print("点击坐标(相对控件):", event.x, event.y)
def on_enter(self, event):
print("按下了键:", event.keysym)
if __name__ == "__main__":
root = tk.Tk()
App(root)
root.mainloop()要点(与正文 OOP 写法一致):
控件.bind("<事件序列>", 回调函数)。def 方法(self, event):——tkinter 触发事件时会自动把 event 对象传进来,所以方法必须留第二个参数 event(名字任意,习惯叫 event)。不加这个参数会报 TypeError。self.root 上是"窗口级"事件(全局);绑在某个具体控件上只对该控件生效。bind(..., add="+")。bind_all()(全程序)、bind_class()(某类控件)等扩展写法,用到再查官方文档。tkinter 事件用一串 <...> 包裹的字符串表示,叫"事件序列"。下表覆盖本书最常见写法:
| 事件写法 | 含义 | 示例 |
|---|---|---|
<Button-1> | 鼠标左键单击 | widget.bind("<Button-1>", self.on_left) |
<Button-2> | 鼠标中键单击 | widget.bind("<Button-2>", self.on_mid) |
<Button-3> | 鼠标右键单击 | widget.bind("<Button-3>", self.on_right) |
<Double-1> | 鼠标左键双击 | widget.bind("<Double-1>", self.on_double) |
<Triple-1> | 鼠标左键三击 | widget.bind("<Triple-1>", self.on_triple) |
<B1-Motion> | 按住左键拖动移动 | widget.bind("<B1-Motion>", self.on_drag) |
<B3-Motion> | 按住右键拖动移动 | widget.bind("<B3-Motion>", self.on_drag3) |
<ButtonRelease-1> | 松开左键(点击结束) | widget.bind("<ButtonRelease-1>", self.on_up) |
<Enter> | 鼠标进入控件区域 | widget.bind("<Enter>", self.on_enter_area) |
<Leave> | 鼠标离开控件区域 | widget.bind("<Leave>", self.on_leave_area) |
<Motion> | 鼠标在控件上移动 | widget.bind("<Motion>", self.on_move) |
<Return> | 按下回车键 | entry.bind("<Return>", self.on_enter) |
<Key> | 按下任意键 | root.bind("<Key>", self.on_any_key) |
<KeyPress-a> | 按下 a 键(等价于 <a>) | root.bind("<KeyPress-a>", self.on_a) |
<KeyPress-A> | 按下大写 A(Shift+a) | root.bind("<KeyPress-A>", self.on_A) |
<KeyRelease> | 松开任意键 | root.bind("<KeyRelease>", self.on_key_up) |
<Escape> | 按下 Esc 键 | root.bind("<Escape>", self.on_esc) |
<F1> | 按下 F1 功能键 | root.bind("<F1>", self.on_f1) |
<MouseWheel> | 滚轮滚动(Windows/macOS) | widget.bind("<MouseWheel>", self.on_wheel) |
<FocusIn> | 控件获得焦点 | entry.bind("<FocusIn>", self.on_focus) |
<FocusOut> | 控件失去焦点 | entry.bind("<FocusOut>", self.on_blur) |
<Configure> | 控件大小/位置变化(如窗口缩放) | root.bind("<Configure>", self.on_resize) |
<Destroy> | 控件被销毁 | root.bind("<Destroy>", self.on_quit) |
<<ComboboxSelected>> | 虚拟事件:Combobox 选项被选中 | combo.bind("<<ComboboxSelected>>", self.on_pick) |
<<TreeviewSelect>> | 虚拟事件:Treeview 选中行变化 | tree.bind("<<TreeviewSelect>>", self.on_select) |
<<NotebookTabChanged>> | 虚拟事件:Notebook 切换了标签页 | nb.bind("<<NotebookTabChanged>>", self.on_tab) |
<<ListboxSelect>> | 虚拟事件:Listbox 选中项变化 | lb.bind("<<ListboxSelect>>", self.on_lb) |
📌 两类事件要分清:
- 普通事件用尖括号包裹,如<Button-1>、<Return>、<Key>。
- 虚拟事件(Virtual Event)用双尖括号<<...>>,它表示"某个语义动作发生了",而不是具体哪只鼠标/哪个键——例如<<ComboboxSelected>>无论你用鼠标点还是键盘选都会触发,比监听具体按键更稳妥。双尖括号是虚拟事件的标志,别写成单尖括号。
event)常用属性回调里拿到的 event 是个对象,里面装着这次事件的各种信息。常用属性:
| 属性 | 含义 | 典型取值 / 说明 |
|---|---|---|
x / y | 鼠标位置,相对触发控件左上角(像素) | 整数,如 x=120, y=45 |
x_root / y_root | 鼠标位置,相对屏幕左上角(像素) | 跨窗口定位时用 |
num | 鼠标键号 | 1=左键、2=中键、3=右键(Button 事件里) |
keysym | 按键的符号名(字符串) | 'a'、'Return'、'Escape'、'F1' 等 |
keycode | 按键的数字编码(整数) | 与键盘硬件相关,用来精确区分按键 |
char | 按键对应的字符 | 可打印键如 'a'、'1';功能键为 '' 空串 |
widget | 触发事件的控件对象本身 | 相当于 self.xxx,可在回调里直接操作它 |
type | 事件类型编号 | 内部常量,一般不用 |
delta | 滚轮滚动量(<MouseWheel>) | Windows 上 ±120 的整数,正上负下 |
width / height | 新尺寸(<Configure> 事件) | 窗口缩放后控件的新宽高(像素) |
state | 事件发生时修饰键状态 | 位掩码,可判断是否按住 Ctrl/Shift 等 |
📌x/yvsx_root/y_root区别:x/y是相对"那个控件"左上角的坐标,适合在控件内部画图;x_root/y_root是相对整个屏幕的坐标,做"拖拽窗口""弹出菜单跟随鼠标"时更实用。
📌 键盘事件三件套:想判断"按了哪个键",优先用keysym(人类可读,如'Return');要精确区分物理键用keycode;要拿实际输入的字符用char。keysym和char在<Key>/<KeyPress>事件中都有,num只在鼠标按钮事件里有意义。
控件.bind("<事件>", self.回调),回调写 def 回调(self, event):。<Button-1>/<Double-1>/<B1-Motion>/<Enter>;键盘看 <Return>/<Key>/<KeyPress-a>。<<ComboboxSelected>>、<<TreeviewSelect>>。event.x/event.y,取按键名用 event.keysym,取触发控件用 event.widget。