🐸 青蛙编程网
首页 / Python / 桌面客户端 / 5. 事件与变量:让界面动起来

5. 事件与变量:让界面动起来

这是什么:事件绑定(bind)让控件在"被点击、被按键、被划过"时执行你的函数;Tkinter 变量(StringVar 等)是绑在控件上的特殊盒子——程序改它,界面自动变;界面改它,程序立刻知道。

为什么用:上一章的界面是"死"的——按钮只认 command,输入框要你手动 get()。学会这两样,界面就活了:回车也能提交、勾选状态直接读、输入一个字标签就同步一个字、窗口里还能走一个秒表。

如何用:先用 bind 把"按回车"接进上一章的打招呼机,再用 StringVar 做一个实时字数统计。

bind:什么事件、哪个控件、干什么

import tkinter as tk

def on_key(event):
    label.config(text=f"你按下了:{event.char!r}")

root = tk.Tk()
root.title("键盘监视器")

label = tk.Label(root, text="按任意键", font=("", 16))
label.pack(pady=40)

root.bind("<Key>", on_key)
root.bind("<Button-1>", lambda e: label.config(text="鼠标被点了"))

root.mainloop()

你会看到:按 a 显示"你按下了:'a'";点窗口空白处显示"鼠标被点了"。

事件串 触发时机
<Key> / <Return> / <Escape> 任意键 / 回车 / Esc(<KeyPress-a> 精确到 a 键)
<Button-1> / <Button-3> 鼠标左键 / 右键
<Motion> / <Enter> / <Leave> 拖动 / 指针进入 / 离开控件

回调函数必须收一个参数(那个事件对象),常用字段:event.x、event.y(坐标)、event.char(按键字符)。一行小逻辑不想单独写函数时,用 lambda e: ... 顶替(e 就是事件参数)。

Tkinter 变量:界面和程序双向同步

普通 Python 变量和界面是两路人:你改了变量,标签不知道;用户改了输入框,你也不清楚。特殊变量专门解决这个问题:

import tkinter as tk

root = tk.Tk()
root.title("实时字数统计")
root.geometry("320x140")

text_var = tk.StringVar()
agree_var = tk.BooleanVar(value=False)

def update_count(*_):
    n = len(text_var.get().strip())
    count_label.config(text=f"已输入 {n} 字")
    agree_label.config(text="已同意协议" if agree_var.get() else "还没同意")

entry = tk.Entry(root, textvariable=text_var)   # 绑定!
entry.pack(pady=8)
entry.bind("<KeyRelease>", update_count)

cb = tk.Checkbutton(root, text="同意用户协议", variable=agree_var,
                    command=update_count)
cb.pack()

count_label = tk.Label(root, text="已输入 0 字")
count_label.pack()
agree_label = tk.Label(root, text="还没同意", fg="gray")
agree_label.pack()

root.mainloop()

你会看到:输入框每敲一个字,下面"已输入 N 字"实时跳;勾上/取消勾选框,最后一行在"已同意/还没同意"间切换。

变量类型 配对的控件属性 读值 写值
tk.StringVar() textvariable= .get() .set("新文本")
tk.IntVar() / DoubleVar 数值型输入/单选 .get() .set(5)
tk.BooleanVar() 勾选框 variable= .get() .set(True)

记忆口诀:特殊变量两头翘——textvariable 连输入,variable 连勾选;一头动,另一头自动知道。

实时联动的正规写法:trace

上面的例子靠 <KeyRelease> 事件凑效,粘贴输入就不灵了。Tkinter 变量自带变化回调才是正路:

text_var.trace_add("write", lambda *_: update_count())

变量只要被写入(不管来自键盘、粘贴还是代码 .set()),回调必跑。*_ 表示"来几个参数我都接住但不用"。

after:给界面装个闹钟

import tkinter as tk

root = tk.Tk()
clock = tk.Label(root, text="", font=("", 24))
clock.pack(pady=30)

def tick():
    import time
    clock.config(text=time.strftime("%H:%M:%S"))
    root.after(1000, tick)        # 1000 毫秒后再叫自己一次

tick()                              # 启动第一次
root.mainloop()

你会看到:一个 24 小时制秒级时钟,永远在走。

after(毫秒, 函数) 是"N 毫秒后让事件循环跑一次这个函数"。它不阻塞界面——这正是它存在的意义:在 mainloop 里你永远不要用 time.sleep(),整个界面会冻住 1 秒不响应;要延时,一律 after。

记忆口诀:bind 认事件、变量认双向、trace 认变化、after 认闹钟;sleep 一响,界面冻僵。

动手改

  1. 给打招呼机加上:输入框按回车 = 点按钮(提示:entry.bind("<Return>", lambda e: greet()))
  2. 把字数统计里的 <KeyRelease> 换成 trace_add,然后从记事本粘贴一段长文本进输入框——验证两种写法的差别
  3. 用 after 做一个"番茄钟":点按钮开始 25:00 倒计时,倒数到 0 标签变红(提示:after(1000, ...) + IntVar 存秒数)

下一章:菜单、对话框与多窗口 🐸

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。