Qt按钮类05 QCheckBox——大丙+gemini
Qt按钮类05 QCheckBox——大丙+gemini
https://www.bilibili.com/video/BV1ff4y1C7CK
QCheckBox 是 Qt 框架中用于实现“多选多”逻辑的复选框按钮控件。与 QRadioButton 的排他性互斥机制(“多选一”)不同,QCheckBox 允许在同一个容器作用域内同时选中多个选项,且支持通过二次点击反转或取消选中状态。
在底层设计上,它同样继承自 QAbstractButton。在实际工程中,它常用于多项复合配置开关,或以联动的方式构建具备三态(Tristate)控制逻辑的树状层次结构。
1. 控件继承体系与多态接口
┌───────────────┐
│ QWidget │
└───────┬───────┘
│
┌───────┴───────┐
│QAbstractButton│
└───────┬───────┘
│
┌───────┴───────┐
│ QCheckBox │
└───────────────┘
1.1 显式构造函数
在纯代码编写阶段,QCheckBox 提供了两个标准的显式构造接口:
// 1. 标题构造:初始化复选框文本并挂载物理父对象
QCheckBox(const QString &text, QWidget *parent = nullptr);
// 2. 空白构造:仅指定父对象,属性后续通过属性函数异步配置
explicit QCheckBox(QWidget *parent = nullptr);
1.2 三态(Tristate)管理核心 API
默认情况下,QCheckBox 仅在两种状态间发生状态机翻转。若要使其支持半选中状态(常用于树状结构的父节点),必须显式使能三态属性:
// 1. 属性使能:配置当前复选框是否开启三态机制(默认值为 false)
void setTristate(bool y = true);
// 2. 属性读取:查询当前复选框是否为三态复选框
bool isTristate() const;
1.3 状态读写多态接口(基于 Qt::CheckState)
对于普通双稳态复选框,可直接调用基类的 setChecked(bool) 与 isChecked()。而一旦使能了三态机制,则必须采用更为精确的枚举读写接口:
// 1. 状态写入:显式将复选框驱动至特定的三态位置
void setCheckState(Qt::CheckState state);
// 2. 状态读取:获取当前复选框的确切逻辑状态
Qt::CheckState checkState() const;
Qt::CheckState 状态枚举模型
| 枚举常量定义 | 物理数值 | 界面视觉表现与工程含义 |
|---|---|---|
Qt::Unchecked |
0 |
未选中状态:物理控件框内完全留白,表示选项未生效。 |
Qt::PartiallyChecked |
1 |
半选中状态:物理控件框内填充方块(或横线),表示下属子节点处于“部分选中”的分流状态。 |
Qt::Checked |
2 |
选中状态:物理控件框内显示勾选标记,表示选项完全生效。 |
1.4 状态机级联信号
// 核心通知信号:当控件的逻辑 CheckState 发生任何洗牌或变更时,该信号即被底层发射
[signal] void QCheckBox::stateChanged(int state);
注意:由于历史架构原因,该信号发射时附带的入参为
int类型。在槽函数内进行控制流研判时,应当将其显式或隐式与Qt::CheckState枚举常量进行比对。
2. 核心深度机理:多路复选树的联动与控制流拓扑
在纯代码构建具有层级关系的复选树(如“父节点 - 子节点群”)时,界面图纸的删除要求我们必须在底层构建清晰的逻辑拓扑。树状级联联动由两个互为逆向的控制流回路构成:
2.1 正向驱动流:父控子(Top-Down Control Loop)
当用户物理触发根节点(父复选框)时,状态机产生确定性的翻转(要么全选,要么全不选)。此时,系统应当拦截该物理点击事件,强行将下属的所有子节点的状态级联修改为与父节点相同的状态。这一过程称为正向状态广播。
2.2 逆向推导流:子反馈父(Bottom-Up Evaluation Loop)
当外围用户逐个点击底层的子节点时,任何一个子节点的状态变更都会向上发射信号。父节点通过实时收集并评估所有活跃子节点的物理勾选状态,进行条件判定:
- 若 已选子节点数 == 子节点总数 $\rightarrow$ 父节点逆向置位为
Qt::Checked。 - 若 已选子节点数 == 0 $\rightarrow$ 父节点逆向置位为
Qt::Unchecked。 - 若 $0 <$ 已选子节点数 $<$ 子节点总数 $\rightarrow$ 父节点逆向置位为
Qt::PartiallyChecked。
3. 示例代码
3.1 独立类头文件声明 (evasystemwidget.h)
#pragma once
#include <QWidget>
#include <QList>
class QCheckBox; // 前向声明,规避头文件交叉编译污染
/**
* @brief NERV 人类补完计划中央推进域独立控制组件
* @note 采用纯代码架构设计,完全脱离 MainWindow 依赖,可作为独立模块挂载至任意视窗
*/
class EvaSystemWidget : public QWidget
{
Q_OBJECT
public:
explicit EvaSystemWidget(QWidget *parent = nullptr);
~EvaSystemWidget() override = default;
private slots:
/**
* @brief 拦截并处理任意 NERV 成员状态(同步率)变更的通用逆向槽函数
* @param state 触发该次变更的子节点当前整型状态
*/
void onNervStateChanged(int state);
private:
// 根节点(初号机驾驶员-碇真嗣,核心状态驱动/观测器)
QCheckBox* m_shinjiIkari = nullptr;
// 子节点群容器(NERV 核心成员链表,解耦硬编码限制)
QList<QCheckBox*> m_nervMemberList;
};
3.2 独立类源文件实现 (evasystemwidget.cpp)
#include "evasystemwidget.h"
#include <QVBoxLayout>
#include <QGroupBox>
#include <QCheckBox>
EvaSystemWidget::EvaSystemWidget(QWidget *parent)
: QWidget(parent)
{
// 1. 纯代码构建局部核心排版布局管理器
QVBoxLayout* externalLayout = new QVBoxLayout(this);
// 2. 实例化主题复合组件域
QGroupBox* evaGroup = new QGroupBox("NERV · 人类补完计划中央推进域", this);
externalLayout->addWidget(evaGroup);
// 3. 组内内部垂直排版管理器配置
QVBoxLayout* groupLayout = new QVBoxLayout(evaGroup);
// 4. 实例化根节点(Shinji),并强行开启三态属性
m_shinjiIkari = new QCheckBox("Shinji (碇真嗣) [初号机核心觉醒观测器]", evaGroup);
m_shinjiIkari->setTristate(true);
groupLayout->addWidget(m_shinjiIkari);
// 5. 纯代码批量实例化子节点(极简罗马名),并追加至托管容器中
QStringList nervNames = {
"Rei (绫波丽) [零号机]",
"Asuka (明日香) [二号机]",
"Mari (玛丽) [八号机]",
"Kaworu (渚薰) [六号机]",
"Misato (葛城美里) [战术指挥官]"
};
for (const QString& name : nervNames)
{
QCheckBox* subBox = new QCheckBox(name, evaGroup);
groupLayout->addWidget(subBox);
m_nervMemberList.append(subBox); // 建立物理拓扑映射
}
// =========================================================================
// 控制流拓扑 1:正向驱动流 (Top-Down) -> 真嗣核心觉醒,强制重刷全员状态
// =========================================================================
connect(m_shinjiIkari, &QCheckBox::clicked, this, [this](bool checked) {
// 当用户物理点击根节点时,驱动所有 NERV 成员的状态发生强制硬洗牌
// checked == true -> 开启人类补完计划,全员强制进入补完状态
// checked == false -> 中止补完计划,全员回归独立物理个体
Qt::CheckState targetState = checked ? Qt::Checked : Qt::Unchecked;
for (QCheckBox* subBox : m_nervMemberList)
{
// 写入属性时自动触发子节点的 stateChanged 信号,激活逆向控制流逻辑
subBox->setCheckState(targetState);
}
});
// =========================================================================
// 控制流拓扑 2:逆向反馈流 (Bottom-Up) -> 各成员的同步率动态反馈至真嗣的状态机
// =========================================================================
for (QCheckBox* subBox : m_nervMemberList)
{
connect(subBox, &QCheckBox::stateChanged, this, &EvaSystemWidget::onNervStateChanged);
}
// 默认初始化界面:日常静默状态
m_shinjiIkari->setCheckState(Qt::Unchecked);
}
void EvaSystemWidget::onNervStateChanged(int /*state*/)
{
// 实时通过物理迭代法统计当前进入补完状态的成员数,彻底规避传统计数器错位的边界漏洞
int activatedCount = 0;
for (const QCheckBox* subBox : m_nervMemberList)
{
if (subBox->isChecked())
{
activatedCount++;
}
}
// 执行逆向推导控制流判定(根据 NERV 成员激活规模,洗牌碇真嗣的状态机)
if (activatedCount == m_nervMemberList.size())
{
// 边界条件 A:全员参与补完 -> 真嗣进入完全觉醒/引发冲击状态(Checked)
m_shinjiIkari->setCheckState(Qt::Checked);
}
else if (activatedCount == 0)
{
// 边界条件 B:全员处于日常静默 -> 真嗣进入平稳/拒绝补完状态(Unchecked)
m_shinjiIkari->setCheckState(Qt::Unchecked);
}
else
{
// 边界条件 C:仅部分成员激活同步率 -> 处于补完中转的半选中混沌态(PartiallyChecked)
m_shinjiIkari->setCheckState(Qt::PartiallyChecked);
}
}
3.3 模块挂载:如何在主窗体中纯代码调用此独立类
由于该自定义组件独立继承自 QWidget,其解耦度极高。若要在 MainWindow 中展现,只需实例化该类并在布局管理器中进行托管挂载即可:
// mainwindow.cpp 纯代码挂载机制说明
#include "mainwindow.h"
#include "evasystemwidget.h"
#include <QHBoxLayout>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
this->resize(900, 700);
// 实例化中心主承载组件
QWidget* centralWidget = new QWidget(this);
this->setCentralWidget(centralWidget);
// 构建全局主布局
QHBoxLayout* mainLayout = new QHBoxLayout(centralWidget);
// 纯代码实例化并挂载独立封装的 EVA 控制域组件
EvaSystemWidget* evaSystem = new EvaSystemWidget(centralWidget);
// 塞入主布局管理器进行排版托管
mainLayout->addWidget(evaSystem);
}

浙公网安备 33010602011771号