Note
本篇逐一讲解 Qt 中最常用的 11 个控件:显示类(QLabel、QProgressBar、QGroupBox)、按钮类(QPushButton、QCheckBox、QRadioButton)、输入类(QLineEdit、QTextEdit、QComboBox、QSpinBox、QSlider)。每个控件按”是什么→怎么用”展开,并附可编译的完整示例。适合刚装好 Qt Creator、写过 Hello World 的初学者。
控件家族的共同底座:QWidget
在认识各个控件之前,先要明白一件事:所有控件都直接或间接继承自 QWidget。QWidget 提供了位置、大小、字体、提示、鼠标光标等通用属性,所以任何控件都有 setToolTip()、setFont()、setFixedSize() 这些方法。
更重要的机制是父子关系:构造控件时传入父对象指针,控件就会显示在父窗口内部,并在父窗口销毁时自动销毁——你几乎不需要手写 delete。
Warning
如果用
new创建控件却没传父对象,Qt 不会帮你回收,必须自己管理内存,否则就是内存泄漏。养成习惯:控件创建时就把this传进去。
QLabel:显示文本与图片
QLabel 是最基础的显示控件,不接收输入,可以显示纯文本、富文本(HTML)、图片(QPixmap)、动态图(QMovie)。
QLabel *label = new QLabel(this);
label->setText("用户名:"); // 显示文本
label->setAlignment(Qt::AlignCenter); // 居中对齐,可用 | 组合多方向
label->setPixmap(QPixmap(":/logo.png")); // 显示图片(来自资源文件)适用场景:标题、说明文字、字段名(配合输入框)、图片预览。它还有个隐藏技能——setBuddy() 设置”伙伴”控件后,用户按下标签文本里 & 指定的快捷键(如 &N 对应 Alt+N),焦点会自动跳到伙伴输入框上。
QPushButton:点击触发动作
QPushButton 是最常见的按钮,核心是 clicked() 信号。它支持文本、图标、工具提示三种配置:
QPushButton *btn = new QPushButton("打开文件", this);
btn->setIcon(QIcon(":/open.png"));
btn->setToolTip("选择一个文件打开");
connect(btn, &QPushButton::clicked, this, &MainWindow::openFile);适用场景:确认、取消、打开、提交等一次性动作。clicked(bool checked) 的参数对普通按钮没什么用——它按下即弹起,没有”按住”状态。
QLineEdit:单行文本输入
QLineEdit 接收一行文本,自带复制、粘贴、撤销、重做等编辑功能。核心属性是 text:
QLineEdit *edit = new QLineEdit(this);
edit->setPlaceholderText("请输入用户名"); // 灰色提示文字
edit->setMaxLength(20); // 限制最大长度
connect(edit, &QLineEdit::textChanged, this, &MainWindow::onTextChanged);密码框是它的经典用法,通过回显模式(echoMode)切换:
QLineEdit *pwd = new QLineEdit(this);
pwd->setEchoMode(QLineEdit::Password); // 输入内容显示为 *常用信号:textChanged(任何原因导致的文本变化都触发)、textEdited(仅用户手动编辑触发)、returnPressed(按下回车)、editingFinished(回车或失去焦点)。需要校验输入时挂一个验证器,非法字符根本输不进去:
// 只允许输入 0~65535 的整数
edit->setValidator(new QIntValidator(0, 65535, edit));QTextEdit:多行富文本编辑
QTextEdit 支持多行文本和 HTML 富文本编辑;只读场景用它的子类 QTextBrowser(还能打开链接)。需要纯文本(无格式、占用小)时,QPlainTextEdit 更合适。
QTextEdit *editor = new QTextEdit(this);
editor->setPlainText("初始内容"); // 按纯文本设置
editor->setHtml("<b>加粗</b>内容"); // 按 HTML 设置
QString text = editor->toPlainText(); // 取出纯文本适用场景:留言板、日志显示、笔记编辑器。取内容时注意选对 toPlainText() 还是 toHtml()。
QCheckBox:多选项与三态
QCheckBox 表示可以多个同时勾选的开关选项,核心是 isChecked() 和 toggled(bool) 信号(状态翻转时触发)。
QCheckBox *cb = new QCheckBox("记住密码", this);
connect(cb, &QCheckBox::toggled, this, [](bool on){
qDebug() << "记住密码:" << on;
});开启 setTristate(true) 后变成三态:未选中 / 部分选中 / 全选中,状态用 checkState() 读取。“全选”复选框就是典型应用——子项全选时它是选中态,部分选中时它自动显示半选态。
QRadioButton:单选互斥
QRadioButton 用于一组中只能选一个的场景。同一父窗口(或同一个分组容器)里的单选按钮自动互斥:选中新的,旧的自动取消。
// 同一 QGroupBox 内的两个单选按钮天然互斥
QGroupBox *box = new QGroupBox("性别", this);
QRadioButton *male = new QRadioButton("男");
QRadioButton *female = new QRadioButton("女");
female->setChecked(true); // 设置默认选中Warning
互斥只对”同一个父容器”内的单选按钮生效。如果性别和年龄段的单选按钮都直接放在窗体上,它们会互相干扰——点性别会把年龄段的选中清掉。解决方法:要么分别放进不同的 QGroupBox,要么用 QButtonGroup 在内存里做虚拟分组。
QComboBox:下拉选择
QComboBox 是”单行编辑框 + 下拉列表”的组合,用户可以点选,可编辑模式下还能手动输入。条目可以携带隐藏的自定义数据:
QComboBox *combo = new QComboBox(this);
combo->addItem("米饭", 5.0); // 第二个参数是隐藏数据(单价)
combo->addItems({"面条", "饺子"}); // 批量添加
combo->setEditable(true); // 允许用户输入新条目
connect(combo, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, [this](int i){ qDebug() << "选中" << combo->itemData(i); });itemData() 取出的隐藏数据,比”根据序号再查表”的写法干净得多。常用信号是 currentIndexChanged(int)(序号变)和 currentTextChanged(QString)(文本变)。
QSpinBox / QSlider:数值输入的两种姿势
QSpinBox(微调框)是”整数编辑框 + 上下箭头”,适合精确输入;QSlider(滑动条)是拖动的滑块,适合模糊地连续调整。两者都继承自整数值体系,用信号槽可以轻松联动:
QSpinBox *spin = new QSpinBox(this);
spin->setRange(0, 100);
spin->setSuffix(" %"); // 数值后自动带单位
QSlider *slider = new QSlider(Qt::Horizontal, this);
slider->setRange(0, 100);
// 双向联动:拖滑块改数字,改数字动滑块
connect(spin, QOverload<int>::of(&QSpinBox::valueChanged),
slider, &QSlider::setValue);
connect(slider, &QSlider::valueChanged,
spin, &QSpinBox::setValue);Success
不用担心双向 connect 造成死循环:Qt 的
setValue()发现新值与当前值相同会直接返回,不再发信号。音量调节、缩放比例这类”数字 + 滑块”界面就是标准写法。
QSlider 还可以用 setTickPosition(QSlider::TicksBelow) 和 setTickInterval(10) 显示刻度。处理浮点数时换用 QDoubleSpinBox(多了 setDecimals() 控制小数位数)。
QProgressBar:进度展示
QProgressBar 显示任务完成比例,不接收用户输入,只被代码驱动:
QProgressBar *bar = new QProgressBar(this);
bar->setRange(0, 100);
bar->setValue(40);
bar->setFormat("%v / %m"); // 自定义文本格式:40 / 100显示文本由范围与当前值自动算出,占位符 %p 是百分比、%v 是当前值、%m 是上限减下限。它的绝配是 QSlider——官方教程里”滑块拖动、进度条同步”就是一行 connect 完成的。
QGroupBox:分组容器
QGroupBox 是带标题和边框的容器,本身不处理业务,价值在于视觉分组与逻辑分组:一组单选按钮放进一个 QGroupBox,既明确了归属,又天然实现了组内互斥。
QGroupBox *box = new QGroupBox("字体样式", this);
QCheckBox *bold = new QCheckBox("加粗");
QCheckBox *italic = new QCheckBox("斜体");
QVBoxLayout *lay = new QVBoxLayout(box); // 布局器以 box 为父
lay->addWidget(bold);
lay->addWidget(italic);Success
给容器控件设置布局时,直接
new QVBoxLayout(box)就等于完成了”建布局 + setLayout”两步。往组框里塞控件时注意始终通过布局器排布,别用固定坐标。
控件速查表
| 控件 | 用途 | 常用信号 |
|---|---|---|
| QLabel | 显示文本/图片 | linkActivated |
| QPushButton | 触发动作 | clicked |
| QLineEdit | 单行输入 | textChanged、returnPressed |
| QTextEdit | 多行富文本编辑 | textChanged |
| QCheckBox | 多选开关 | toggled、stateChanged |
| QRadioButton | 组内单选 | toggled |
| QComboBox | 下拉选择 | currentIndexChanged |
| QSpinBox | 整数微调输入 | valueChanged |
| QSlider | 连续数值调节 | valueChanged |
| QProgressBar | 进度展示 | valueChanged |
| QGroupBox | 分组容器 | — |
完整可运行示例
下面把以上控件拼在一个窗口里,Qt5 / Qt6 均可编译:
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QLineEdit>
#include <QCheckBox>
#include <QRadioButton>
#include <QComboBox>
#include <QSpinBox>
#include <QSlider>
#include <QProgressBar>
#include <QGroupBox>
#include <QVBoxLayout>
#include <QFormLayout>
#include <QDebug>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget window;
window.setWindowTitle("常用控件示例");
window.resize(420, 480);
// 标签:提示文字
QLabel *label = new QLabel("下面是一个简单的设置表单:");
// 单行输入:用户名,限制 20 字符
QLineEdit *nameEdit = new QLineEdit;
nameEdit->setPlaceholderText("请输入用户名");
nameEdit->setMaxLength(20);
// 密码框:回显模式显示为 *
QLineEdit *pwdEdit = new QLineEdit;
pwdEdit->setEchoMode(QLineEdit::Password);
// 下拉框:选择城市,条目携带隐藏数据(区号)
QComboBox *cityCombo = new QComboBox;
cityCombo->addItem("北京", "010");
cityCombo->addItem("上海", "021");
// 单选按钮:放在同一分组框内自动互斥
QGroupBox *genderBox = new QGroupBox("性别");
QRadioButton *male = new QRadioButton("男");
QRadioButton *female = new QRadioButton("女");
female->setChecked(true);
QVBoxLayout *genderLay = new QVBoxLayout(genderBox);
genderLay->addWidget(male);
genderLay->addWidget(female);
// 复选框:三选三不互斥
QCheckBox *agree = new QCheckBox("我已阅读并同意用户协议");
// 数值输入与滑块联动:0~100
QSpinBox *spin = new QSpinBox;
spin->setRange(0, 100);
QSlider *slider = new QSlider(Qt::Horizontal);
slider->setRange(0, 100);
connect(spin, QOverload<int>::of(&QSpinBox::valueChanged),
slider, &QSlider::setValue);
connect(slider, &QSlider::valueChanged,
spin, &QSpinBox::setValue);
// 进度条跟随滑块
QProgressBar *bar = new QProgressBar;
bar->setRange(0, 100);
connect(slider, &QSlider::valueChanged,
bar, &QProgressBar::setValue);
// 多行编辑
QTextEdit *editor = new QTextEdit;
editor->setPlaceholderText("备注信息(可多行)");
// 按钮
QPushButton *btn = new QPushButton("提交");
connect(btn, &QPushButton::clicked, [nameEdit, pwdEdit, cityCombo, agree, spin](){
if (!agree->isChecked()) {
qDebug() << "请先勾选协议";
return;
}
qDebug() << "提交:" << nameEdit->text()
<< cityCombo->currentText()
<< "透明度" << spin->value() << "%";
});
// 表单布局:左列标签、右列输入控件
QFormLayout *form = new QFormLayout;
form->addRow("用户名", nameEdit);
form->addRow("密码", pwdEdit);
form->addRow("城市", cityCombo);
// 垂直主布局把所有内容从上到下排好
QVBoxLayout *mainLay = new QVBoxLayout(&window);
mainLay->addWidget(label);
mainLay->addLayout(form);
mainLay->addWidget(genderBox);
mainLay->addWidget(agree);
mainLay->addWidget(spin);
mainLay->addWidget(slider);
mainLay->addWidget(bar);
mainLay->addWidget(editor);
mainLay->addWidget(btn);
window.show();
return app.exec();
}Success
记控件不必死记硬背:先记住 QWidget 这个共同基类,再按”显示 / 按钮 / 输入 / 容器”四类归类理解。真正写项目时,Qt 助手(Qt Creator 里按 F1)随时能查到每个控件的全部方法与信号。
自测:三个问题检验掌握程度
- 密码框怎么让输入内容显示成星号?用哪个枚举值?
- 两个 QRadioButton 放在同一个窗体上却互不互斥,可能是什么原因?怎么解决?
- QComboBox 条目携带的”隐藏数据”如何读取?它和 currentText() 有什么区别? (答案都在上文)