📚 目录 第 8 章 勾选与单选:Checkbutton / Radiobutton(让用户做"多选"和"单选")

第 8 章 勾选与单选:Checkbutton / Radiobutton(让用户做"多选"和"单选")

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

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

上节回顾(第 7 章):你学会了用 ttk 主题控件和 Style 给整个界面"换肤",让按钮、标签变得更好看、风格统一。本章我们给通讯录加上"勾选"和"单选"能力——也就是 Checkbutton(复选框) 和 Radiobutton(单选钮),让用户做"多选"和"单选"。

本章目标:认识两个与"选择状态"相关的控件——Checkbutton(复选框,可多选/开关) 和 Radiobutton(单选钮,一组里只能选一个)。重点是学会用"变量类"(BooleanVar / StringVar / IntVar)把"界面上的勾选状态"和"程序里的数据"绑起来:勾上就是 True,没勾就是 False;单选里选了哪个,变量就存哪个 value。OOP 落点:这些变量和控件都收在 self 下,点按钮时通过 var.get() 读取用户选择。


8.1 是什么 / 适用场景 ★

是什么:

适用场景:

控件典型场景通讯录里的用法
Checkbutton是否收藏、是否启用、多个可叠加的偏好"是否收藏此联系人"
Radiobutton性别、会员等级、单选分类等互斥选项"性别:男 / 女"

不适用:选项特别多(十几个)时应改用第 9 章的 Combobox(下拉);选项可以连续变化用第 9 章的 Scale(滑块)。


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

Checkbutton 与 Radiobutton 都来自 ttk,常用选项如下(官方文档:tkinter.ttk.Checkbutton / tkinter.ttk.Radiobutton):

选项作用常用取值重要度
text控件旁显示的文字"男" / "是否收藏"★★★★
variable绑定的变量(状态存这里)tk.BooleanVar() / tk.StringVar() / tk.IntVar()★★★★
onvalue选中时变量变成的值(Checkbutton)True(默认 1)★★★
offvalue未选中时变量变成的值(Checkbutton)False(默认 0)★★★
value选中时变量变成的值(Radiobutton)"男" / 1 等,每个钮不同★★★★
command状态变化时调用的回调self.on_change(方法名)★★★
state可用 / 禁用"normal" / "disabled"★★
📌 两个控件最大的区别在 variable 的"共用":
- Checkbutton 每个框可以各自绑一个变量(或同一个变量代表一个开关)。
- Radiobutton 一组必须共用同一个 variable,靠不同的 value 来区分"选的是哪一个"。忘了共用同一个变量,单选就失灵了(见 8.6 易错清单)。

8.3 常用方法 / 变量速查 ★★

Checkbutton / Radiobutton 本身方法不多,真正的"取值/设值"都在绑定的变量类上:

对象 / 方法作用示例
var.get()读取变量当前值self.gender_var.get() → "男"
var.set(值)设置变量值(界面同步变化)self.gender_var.set("女")
tk.BooleanVar()布尔变量(True/False)self.fav = tk.BooleanVar()
tk.StringVar()字符串变量self.gender = tk.StringVar()
tk.IntVar()整数变量self.level = tk.IntVar()
widget.invoke()程序模拟"点一下"(切换状态并触发 command)cb.invoke()
关键认知:"用户看到的勾选状态"和"程序里的数据"通过 variable 合二为一。你不用去读控件,直接 var.get() 读数据、var.set() 写数据,界面会自动跟着变。这是 GUI 里"数据驱动界面"思想的雏形。

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

本书红线:示例一律写在 class App 里。下面每个例子都可直接运行(本机会弹出窗口)。

例 8.1 单个 Checkbutton:是否收藏(BooleanVar)★★

最简单的复选框——一个开关,勾上代表"收藏"。

# 第8章 例8.1:用 Checkbutton 实现"是否收藏"开关
import tkinter as tk
from tkinter import ttk


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

        # ① 创建一个布尔变量,专门记录"是否收藏";初值 False(未勾选)
        self.fav_var = tk.BooleanVar(value=False)

        # ② 创建 Checkbutton:variable 绑到 fav_var,勾上=onvalue(True),没勾=offvalue(False)
        self.fav_cb = ttk.Checkbutton(
            self.root,
            text="是否收藏",
            variable=self.fav_var,   # 状态写进 fav_var
            onvalue=True,            # 勾选时变量变 True
            offvalue=False,          # 取消勾选时变量变 False
            command=self.on_toggle,  # 状态变化时调用的方法
        )
        self.fav_cb.pack(padx=20, pady=10)

        # ③ 一个 Label 用于把当前状态显示出来
        self.label = ttk.Label(self.root, text="收藏状态:未收藏")
        self.label.pack(padx=20, pady=10)

    def on_toggle(self):
        # ④ 通过 var.get() 读取勾选状态:返回 True 或 False
        if self.fav_var.get():
            self.label.config(text="收藏状态:已收藏 ★")
        else:
            self.label.config(text="收藏状态:未收藏")


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

逐行说明(重点看 ①②④):

例 8.2 一组 Radiobutton:性别(StringVar 共用变量)★★★

单选钮的关键:一组共用同一个 variable,每个钮 value 不同。

# 第8章 例8.2:用 Radiobutton 实现"性别:男/女"单选
import tkinter as tk
from tkinter import ttk


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

        # ① 一个字符串变量,记录"当前选中的性别";value 用字符串,初值"男"
        self.gender_var = tk.StringVar(value="男")

        # ② 男、女两个单选钮【共用同一个 gender_var】,但 value 不同
        self.rb_male = ttk.Radiobutton(
            self.root,
            text="男",
            variable=self.gender_var,   # 和下面"女"共用同一个变量
            value="男",                 # 选中"男"时,gender_var 变成 "男"
            command=self.show_gender,
        )
        self.rb_female = ttk.Radiobutton(
            self.root,
            text="女",
            variable=self.gender_var,   # 必须是同一个 variable 才能互斥
            value="女",                 # 选中"女"时,gender_var 变成 "女"
            command=self.show_gender,
        )
        self.rb_male.pack(padx=20, pady=5)
        self.rb_female.pack(padx=20, pady=5)

        self.label = ttk.Label(self.root, text="性别:男")
        self.label.pack(padx=20, pady=10)

    def show_gender(self):
        # ③ var.get() 取的就是当前被选中钮的 value
        self.label.config(text=f"性别:{self.gender_var.get()}")


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

逐行说明(重点看 ②):

例 8.3 综合:通讯录表单+收藏+性别,按钮汇总显示 ★★★

把前面两个控件放进同一个通讯录添加表单,点"确认"按钮把选择显示在 Label。

# 第8章 例8.3:通讯录表单里同时用 Checkbutton(收藏) + Radiobutton(性别)
import tkinter as tk
from tkinter import ttk


class App:
    def __init__(self, root):
        self.root = root
        self.root.title("通讯录管理器 - 新增联系人")

        # 姓名输入框(用 ttk.Entry,回忆第4章;这里只取它的值用)
        self.name_var = tk.StringVar()
        ttk.Label(self.root, text="姓名:").pack(padx=20, pady=(10, 0))
        self.name_entry = ttk.Entry(self.root, textvariable=self.name_var)
        self.name_entry.pack(padx=20, pady=5)

        # ① Checkbutton:是否收藏(布尔变量)
        self.fav_var = tk.BooleanVar(value=False)
        self.fav_cb = ttk.Checkbutton(
            self.root, text="是否收藏",
            variable=self.fav_var, onvalue=True, offvalue=False,
        )
        self.fav_cb.pack(padx=20, pady=5, anchor="w")

        # ② Radiobutton:性别(字符串变量,男/女共用)
        self.gender_var = tk.StringVar(value="男")
        ttk.Radiobutton(self.root, text="男",
                        variable=self.gender_var, value="男").pack(padx=20, pady=2, anchor="w")
        ttk.Radiobutton(self.root, text="女",
                        variable=self.gender_var, value="女").pack(padx=20, pady=2, anchor="w")

        # ③ 确认按钮:点击后汇总显示
        self.btn = ttk.Button(self.root, text="确认", command=self.confirm)
        self.btn.pack(padx=20, pady=10)

        # ④ 结果显示 Label
        self.result = ttk.Label(self.root, text="(等待提交)")
        self.result.pack(padx=20, pady=10)

    def confirm(self):
        name = self.name_var.get() or "(未填姓名)"
        # ⑤ 直接读两个变量,拼成一句话
        fav = "已收藏" if self.fav_var.get() else "未收藏"
        gender = self.gender_var.get()
        self.result.config(text=f"姓名:{name} 性别:{gender} {fav}")


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

逐行说明:


8.5 OOP 落点 ★★

本章把"界面状态"正式纳入了对象的属性体系:


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


8.7 本章练习

  1. 写一个程序:一个 Checkbutton 文字"显示密码",勾上时把一个 Label 从""变成"123456",取消勾选变回""(提示:var.get() 判断真假)。
  2. 写一个"学历"单选:三个 Radiobutton(本科/硕士/博士)共用一个 IntVar,value 分别用 1/2/3,点按钮把选中的数字显示在 Label。
  3. 综合练习:给通讯录表单加"是否收藏"勾选(Checkbutton)和"男/女"单选(Radiobutton),点"显示选择"按钮把两项选择显示在 Label。
  4. 思考题:如果把例 8.2 里两个 Radiobutton 的 variable 写成两个不同的 StringVar(各绑一个),运行后点击会怎样?(提示:两个钮各自独立,可以同时"都选中",不再是单选。)
下节预告:第 9 章我们学更省空间的"选择/调数"控件——Combobox(下拉框选分类)、Scale(滑块调年龄)、Spinbox(上下箭头调数字),选项一多就用它们替代满屏的单选钮。