📚 目录 第 10 章 列表展示:Listbox 与 Scrollbar(显示"一列"多条数据并滚动)

第 10 章 列表展示:Listbox 与 Scrollbar(显示"一列"多条数据并滚动)

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

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

上节回顾(第 9 章):你学会了用更省空间的控件做选择与调数——Combobox 下拉选分类(记得 state="readonly")、Scale 滑块选年龄(记得 from_ 带下划线)、Spinbox 上下箭头调数。本章给通讯录加上"列表"能力——Listbox(列表框) 配合 Scrollbar(滚动条),一次性展示"一列"多条联系人姓名,支持选中与滚动。

本章目标:认识 Listbox(显示一列多行文本) 和 Scrollbar(滚动条),并学会两者双向绑定让长列表能滚动。重点是 insert/delete/get/curselection,以及用 listvariable 绑定列表、用 refresh(self) 方法从数据重绘列表——建立"数据 → 界面"的刷新思维。OOP 落点:self.listbox 作为实例属性,把"数据层"和"显示层"分开。


10.1 是什么 / 适用场景 ★

是什么:

适用场景:

📌 Listbox / Scrollbar 没有 ttk 版本,直接用 tk.Listbox / tk.Scrollbar(这是官方定死的,不是我们不用 ttk)。

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

控件选项作用常用取值重要度
Listboxheight可见行数(不滚动时能看几行)8★★★
Listboxselectmode选择模式"single"(单选) / "browse" / "multiple" / "extended"(框选)★★★
Listboxlistvariable绑定一个 StringVar(值为列表)tk.StringVar()★★★
Listboxyscrollcommand垂直滚动回调(绑滚动条)sb.set★★★★
Scrollbarorient方向"vertical" / "horizontal"★★
Scrollbarcommand滚动时通知列表(绑列表)listbox.yview★★★★
双向绑定公式(记住就行,10.4 会写):listbox.config(yscrollcommand=sb.set) + sb.config(command=listbox.yview)。两边各指对方,"谁能滚"和"滚到哪"才联动。

10.3 常用方法速查 ★★

方法作用示例
insert(位置, 文本)加一项;tk.END 表示末尾lb.insert(tk.END, "张三")
delete(起, 止)删除项;delete(0, tk.END) 清空全部lb.delete(0, tk.END)
get(起, 止)取文本;get(0, tk.END) 取全部lb.get(0, tk.END)
curselection()取选中项下标(元组,可能为空)idx = lb.curselection()
selection_set(下标)程序选中某项lb.selection_set(0)
yview / xview滚动视图(给滚动条 command 用)lb.yview
⚠️ curselection() 返回的是下标元组(如 (2,)),且可能为空——用户没选时返回空元组。用之前一定要先判断 if idx: 或 if idx:,否则下标越界报错。

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

每个例子都写在 class App 里,可直接运行(本机会弹出窗口)。

例 10.1 最简单的 Listbox:加几个联系人 ★★

把几个姓名插进列表,并支持鼠标点击选中(点按钮显示选中项)。

# 第10章 例10.1:Listbox 添加并选中显示联系人
import tkinter as tk
from tkinter import ttk


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

        # ① 创建 Listbox,height=8 表示不滚动时显示 8 行
        self.listbox = tk.Listbox(self.root, height=8, selectmode="single")
        self.listbox.pack(padx=20, pady=10, fill="both", expand=True)

        # ② 用 insert(tk.END, 文本) 往末尾加几项
        for name in ["张三", "李四", "王五", "赵六"]:
            self.listbox.insert(tk.END, name)

        # ③ 按钮:点击后显示当前选中项
        self.btn = ttk.Button(self.root, text="显示选中", command=self.show_sel)
        self.btn.pack(padx=20, pady=10)

        self.label = ttk.Label(self.root, text="(请选择)")
        self.label.pack(padx=20, pady=10)

    def show_sel(self):
        # ④ curselection() 返回选中项下标元组,可能为空,必须先判断
        idx = self.listbox.curselection()
        if idx:                                      # 有选中才处理
            name = self.listbox.get(idx[0])          # 按下标取文字
            self.label.config(text=f"选中:{name}")
        else:
            self.label.config(text="还没选任何人")


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

逐行说明:

例 10.2 Listbox + Scrollbar 双向绑定(长列表滚动)★★★

当联系人有几十个、超过框高,就需要滚动条。关键是双向绑定两边都配。

# 第10章 例10.2:Listbox 与 Scrollbar 双向绑定实现滚动
import tkinter as tk
from tkinter import ttk


class App:
    def __init__(self, root):
        self.root = root
        self.root.title("通讯录管理器 - 可滚动列表")

        # 用一个 Frame 把 Listbox 和滚动条并排摆放
        self.frame = ttk.Frame(self.root)
        self.frame.pack(padx=20, pady=10, fill="both", expand=True)

        # ① 滚动条(竖向)
        self.sb = tk.Scrollbar(self.frame, orient="vertical")
        self.sb.pack(side="right", fill="y")

        # ② Listbox:yscrollcommand 指向滚动条的 set 方法
        self.listbox = tk.Listbox(
            self.frame, height=6,
            yscrollcommand=self.sb.set,    # 列表内容变化 → 通知滚动条更新
        )
        self.listbox.pack(side="left", fill="both", expand=True)

        # ③ 滚动条 command 指向列表的 yview → 双向绑定完成
        self.sb.config(command=self.listbox.yview)

        # ④ 塞入很多项,超过 6 行就能滚动
        for i in range(1, 31):
            self.listbox.insert(tk.END, f"联系人_{i:02d}")


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

逐行说明(双向绑定是核心):

例 10.3 refresh(self) 重绘 + 删除选中项(数据→界面思维)★★★

本章重点:写一个 refresh(self) 方法,从"数据列表"重新把 Listbox 画一遍;点"删除"删掉选中项并重绘。这建立了"改数据 → 调 refresh → 界面更新"的刷新思维。

# 第10章 例10.3:用数据列表 + refresh() 方法驱动 Listbox,并删除选中项
import tkinter as tk
from tkinter import ttk


class App:
    def __init__(self, root):
        self.root = root
        self.root.title("通讯录管理器 - 数据驱动列表")

        # ① 数据层:一个普通 Python 列表保存联系人(界面"背后"的数据)
        self.contacts = ["张三", "李四", "王五", "赵六", "孙七"]

        self.listbox = tk.Listbox(self.root, height=8, selectmode="single")
        self.listbox.pack(padx=20, pady=10, fill="both", expand=True)

        # ② 删除按钮
        self.del_btn = ttk.Button(self.root, text="删除选中", command=self.delete_sel)
        self.del_btn.pack(padx=20, pady=5)

        self.label = ttk.Label(self.root, text="共 0 位联系人")
        self.label.pack(padx=20, pady=10)

        # ③ 初始先按数据重绘一次
        self.refresh()

    def refresh(self):
        # ④ 清空列表,再从 self.contacts 逐项插入 —— "数据 → 界面" 的标准写法
        self.listbox.delete(0, tk.END)
        for name in self.contacts:
            self.listbox.insert(tk.END, name)
        self.label.config(text=f"共 {len(self.contacts)} 位联系人")

    def delete_sel(self):
        idx = self.listbox.curselection()
        if not idx:                              # 没选中就提示
            self.label.config(text="请先选中一位再删除")
            return
        # ⑤ 先按界面下标删数据,再 refresh 重绘
        pos = idx[0]
        self.contacts.pop(pos)                  # 从数据层删
        self.refresh()                          # 重绘界面


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

逐行说明:


10.5 OOP 落点 ★★


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


10.7 本章练习

  1. 写一个程序:用 Listbox 显示 5 个水果名,点按钮把"当前选中项"显示在 Label(提示:先 if idx: 判断再 get(idx[0]))。
  2. 写一个程序:往 Listbox 插入 20 行文本,配 Scrollbar 实现滚动(提示:双向绑定两行配置都要写)。
  3. 综合练习:用 self.contacts 数据列表保存几个联系人姓名,点"删除"按钮删掉 Listbox 中选中的那一项并重绘;用 refresh() 方法实现重绘。
  4. 思考题:例 10.3 如果把 delete_sel 里改成直接 self.listbox.delete(idx[0]) 而不改 self.contacts、不调 refresh(),会发生什么?(提示:界面删了但数据没删,下次 refresh() 又被加回来——界面和数据不一致。务必"先改数据,再重绘"。)
下节预告:第 11 章我们学 Treeview——通讯录的核心展示控件,用"表格"显示多列结构化数据(姓名/电话/分类),并支持点行回填表单。它是前几章所有控件能力的集大成者。