3、PyQt5基础控件(下):QComboBox下拉框、QCheckBox复选框、QRadioButton单选框、QListWidget列表控件、QTableWidget表格控件

好,咱们接着聊。上一节我们把按钮、标签、输入框这些基础控件过了一遍,今天要讲的这几个,说实话,在真正的项目里出现频率极高。尤其是表格和列表,几乎每个带数据展示的桌面应用都离不开它们。

我个人习惯把今天的内容分成两类:一类是“选择型”控件,比如下拉框、复选框、单选框;另一类是“展示型”控件,比如列表和表格。这样分,你学起来思路会更清晰。

PyQt5 基础控件(下)知识体系 选择型控件 QComboBox 下拉框 QCheckBox 复选框 QRadioButton 单选框 信号:currentIndexChanged / toggled 常用方法:addItem / setChecked / isChecked 展示型控件 QListWidget 列表控件 QTableWidget 表格控件 信号:itemClicked / cellChanged 常用方法:addItem / setItem / setRowCount 样式:setAlternatingRowColors 组合使用

3.1 QComboBox 下拉框:省空间的利器

下拉框这东西,说白了就是一个节省界面空间的“选择器”。你想想看,如果让用户从 50 个城市里选一个,用一排按钮肯定不现实,用列表又占地方。下拉框就完美解决了这个问题——平时只显示一行,点击才展开。

我在项目中遇到过最典型的场景是“设置界面”。用户选择语言、主题、分辨率,几乎都用下拉框。嗯,这里要注意:下拉框的选项不要太多,超过 20 项建议加搜索功能,否则用户体验会下降。

from PyQt5.QtWidgets import QComboBox

combo = QComboBox()
combo.addItem("北京")
combo.addItem("上海")
combo.addItem("广州")
combo.addItem("深圳")
combo.addItem("杭州")

# 设置默认选中项
combo.setCurrentIndex(0)

# 获取当前选中文本
current_text = combo.currentText()
print(f"当前选择:{current_text}")

# 信号连接
combo.currentIndexChanged.connect(lambda idx: print(f"选中索引:{idx}"))
小技巧:如果你想让下拉框可编辑(允许用户输入自定义值),设置 combo.setEditable(True) 即可。我经常在“自定义筛选”功能里用这个。

3.2 QCheckBox 复选框:多选场景的标配

复选框,就是那个小方框,打勾表示选中。它最常用的场景是“多选”,比如用户选择兴趣爱好、设置权限、勾选协议条款等。

我个人习惯把复选框的状态分为三种:选中、未选中、部分选中。部分选中这个状态很多人会忽略,但在树形结构或级联选择中非常有用。比如你有一个“全选”复选框,下面有多个子项,当子项部分选中时,父级复选框就应该显示为部分选中状态。

from PyQt5.QtWidgets import QCheckBox

cb = QCheckBox("我同意用户协议")
cb.setChecked(False)  # 默认未选中

# 获取状态
if cb.isChecked():
    print("已勾选")
else:
    print("未勾选")

# 信号连接
cb.stateChanged.connect(lambda state: print(f"状态变化:{state}"))
# state: 0=未选中, 1=部分选中, 2=完全选中
避坑指南:我曾经在做一个配置工具时,忘了处理复选框的“部分选中”状态,结果用户勾选了部分子项后,父级复选框显示为未选中,导致用户以为没生效。后来我加了三态支持,问题才解决。记住:setTristate(True) 可以开启三态模式。

3.3 QRadioButton 单选框:互斥选择的好帮手

单选框和复选框最大的区别是什么?单选框是“多选一”,复选框是“多选多”。单选框通常成组出现,同一组内只能选中一个。

你想想看,性别选择、支付方式、排序方式……这些场景用单框最合适。但要注意:单选框必须放在 QButtonGroup 里才能实现互斥,否则它们各自独立,互不影响。

from PyQt5.QtWidgets import QRadioButton, QButtonGroup

rb_male = QRadioButton("男")
rb_female = QRadioButton("女")
rb_other = QRadioButton("其他")

# 创建按钮组,实现互斥
gender_group = QButtonGroup()
gender_group.addButton(rb_male, 1)    # id=1
gender_group.addButton(rb_female, 2)  # id=2
gender_group.addButton(rb_other, 3)   # id=3

# 默认选中
rb_male.setChecked(True)

# 获取选中按钮的 id
selected_id = gender_group.checkedId()
print(f"选中按钮 ID:{selected_id}")

# 信号连接
gender_group.buttonClicked[int].connect(
    lambda btn_id: print(f"选中了 ID 为 {btn_id} 的按钮")
)
重点:QButtonGroup 本身不负责界面显示,它只管理逻辑上的互斥关系。你仍然需要把每个 QRadioButton 添加到布局中。

3.4 QListWidget 列表控件:轻量级数据展示

列表控件,说白了就是一个垂直排列的条目集合。它比下拉框更直观,因为所有选项都直接可见。适合选项数量不多(比如 5-15 项)且需要用户快速浏览的场景。

我记得有一次做文件管理器,左侧的目录树用 QTreeWidget,右侧的文件列表就用 QListWidget。用户点击左侧目录,右侧就刷新文件列表,体验非常好。

from PyQt5.QtWidgets import QListWidget, QListWidgetItem

list_widget = QListWidget()
list_widget.addItem("文档")
list_widget.addItem("图片")
list_widget.addItem("视频")
list_widget.addItem("音乐")

# 添加带图标的条目
item = QListWidgetItem("下载")
item.setIcon(QIcon("download.png"))  # 需要导入 QIcon
list_widget.addItem(item)

# 设置当前选中项
list_widget.setCurrentRow(0)

# 信号连接
list_widget.currentItemChanged.connect(
    lambda current, previous: print(f"当前选中:{current.text()}")
)

# 获取所有条目
for i in range(list_widget.count()):
    item = list_widget.item(i)
    print(f"第 {i} 项:{item.text()}")
小技巧:如果你想让列表支持多选,设置 list_widget.setSelectionMode(QAbstractItemView.MultiSelection)。我经常在批量操作功能里用这个。

3.5 QTableWidget 表格控件:结构化数据的王者

表格控件,这是今天最重量级的一个。它用来展示二维数据,比如学生成绩表、订单列表、库存清单等。说白了,就是 Excel 表格的简化版。

我在项目中用过很多次 QTableWidget,最头疼的是性能问题。如果你要展示几千行数据,直接用 QTableWidget 会卡死。这时候需要用 QTableView + 自定义模型。不过对于中小规模数据(几百行以内),QTableWidget 完全够用。

from PyQt5.QtWidgets import QTableWidget, QTableWidgetItem, QHeaderView

table = QTableWidget()
table.setRowCount(5)      # 5 行
table.setColumnCount(3)   # 3 列

# 设置表头
table.setHorizontalHeaderLabels(["姓名", "年龄", "城市"])

# 填充数据
data = [
    ["张三", "25", "北京"],
    ["李四", "30", "上海"],
    ["王五", "28", "广州"],
    ["赵六", "22", "深圳"],
    ["孙七", "35", "杭州"]
]

for row, row_data in enumerate(data):
    for col, value in enumerate(row_data):
        item = QTableWidgetItem(value)
        table.setItem(row, col, item)

# 自动调整列宽
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)

# 设置交替行颜色(提升可读性)
table.setAlternatingRowColors(True)

# 信号连接
table.cellClicked.connect(lambda row, col: print(f"点击了第 {row} 行第 {col} 列"))

# 获取选中行的数据
selected_row = table.currentRow()
if selected_row >= 0:
    name = table.item(selected_row, 0).text()
    age = table.item(selected_row, 1).text()
    print(f"选中:{name}, {age} 岁")
避坑指南:我曾经在做一个订单管理系统时,直接往 QTableWidget 里塞了 5000 条数据,结果界面卡了 10 秒才出来。后来改用 QTableView + QStandardItemModel,并且分页加载,才解决了问题。记住:QTableWidget 适合小数据量,大数据量请用 Model/View 架构。

3.6 综合示例:一个简单的设置界面

好了,理论讲完了,咱们来点实战。下面这个例子,把今天学的所有控件组合起来,做一个简单的“用户设置”界面。

from PyQt5.QtWidgets import (
    QWidget, QVBoxLayout, QHBoxLayout, QLabel,
    QComboBox, QCheckBox, QRadioButton, QButtonGroup,
    QListWidget, QTableWidget, QTableWidgetItem, QPushButton
)

class SettingsWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()

    def init_ui(self):
        layout = QVBoxLayout()

        # 1. 下拉框:选择语言
        lang_layout = QHBoxLayout()
        lang_layout.addWidget(QLabel("语言:"))
        self.combo_lang = QComboBox()
        self.combo_lang.addItems(["中文", "English", "日本語", "한국어"])
        lang_layout.addWidget(self.combo_lang)
        layout.addLayout(lang_layout)

        # 2. 复选框:通知设置
        self.cb_email = QCheckBox("邮件通知")
        self.cb_sms = QCheckBox("短信通知")
        self.cb_push = QCheckBox("推送通知")
        layout.addWidget(self.cb_email)
        layout.addWidget(self.cb_sms)
        layout.addWidget(self.cb_push)

        # 3. 单选框:主题选择
        theme_layout = QHBoxLayout()
        theme_layout.addWidget(QLabel("主题:"))
        self.rb_light = QRadioButton("浅色")
        self.rb_dark = QRadioButton("深色")
        self.rb_auto = QRadioButton("自动")
        theme_group = QButtonGroup()
        theme_group.addButton(self.rb_light, 1)
        theme_group.addButton(self.rb_dark, 2)
        theme_group.addButton(self.rb_auto, 3)
        self.rb_light.setChecked(True)
        theme_layout.addWidget(self.rb_light)
        theme_layout.addWidget(self.rb_dark)
        theme_layout.addWidget(self.rb_auto)
        layout.addLayout(theme_layout)

        # 4. 列表:快捷操作
        self.list_shortcuts = QListWidget()
        self.list_shortcuts.addItems(["新建文件", "打开文件", "保存文件", "导出数据"])
        layout.addWidget(QLabel("快捷操作:"))
        layout.addWidget(self.list_shortcuts)

        # 5. 表格:最近文件
        self.table_recent = QTableWidget()
        self.table_recent.setRowCount(3)
        self.table_recent.setColumnCount(2)
        self.table_recent.setHorizontalHeaderLabels(["文件名", "修改时间"])
        recent_data = [
            ["report.docx", "2024-01-15"],
            ["data.xlsx", "2024-01-14"],
            ["presentation.pptx", "2024-01-13"]
        ]
        for row, row_data in enumerate(recent_data):
            for col, value in enumerate(row_data):
                self.table_recent.setItem(row, col, QTableWidgetItem(value))
        layout.addWidget(QLabel("最近文件:"))
        layout.addWidget(self.table_recent)

        # 保存按钮
        btn_save = QPushButton("保存设置")
        btn_save.clicked.connect(self.save_settings)
        layout.addWidget(btn_save)

        self.setLayout(layout)

    def save_settings(self):
        lang = self.combo_lang.currentText()
        email_notify = self.cb_email.isChecked()
        sms_notify = self.cb_sms.isChecked()
        push_notify = self.cb_push.isChecked()
        print(f"语言:{lang}")
        print(f"通知:邮件={email_notify}, 短信={sms_notify}, 推送={push_notify}")
        print("设置已保存!")
总结一下:今天讲的五个控件,各有各的适用场景。下拉框省空间,复选框多选,单选框互斥,列表直观,表格结构化。实际项目中,它们经常组合使用。你只要记住每个控件的核心用途和常用方法,写界面的时候自然就知道该用哪个了。

嗯,今天就到这里。代码不多,但每个控件都值得你亲手敲一遍。尤其是表格,多练练增删改查,后面做项目会非常顺手。