📚 目录 第 3 章 点击交互:Button 与方法即回调(第一个"活"的程序)

第 3 章 点击交互:Button 与方法即回调(第一个"活"的程序)

本书红线(贯穿全教程,回顾):
1. 每个界面都用 class App: 封装,__init__ 里建控件,方法即事件回调。
2. 所有控件示例一律写在类里,不用脱离类的扁平脚本。
3. 代码块带 if __name__ == "__main__": 守卫,可独立运行。
4. 星级:★ 了解 · ★★ 理解 · ★★★ 掌握 · ★★★★ 重点必会。

主线项目:图形界面通讯录管理器,随章节每章新增一个控件慢慢长大。

上节回顾(第 2 章):你认识了第一个控件 Label,学会了在类里创建它、设置字体颜色与边框、用 config() 在运行时改它的文字,并记住了"界面上的每个控件都是 self 的一个属性"。本章认识第二个控件——Button(按钮),它是第一个"能交互"的控件。

本章目标:认识第二个控件——Button(按钮)。它是第一个"能交互"的控件:你点它,它就干活。本章重点是全书的 OOP 核心范式——"方法即回调":把"按钮被点击"这件事,交给类里的一个方法来处理。


3.1 是什么 / 适用场景 ★

是什么:Button 是按钮控件。它在窗口上显示一个可点击的区域(通常带文字或图标),用户一点击,就触发一个动作。

适用场景:

和 Label 的区别:Label 只能"看",Button 能"点"——点一下会去调用你指定的函数(叫"回调函数")。


3.2 构造选项(对照官方手册)★★

下面是 Button 最常用的选项(来自官方 tkinter.Button 文档):

选项作用常用取值重要度
text按钮上显示的文字"添加"★★★★
command点击时调用的函数(回调)self.on_click(方法名,不加括号)★★★★
image按钮上的图片(需 PhotoImage)tk.PhotoImage(...)★
compound图文混排时图片相对文字位置"left"/"top" 等★
state按钮状态"normal"(可用,默认)/"disabled"(禁用灰掉)★★★
width / height按钮宽/高(字符数)整数★★
fg / bg文字色 / 背景色颜色字符串★★
font字体字号元组★★
relief边框样式"raised"(默认)/"flat" 等★★
default是否为默认按钮(视觉提示)"active"/"normal"★
📌 本章最关键的选项就是 command——它决定了"点这个按钮会发生什么"。下面 3.5 节会把它讲透。

3.3 常用方法速查 ★★

方法作用示例
config() / configure()运行时改选项(如改文字、禁用)btn.config(state="disabled")
invoke()编程方式"模拟点击"(不真用鼠标点,也触发 command)btn.invoke()
flash()让按钮闪一下(视觉反馈)btn.flash()
invoke() 很实用:写自动化测试、或想"按回车等于点按钮"时(后续章节事件绑定会用到),可以直接 btn.invoke() 触发回调,而不用真去点。

3.4 详细例子(循序渐进)★★★

例 3.1 点击按钮,改变 Label 文字(第一个"活"的程序)★★★★

这是本书第一个有交互的程序:点按钮,窗口里的文字就变。

# 第3章 例3.1:点击按钮,改变 Label 显示的文字
import tkinter as tk


class App:
    def __init__(self, root):
        self.root = root
        self.root.title("通讯录管理器")

        # ① 先放一个 Label 作为"被改变"的对象,存进 self
        self.msg = tk.Label(self.root, text="还没点呢", font=("微软雅黑", 14))
        self.msg.pack(padx=20, pady=10)

        # ② 创建一个 Button,command 指定"点击后调用的方法"
        #    注意:command=self.on_click 是"方法名",不要加括号!
        self.btn = tk.Button(
            self.root,
            text="点我",
            command=self.on_click,        # 点击 → 调用 self.on_click 方法
        )
        self.btn.pack(padx=20, pady=10)

    # ③ 被调用的方法(回调函数):第一个参数永远是 self
    def on_click(self):
        self.msg.config(text="你点了我!")   # 通过 self.msg 改那个 Label 的文字


if __name__ == "__main__":
    root = tk.Tk()
    App(root)
    root.mainloop()

逐行说明(重点看 ②③):

例 3.2 计数器(+1 并显示)★★★

把"点击"和"数据"结合起来——每点一次,计数 +1 并刷新显示。这是 GUI 程序最典型的模式(点一下 → 改数据 → 刷新界面):

# 第3章 例3.2:点击按钮,计数器加 1 并显示
import tkinter as tk


class App:
    def __init__(self, root):
        self.root = root
        self.root.title("通讯录管理器")
        self.count = 0                              # ① 用一个实例属性保存"计数"状态

        self.label = tk.Label(self.root, text=f"已点击 {self.count} 次",
                              font=("微软雅黑", 14))
        self.label.pack(padx=20, pady=10)

        self.btn = tk.Button(self.root, text="加 1", command=self.add_one)
        self.btn.pack(padx=20, pady=10)

    def add_one(self):                             # ② 点击时调用
        self.count += 1                            # ③ 改数据:计数 +1
        self.label.config(text=f"已点击 {self.count} 次")   # ④ 刷界面:把新数据显示出来


if __name__ == "__main__":
    root = tk.Tk()
    App(root)
    root.mainloop()

逐行说明:

例 3.3 禁用按钮(state)★★

# 第3章 例3.3:点击后把按钮禁用,避免重复点击
import tkinter as tk


class App:
    def __init__(self, root):
        self.root = root
        self.root.title("通讯录管理器")
        self.btn = tk.Button(self.root, text="提交", command=self.submit)
        self.btn.pack(padx=20, pady=20)

    def submit(self):
        self.btn.config(state="disabled", text="已提交")   # 禁用并改文字


if __name__ == "__main__":
    root = tk.Tk()
    App(root)
    root.mainloop()

说明:state="disabled" 让按钮变灰、不可点;state="normal" 恢复可用。常用于"提交后防止重复提交"。


3.5 OOP 落点(本章重点):方法即回调 ★★★★

这是 tkinter + 面向对象结合处最重要的一句话,请记住:

按钮的 command 指向类里的一个方法;用户点击 → tkinter 自动调用这个方法。这个方法通过 self 拿起界面上的其它控件,去改它们、去读数据。

为什么这叫"回调"(callback):你写 command=self.on_click 时,并没有马上调用 on_click——你只是"登记"说"将来有人点按钮,请你帮我调用它"。点击发生时,tkinter 反过来"回调"你的方法。这种"你登记、它回头调用"的模式就叫回调。

对比:扁平函数版 vs 类版(让你看清类的优势)

扁平写法(不用类):

# 不推荐:控件和函数散落在全局,函数要自己找全局变量 label
import tkinter as tk
root = tk.Tk()
label = tk.Label(root, text="hi")
label.pack()
def on_click():
    label.config(text="clicked")     # 得靠全局变量 label,多个按钮时易乱
btn = tk.Button(root, text="点", command=on_click)
btn.pack()
root.mainloop()

类写法(本书推荐):

# 推荐:所有控件挂 self,方法通过 self 互相关联,结构清晰
import tkinter as tk

class App:
    def __init__(self, root):
        self.root = root
        self.label = tk.Label(root, text="hi")
        self.label.pack()
        self.btn = tk.Button(root, text="点", command=self.on_click)
        self.btn.pack()
    def on_click(self):
        self.label.config(text="clicked")   # 通过 self.label 直接拿,不用全局变量
root = tk.Tk(); App(root); root.mainloop()

类的优势一目了然:控件都收在 self 下,方法之间用 self.xxx 沟通,不用到处传全局变量。当界面有几十个控件、十几个方法时,这种组织方式能救命。


3.6 注意事项 / 易错清单 ★★★


3.7 本章练习

  1. 写一个程序:一个 Label 显示"分数为 0",一个按钮"+5",每点一次分数加 5 并显示。
  2. 在练习 1 基础上,当分数达到 20 时,把按钮 state 设为 "disabled",文字改为"已满分"。
  3. 写一个"开关灯"程序:一个 Label 初始显示"灯:关",按钮文字"开灯";点一下 Label 变"灯:开"、按钮变"关灯",再点变回——即按钮在两种状态间切换(提示:用一个 self.is_on 布尔变量记录状态,在回调里取反)。
  4. 思考题:例 3.1 里如果把 command=self.on_click 改成 command=self.on_click(),运行时会发生什么?(提示:启动即报错或立即执行一次,且点击无效——印证 3.6 节的 🔴 易错点。)
本章小结:你现在已经能让程序"响应点击"了——这是 GUI 程序的灵魂。下一章我们学怎么把 Label、Button 这些控件排得整整齐齐(布局管理器),目前它们只是简单地上下叠着。