第七章 基础控件(二):QCheckBox、QRadioButton、QSpinBox、QSlider、QProgressBar、QDateTimeEdit
好,咱们接着聊基础控件。上一章我们把按钮、输入框这些“门面”控件讲透了,这一章要深入一些交互性更强、带状态反馈的控件。说白了,就是让用户能做选择、调数值、看进度、选时间。这些控件在项目中几乎天天见,我敢说你写的每个Qt程序里至少会用到其中两三个。
7.1 QCheckBox:多选开关,状态不止两种
QCheckBox,复选框,大家再熟悉不过了。但你真的用对了吗?
先看最基础的用法:
QCheckBox *checkBox = new QCheckBox("记住密码", this);
connect(checkBox, &QCheckBox::toggled, [](bool checked) {
qDebug() << "选中状态:" << checked;
});
嗯,很简单。但我要说的是——复选框其实有三种状态。默认是两态(选中/未选中),但你还可以开启第三态:
checkBox->setTristate(true); // 开启三态
checkBox->setCheckState(Qt::PartiallyChecked); // 部分选中
什么时候用第三态? 我在做权限管理模块时遇到过:一个父节点下有多个子权限,如果子权限部分选中、部分未选,父节点就应该显示“部分选中”状态。这个设计很符合直觉,用户一看就懂。
7.2 QRadioButton:单选,天生排他
QRadioButton 和 QCheckBox 长得像,但逻辑完全不同。单选按钮是“排他”的——同一组里只能选一个。
关键点:必须放到 QButtonGroup 里。否则同个父窗口下的所有单选按钮会自动互斥,这往往不是你想要的效果。
QRadioButton *rbMale = new QRadioButton("男");
QRadioButton *rbFemale = new QRadioButton("女");
QRadioButton *rbOther = new QRadioButton("其他");
QButtonGroup *group = new QButtonGroup(this);
group->addButton(rbMale, 1);
group->addButton(rbFemale, 2);
group->addButton(rbOther, 3);
connect(group, QOverload<int>::of(&QButtonGroup::buttonClicked),
[](int id) {
qDebug() << "选中了ID:" << id;
});
我个人习惯给每个按钮分配一个枚举值,这样代码可读性高,后期维护也方便。比如:
enum Gender { Male = 1, Female, Other };
7.3 QSpinBox:数字调节,精准又灵活
QSpinBox 是整数调节框,QDoubleSpinBox 是浮点版。用法几乎一样,我就合在一起讲了。
QSpinBox *spinBox = new QSpinBox(this);
spinBox->setRange(0, 100);
spinBox->setValue(50);
spinBox->setSingleStep(5);
spinBox->setSuffix(" 分"); // 显示后缀
connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged),
[](int value) {
qDebug() << "当前值:" << value;
});
我曾经在做一个视频剪辑软件时,需要让用户设置帧率。帧率一般是 24、25、30、60 这些特殊值,不是连续的。这时候 QSpinBox 的 setSpecialValueText() 就派上用场了:
spinBox->setSpecialValueText("自动"); // 当值为最小值时显示"自动"
但更灵活的做法是用 QComboBox 配合自定义模型。嗯,这个后面再细讲。
7.4 QSlider:滑动条,手感很重要
QSlider 有两种方向:水平(Qt::Horizontal)和垂直(Qt::Vertical)。
QSlider *slider = new QSlider(Qt::Horizontal, this);
slider->setRange(0, 100);
slider->setValue(30);
slider->setTickPosition(QSlider::TicksBelow); // 刻度线在下方
slider->setTickInterval(10);
滑动条最常用的场景是音量控制、亮度调节、进度跳转。但有个细节很多人忽略——滑块移动的粒度。默认是 1,如果你想让用户拖动时感觉更“顺滑”,可以把 singleStep 设小一点,pageStep 设大一点:
slider->setSingleStep(1); // 键盘左右键步进
slider->setPageStep(10); // 键盘 PageUp/PageDown 步进
7.5 QProgressBar:进度条,别让用户干等
进度条是给用户“定心丸”的。哪怕进度不准,也比没有强。
QProgressBar *progressBar = new QProgressBar(this);
progressBar->setRange(0, 100);
progressBar->setValue(0);
progressBar->setTextVisible(true); // 显示百分比文字
如果你不知道任务总量,可以用“忙碌指示”模式:
progressBar->setRange(0, 0); // 范围设为 0-0,进入忙碌模式
// 此时进度条会显示一个来回滚动的动画条
我在做文件上传工具时,进度条是必须的。但有个坑——不要在 UI 线程里做耗时操作。如果你在主线程里直接循环更新进度,界面会卡死,进度条根本动不了。正确做法是用 QThread 或 QtConcurrent 把任务丢到后台线程,然后通过信号更新进度。
// 后台线程发射信号
emit progressUpdated(current, total);
// 主线程槽函数更新
connect(worker, &Worker::progressUpdated,
progressBar, &QProgressBar::setMaximum); // 设置最大值
connect(worker, &Worker::progressUpdated,
progressBar, &QProgressBar::setValue); // 设置当前值
7.6 QDateTimeEdit:日期时间选择,格式要人性化
QDateTimeEdit 是日期时间选择器,支持年、月、日、时、分、秒的单独调节。
QDateTimeEdit *dateTimeEdit = new QDateTimeEdit(QDateTime::currentDateTime(), this);
dateTimeEdit->setDisplayFormat("yyyy-MM-dd HH:mm:ss");
dateTimeEdit->setCalendarPopup(true); // 弹出日历选择
显示格式的占位符要记牢:
| 占位符 | 含义 | 示例 |
|---|---|---|
| yyyy | 四位年份 | 2025 |
| MM | 两位月份 | 03 |
| dd | 两位日期 | 15 |
| HH | 24小时制 | 14 |
| mm | 分钟 | 30 |
| ss | 秒 | 45 |
我曾经踩过一个坑:用户输入日期时,如果格式是 "yyyy/MM/dd",但系统区域设置用的是 "-" 分隔符,解析就会出错。所以建议统一用 "-" 或 ".",并且 setDisplayFormat 要和实际数据格式一致。
7.7 知识体系总览
这一章的内容比较多,我画了一张图帮你理清脉络:
7.8 综合示例:一个简单的设置面板
把今天学的控件串起来,写一个模拟的设置面板:
// 头文件略
QVBoxLayout *layout = new QVBoxLayout(this);
// 复选框:开启高级模式
QCheckBox *cbAdvanced = new QCheckBox("高级模式");
layout->addWidget(cbAdvanced);
// 单选按钮组:主题选择
QRadioButton *rbLight = new QRadioButton("浅色主题");
QRadioButton *rbDark = new QRadioButton("深色主题");
QButtonGroup *themeGroup = new QButtonGroup(this);
themeGroup->addButton(rbLight, 0);
themeGroup->addButton(rbDark, 1);
rbLight->setChecked(true);
QHBoxLayout *themeLayout = new QHBoxLayout;
themeLayout->addWidget(rbLight);
themeLayout->addWidget(rbDark);
layout->addLayout(themeLayout);
// 滑动条 + 数字框:音量调节
QHBoxLayout *volumeLayout = new QHBoxLayout;
QSlider *volumeSlider = new QSlider(Qt::Horizontal);
volumeSlider->setRange(0, 100);
QSpinBox *volumeSpin = new QSpinBox;
volumeSpin->setRange(0, 100);
volumeSpin->setSuffix("%");
connect(volumeSlider, &QSlider::valueChanged, volumeSpin, &QSpinBox::setValue);
connect(volumeSpin, QOverload<int>::of(&QSpinBox::valueChanged), volumeSlider, &QSlider::setValue);
volumeLayout->addWidget(new QLabel("音量:"));
volumeLayout->addWidget(volumeSlider);
volumeLayout->addWidget(volumeSpin);
layout->addLayout(volumeLayout);
// 进度条:模拟加载
QProgressBar *progressBar = new QProgressBar;
progressBar->setRange(0, 100);
layout->addWidget(progressBar);
QPushButton *btnStart = new QPushButton("开始模拟");
layout->addWidget(btnStart);
connect(btnStart, &QPushButton::clicked, [progressBar]() {
for (int i = 0; i <= 100; ++i) {
progressBar->setValue(i);
QApplication::processEvents(); // 简单演示用,实际项目别这么写
QThread::msleep(30);
}
});
// 日期时间选择
QDateTimeEdit *dtEdit = new QDateTimeEdit(QDateTime::currentDateTime());
dtEdit->setDisplayFormat("yyyy-MM-dd HH:mm:ss");
dtEdit->setCalendarPopup(true);
layout->addWidget(new QLabel("计划执行时间:"));
layout->addWidget(dtEdit);
这个例子虽然简单,但涵盖了今天讲的大部分控件。你可以在项目里直接套用这个结构,稍作修改就能用。
好,这一章就到这里。代码多敲几遍,手感自然就来了。
公众号:蓝海资料掘金营,微信deep3321