跳到主要内容

基础控件

Qt Widgets 提供了丰富的桌面控件,本节深入介绍最常用的基础控件及其用法。理解这些控件的工作原理和使用技巧,是构建专业桌面应用的基础。

控件的共同特性

Qt 的所有控件都继承自 QWidget,这意味着它们拥有一些共同的属性和行为。理解这些共同特性有助于快速掌握各类控件的使用方法。

基础属性操作

每个控件都具备以下核心属性:

// 启用与禁用
widget->setEnabled(false); // 禁用控件(变灰,不响应交互)
widget->setEnabled(true); // 重新启用
bool isDisabled = widget->isEnabled(); // 检查状态

// 可见性控制
widget->setVisible(true); // 显示控件
widget->setVisible(false); // 隐藏控件
widget->hide(); // 隐藏(等效于 setVisible(false))
widget->show(); // 显示(等效于 setVisible(true))
bool isVisible = widget->isVisible();

// 窗口标题(对顶层窗口有效)
widget->setWindowTitle("应用程序标题");

// 几何属性
widget->resize(400, 300); // 设置大小
widget->setFixedSize(400, 300); // 固定大小(不可拖拽调整)
widget->move(100, 100); // 设置位置(屏幕坐标)
widget->setGeometry(100, 100, 400, 300); // 同时设置位置和大小

// 样式表
widget->setStyleSheet("color: red; background: white;");

// 工具提示
widget->setToolTip("这是提示信息");

控件的父子关系

Qt 使用对象树管理控件的内存和层次结构:

// 创建控件时指定父对象
QPushButton *button = new QPushButton("按钮", parentWidget);

// 父对象销毁时,子控件会自动销毁
// 因此通常不需要手动 delete 子控件

// 获取父控件
QWidget *parent = button->parentWidget();

// 获取所有子控件
QList<QWidget*> children = parentWidget->findChildren<QWidget*>();

QLabel(标签)

QLabel 是最简单的控件,用于显示文本、图片或动画。它不接收键盘焦点,适合作为信息展示区域。

基本文本显示

#include <QLabel>

// 创建标签
QLabel *label = new QLabel("Hello Qt", this);

// 设置文本内容
label->setText("新文本内容");

// 获取文本
QString text = label->text();

// 文本对齐方式
label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); // 左对齐+垂直居中
label->setAlignment(Qt::AlignCenter); // 完全居中
label->setAlignment(Qt::AlignRight | Qt::AlignTop); // 右上对齐

// 自动换行
label->setWordWrap(true); // 启用自动换行
label->setWordWrap(false); // 禁用自动换行(默认)

文本对齐选项详解

对齐标志说明
Qt::AlignLeft水平左对齐
Qt::AlignRight水平右对齐
Qt::AlignHCenter水平居中
Qt::AlignTop垂直顶部对齐
Qt::AlignBottom垂直底部对齐
Qt::AlignVCenter垂直居中
Qt::AlignCenter水平和垂直同时居中

富文本支持

QLabel 支持 HTML 子集,可以显示富文本:

// HTML 富文本
QLabel *htmlLabel = new QLabel(this);
htmlLabel->setText("<h1>标题</h1>"
"<p style='color:red;'>红色段落文字</p>"
"<ul><li>列表项1</li><li>列表项2</li></ul>");

// 设置文本格式
htmlLabel->setTextFormat(Qt::RichText); // 富文本模式(默认)
htmlLabel->setTextFormat(Qt::PlainText); // 纯文本模式
htmlLabel->setTextFormat(Qt::AutoText); // 自动检测

// 常用 HTML 标签
QString richText = R"(
<b>粗体</b> <i>斜体</i> <u>下划线</u>
<span style="color:#FF0000;">红色文字</span>
<a href="https://qt.io">链接</a>
)";

图片显示

// 显示图片
QLabel *imageLabel = new QLabel(this);
imageLabel->setPixmap(QPixmap(":/images/logo.png"));

// 图片缩放(保持比例)
QPixmap pixmap(":/images/photo.jpg");
QPixmap scaled = pixmap.scaled(200, 150, Qt::KeepAspectRatio, Qt::SmoothTransformation);
imageLabel->setPixmap(scaled);

// 图片自适应标签大小
imageLabel->setScaledContents(true); // 图片会拉伸填充标签
// 注意:可能导致图片变形

// 推荐做法:固定标签大小,图片保持比例
imageLabel->setFixedSize(200, 150);
imageLabel->setPixmap(pixmap.scaled(imageLabel->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));

交互式标签

虽然 QLabel 主要用于显示,但也可以添加简单的交互:

// 启用文本选择
label->setTextInteractionFlags(Qt::TextSelectableByMouse);
// 用户可以用鼠标选择和复制文本

// 启用链接点击
label->setTextInteractionFlags(Qt::TextBrowserInteraction);
label->setOpenExternalLinks(true); // 自动打开外部链接

// 连接链接点击信号
connect(label, &QLabel::linkActivated, this, [](const QString &link) {
qDebug() << "点击了链接:" << link;
});

// 示例:带链接的标签
label->setText("访问 <a href='https://qt.io'>Qt官网</a> 获取更多信息");
label->setTextInteractionFlags(Qt::TextBrowserInteraction);
label->setOpenExternalLinks(true);

QPushButton(按钮)

QPushButton 是最常用的交互控件,用于触发操作。它支持普通按钮、切换按钮、菜单按钮等多种形式。

基本用法

#include <QPushButton>

// 创建按钮
QPushButton *button = new QPushButton("点击我", this);

// 获取和设置文本
button->setText("新文本");
QString text = button->text();

// 设置大小
button->setFixedSize(120, 40); // 固定大小
button->setMinimumSize(100, 30); // 最小大小

// 设置图标
button->setIcon(QIcon(":/icons/save.png"));
button->setIconSize(QSize(24, 24));

// 设置快捷键
button->setShortcut(QKeySequence("Ctrl+S"));
button->setShortcut(Qt::Key_Enter); // 单键快捷键

点击事件处理

按钮的核心功能是响应点击事件,Qt 提供了多种处理方式:

// 方式一:连接 clicked 信号(推荐)
connect(button, &QPushButton::clicked, this, []() {
qDebug() << "按钮被点击";
});

// 方式二:Lambda 捕获上下文
QPushButton *btn1 = new QPushButton("按钮1");
QPushButton *btn2 = new QPushButton("按钮2");

connect(btn1, &QPushButton::clicked, this, [this, btn1]() {
// 可以访问 this 和 btn1
handleButtonClick(btn1->text());
});

// 方式三:连接到槽函数
// 在类声明中
class MainWindow : public QMainWindow {
Q_OBJECT
private slots:
void onButtonClicked();
};

// 在类实现中
void MainWindow::onButtonClicked() {
// 处理点击
}

// 连接
connect(button, &QPushButton::clicked, this, &MainWindow::onButtonClicked);

切换按钮

QPushButton 可以作为切换按钮使用,用于开关类操作:

// 设置为可切换
QPushButton *toggleBtn = new QPushButton("开关", this);
toggleBtn->setCheckable(true); // 启用切换功能

// 设置初始状态
toggleBtn->setChecked(true); // 初始为选中状态

// 监听状态变化
connect(toggleBtn, &QPushButton::toggled, this, [](bool checked) {
if (checked) {
qDebug() << "开关打开";
} else {
qDebug() << "开关关闭";
}
});

// 实际应用:显示/隐藏面板
QPushButton *panelToggle = new QPushButton("显示面板");
panelToggle->setCheckable(true);
panelToggle->setChecked(true);

QWidget *panel = new QWidget;
connect(panelToggle, &QPushButton::toggled, panel, &QWidget::setVisible);

按钮样式定制

使用样式表可以完全自定义按钮外观:

button->setStyleSheet(R"(
QPushButton {
background-color: #4CAF50; /* 背景色 */
color: white; /* 文字颜色 */
border: none; /* 无边框 */
padding: 10px 20px; /* 内边距 */
border-radius: 4px; /* 圆角 */
font-size: 14px; /* 字号 */
font-weight: bold; /* 粗体 */
}
QPushButton:hover {
background-color: #45a049; /* 悬停颜色 */
}
QPushButton:pressed {
background-color: #3d8b40; /* 按下颜色 */
}
QPushButton:disabled {
background-color: #cccccc; /* 禁用颜色 */
color: #666666;
}
)");

带菜单的按钮

按钮可以关联下拉菜单:

#include <QMenu>

QPushButton *menuBtn = new QPushButton("选项", this);
menuBtn->setIcon(QIcon(":/icons/arrow-down.png"));

QMenu *menu = new QMenu(this);
menu->addAction("新建", this, []() { qDebug() << "新建"; });
menu->addAction("打开", this, []() { qDebug() << "打开"; });
menu->addSeparator(); // 分隔线
menu->addAction("退出", this, []() { qDebug() << "退出"; });

menuBtn->setMenu(menu);

// 点击按钮显示菜单
// 也可以设置菜单按钮样式
menuBtn->setStyleSheet(R"(
QPushButton::menu-indicator {
subcontrol-position: right center;
subcontrol-origin: padding;
left: -5px;
}
)");

QLineEdit(单行输入框)

QLineEdit 是用于接收单行文本输入的控件,支持输入验证、密码模式、自动补全等功能。

基本用法

#include <QLineEdit>

// 创建输入框
QLineEdit *lineEdit = new QLineEdit(this);

// 占位文本(输入为空时显示的灰色提示)
lineEdit->setPlaceholderText("请输入用户名");

// 获取和设置文本
lineEdit->setText("默认值");
QString text = lineEdit->text();

// 清空
lineEdit->clear();

// 设置只读
lineEdit->setReadOnly(true); // 只读模式,可选择复制,但不能编辑
lineEdit->setReadOnly(false); // 恢复可编辑

文本变化信号

QLineEdit 提供了多个与文本变化相关的信号,理解它们的区别很重要:

// textChanged: 文本内容任何变化都会触发
// 包括用户输入和程序调用 setText()
connect(lineEdit, &QLineEdit::textChanged, this, [](const QString &text) {
qDebug() << "文本变化(任何原因):" << text;
});

// textEdited: 仅用户编辑时触发
// 调用 setText() 不会触发此信号
connect(lineEdit, &QLineEdit::textEdited, this, [](const QString &text) {
qDebug() << "用户编辑了文本:" << text;
});

// returnPressed: 按下回车键时触发
connect(lineEdit, &QLineEdit::returnPressed, this, [this, lineEdit]() {
QString text = lineEdit->text();
qDebug() << "用户按回车提交:" << text;
// 常用于搜索框、登录表单等
});

// editingFinished: 编辑完成时触发
// 触发条件:按回车 或 输入框失去焦点且内容有变化
connect(lineEdit, &QLineEdit::editingFinished, this, [this, lineEdit]() {
qDebug() << "编辑完成:" << lineEdit->text();
});

// cursorPositionChanged: 光标位置变化
connect(lineEdit, &QLineEdit::cursorPositionChanged, this, [](int oldPos, int newPos) {
qDebug() << "光标从" << oldPos << "移动到" << newPos;
});

信号选择指南

信号触发时机典型用途
textChanged任何文本变化实时验证、搜索建议
textEdited用户编辑记录用户修改
returnPressed按回车键提交表单、执行搜索
editingFinished编辑完成保存数据、验证输入

输入验证

限制用户输入内容是表单设计的重要部分:

// 最大长度限制
lineEdit->setMaxLength(20); // 最多 20 个字符

// 整数验证器:只允许输入 0-100 的整数
lineEdit->setValidator(new QIntValidator(0, 100, this));

// 浮点数验证器:允许输入 0.0-100.0,最多 2 位小数
lineEdit->setValidator(new QDoubleValidator(0.0, 100.0, 2, this));

// 正则表达式验证器
// 验证邮箱格式
QRegularExpression emailRegex(R"(\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b)");
lineEdit->setValidator(new QRegularExpressionValidator(emailRegex, this));

// 验证手机号(中国大陆)
QRegularExpression phoneRegex(R"(^1[3-9]\d{9}$)");
lineEdit->setValidator(new QRegularExpressionValidator(phoneRegex, this));

// 检查输入是否有效
if (lineEdit->hasAcceptableInput()) {
qDebug() << "输入有效";
} else {
qDebug() << "输入无效";
}

输入掩码

输入掩码提供了更精确的格式控制:

// IP 地址格式
lineEdit->setInputMask("000.000.000.000;_");
// 0 = 必须输入数字
// _ = 空白占位符

// 日期格式
lineEdit->setInputMask("0000-00-00");

// MAC 地址
lineEdit->setInputMask("HH:HH:HH:HH:HH:HH;_");
// H = 必须输入十六进制字符

// 电话号码格式
lineEdit->setInputMask("(999) 999-9999");
// 9 = 可选数字

// 许可证密钥格式(字母转大写)
lineEdit->setInputMask(">AAAAA-AAAAA-AAAAA-AAAAA;#");
// > = 后续字母转大写
// A = 必须输入字母
// # = 空白占位符

输入掩码字符说明

字符含义
A必须输入字母
a可选输入字母
N必须输入字母或数字
n可选输入字母或数字
X必须输入任意非空白字符
x可选输入任意非空白字符
9可选输入数字
0必须输入数字
D必须输入 1-9 的数字
H必须输入十六进制字符
>后续字母转大写
<后续字母转小写

密码输入

// 密码模式:显示为圆点
lineEdit->setEchoMode(QLineEdit::Password);

// 无回显模式:什么都不显示(高安全场景)
lineEdit->setEchoMode(QLineEdit::NoEcho);

// 编辑时显示,失去焦点后隐藏
lineEdit->setEchoMode(QLineEdit::PasswordEchoOnEdit);

// 正常模式(默认)
lineEdit->setEchoMode(QLineEdit::Normal);

// 获取显示文本(密码模式下是圆点)
QString displayText = lineEdit->displayText(); // "********"
QString actualText = lineEdit->text(); // "password123"

自动补全

#include <QCompleter>

// 创建补全器
QStringList words;
words << "apple" << "application" << "banana" << "cherry" << "orange";

QCompleter *completer = new QCompleter(words, this);
completer->setCaseSensitivity(Qt::CaseInsensitive); // 不区分大小写
completer->setFilterMode(Qt::MatchContains); // 包含匹配(不是前缀匹配)

lineEdit->setCompleter(completer);

// 文件路径补全
QCompleter *fileCompleter = new QCompleter(this);
fileCompleter->setModel(new QFileSystemModel(fileCompleter));
lineEdit->setCompleter(fileCompleter);

清除按钮

Qt 5.2+ 提供内置清除按钮:

// 显示清除按钮(输入框有内容时显示)
lineEdit->setClearButtonEnabled(true);

// 或者添加自定义操作按钮
QAction *searchAction = lineEdit->addAction(
QIcon(":/icons/search.png"),
QLineEdit::TrailingPosition // 右侧
);
connect(searchAction, &QAction::triggered, this, [this, lineEdit]() {
performSearch(lineEdit->text());
});

// 左侧添加图标
QAction *userAction = lineEdit->addAction(
QIcon(":/icons/user.png"),
QLineEdit::LeadingPosition // 左侧
);

QTextEdit(多行文本编辑)

QTextEdit 用于编辑和显示多行文本,支持纯文本和富文本。

基本用法

#include <QTextEdit>

// 创建文本编辑器
QTextEdit *textEdit = new QTextEdit(this);

// 占位文本
textEdit->setPlaceholderText("请输入内容...");

// 设置纯文本
textEdit->setPlainText("这是一段纯文本");

// 设置富文本(HTML)
textEdit->setHtml("<h1>标题</h1><p>段落内容</p>");

// 获取文本
QString plainText = textEdit->toPlainText(); // 纯文本
QString html = textEdit->toHtml(); // HTML 格式

// 追加文本
textEdit->append("追加的新行");

// 在当前位置插入
textEdit->insertPlainText("插入的文本");

// 清空
textEdit->clear();

只读模式

// 设置只读(用于显示内容)
textEdit->setReadOnly(true);

// 只读模式下用户不能编辑,但可以选择和复制

// 禁用编辑但允许滚动
textEdit->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);

文本格式控制

// 设置字体
textEdit->setFont(QFont("Microsoft YaHei", 12));

// 设置当前字符格式
QTextCharFormat format;
format.setFontFamily("Courier New");
format.setFontPointSize(14);
format.setForeground(Qt::blue);
format.setFontWeight(QFont::Bold);

QTextCursor cursor = textEdit->textCursor();
cursor.mergeCharFormat(format); // 应用格式到选中文本

// 设置段落格式
QTextBlockFormat blockFormat;
blockFormat.setAlignment(Qt::AlignCenter);
blockFormat.setIndent(2);
cursor.mergeBlockFormat(blockFormat);

// 限制最大行数
textEdit->setMaximumBlockCount(1000); // 超过后自动删除最旧的行

撤销/重做

// 检查是否可撤销/重做
if (textEdit->document()->isUndoAvailable()) {
textEdit->undo();
}

if (textEdit->document()->isRedoAvailable()) {
textEdit->redo();
}

// 清除撤销历史
textEdit->document()->clearUndoRedoStacks();

QComboBox(下拉框)

QComboBox 提供下拉选择列表,结合了按钮和弹出列表的功能。

基本用法

#include <QComboBox>

// 创建下拉框
QComboBox *comboBox = new QComboBox(this);

// 添加选项
comboBox->addItem("选项1");
comboBox->addItem("选项2");
comboBox->addItem("选项3");

// 批量添加
comboBox->addItems({"选项A", "选项B", "选项C"});

// 带图标的选项
comboBox->addItem(QIcon(":/icons/user.png"), "用户");
comboBox->addItem(QIcon(":/icons/admin.png"), "管理员");

// 设置当前选项
comboBox->setCurrentIndex(0); // 按索引
comboBox->setCurrentText("选项2"); // 按文本

// 获取当前选项
int index = comboBox->currentIndex(); // 索引
QString text = comboBox->currentText(); // 显示文本
QVariant data = comboBox->currentData(); // 关联数据

关联数据

每个选项可以关联额外数据:

// 添加选项时关联数据
comboBox->addItem("北京", 110000); // 显示"北京",存储 110000
comboBox->addItem("上海", 310000);
comboBox->addItem("广州", 440100);

// 获取选中项的数据
int cityCode = comboBox->currentData().toInt();

// 按数据查找
int index = comboBox->findData(310000);
if (index != -1) {
comboBox->setCurrentIndex(index);
}

// 按文本查找
int index = comboBox->findText("北京", Qt::MatchExactly);

信号处理

QComboBox 有两个重载的 currentIndexChanged 信号:

// 索引变化信号
connect(comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, [](int index) {
qDebug() << "选中索引:" << index;
});

// 文本变化信号
connect(comboBox, &QComboBox::currentTextChanged, this, [](const QString &text) {
qDebug() << "选中文本:" << text;
});

// 激活信号(用户主动选择时触发)
connect(comboBox, QOverload<int>::of(&QComboBox::activated),
this, [](int index) {
qDebug() << "用户激活了选项:" << index;
});

可编辑下拉框

// 设置可编辑
comboBox->setEditable(true);

// 设置插入策略
comboBox->setInsertPolicy(QComboBox::InsertAtBottom); // 插入到底部
comboBox->setInsertPolicy(QComboBox::InsertAtTop); // 插入到顶部
comboBox->setInsertPolicy(QComboBox::NoInsert); // 不插入

// 自动补全
comboBox->setCompleter(new QCompleter(comboBox->model(), this));

// 限制输入
comboBox->setValidator(new QIntValidator(0, 100, this));

QCheckBox & QRadioButton(复选框和单选框)

复选框

#include <QCheckBox>

// 创建复选框
QCheckBox *checkBox = new QCheckBox("同意用户协议", this);

// 设置选中状态
checkBox->setChecked(true);

// 获取状态
bool isChecked = checkBox->isChecked();

// 监听状态变化
connect(checkBox, &QCheckBox::stateChanged, this, [](int state) {
switch (state) {
case Qt::Unchecked:
qDebug() << "未选中";
break;
case Qt::PartiallyChecked:
qDebug() << "部分选中";
break;
case Qt::Checked:
qDebug() << "已选中";
break;
}
});

// 三态复选框
checkBox->setTristate(true); // 启用三种状态
checkBox->setCheckState(Qt::PartiallyChecked);

单选按钮

单选按钮需要分组使用,同一组内只能选中一个:

#include <QRadioButton>
#include <QButtonGroup>

// 创建按钮组
QButtonGroup *group = new QButtonGroup(this);

// 创建单选按钮
QRadioButton *radio1 = new QRadioButton("男", this);
QRadioButton *radio2 = new QRadioButton("女", this);
QRadioButton *radio3 = new QRadioButton("保密", this);

// 添加到组(带 ID)
group->addButton(radio1, 1);
group->addButton(radio2, 2);
group->addButton(radio3, 3);

// 设置默认选中
radio1->setChecked(true);

// 获取选中项
int selectedId = group->checkedId();
QRadioButton *selectedBtn = qobject_cast<QRadioButton*>(group->checkedButton());

// 监听选中变化
connect(group, QOverload<int>::of(&QButtonGroup::buttonClicked),
this, [](int id) {
qDebug() << "选中了 ID:" << id;
});

// 排他性设置
group->setExclusive(true); // 组内互斥(默认)
group->setExclusive(false); // 允许取消选择

QSlider & QSpinBox(滑块和数字框)

滑块

#include <QSlider>

// 创建滑块
QSlider *slider = new QSlider(Qt::Horizontal, this); // 水平滑块
// QSlider *slider = new QSlider(Qt::Vertical, this); // 垂直滑块

// 设置范围
slider->setRange(0, 100);
slider->setMinimum(0);
slider->setMaximum(100);

// 设置当前值
slider->setValue(50);

// 设置步长
slider->setSingleStep(1); // 键盘方向键步长
slider->setPageStep(10); // PageUp/PageDown 步长

// 刻度设置
slider->setTickPosition(QSlider::TicksBelow); // 刻度位置
slider->setTickInterval(10); // 刻度间隔

// 监听值变化
connect(slider, &QSlider::valueChanged, this, [](int value) {
qDebug() << "滑块值:" << value;
});

// 动作信号(用户拖动时触发)
connect(slider, &QSlider::sliderMoved, this, [](int position) {
qDebug() << "拖动到:" << position;
});

数字框

#include <QSpinBox>

// 创建数字框
QSpinBox *spinBox = new QSpinBox(this);

// 设置范围
spinBox->setRange(0, 100);

// 设置当前值
spinBox->setValue(50);

// 设置步长
spinBox->setSingleStep(5);

// 设置前后缀
spinBox->setPrefix("¥ "); // 前缀
spinBox->setSuffix(" 元"); // 后缀

// 特殊值显示(最小值时显示特殊文本)
spinBox->setSpecialValueText("不限");

// 监听值变化
connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged),
this, [](int value) {
qDebug() << "数字框值:" << value;
});

// 浮点数版本
#include <QDoubleSpinBox>
QDoubleSpinBox *doubleSpin = new QDoubleSpinBox(this);
doubleSpin->setRange(0.0, 1.0);
doubleSpin->setSingleStep(0.1);
doubleSpin->setDecimals(2); // 小数位数

滑块与数字框联动

QSlider *slider = new QSlider(Qt::Horizontal, this);
QSpinBox *spinBox = new QSpinBox(this);

slider->setRange(0, 100);
spinBox->setRange(0, 100);

// 双向绑定
connect(slider, &QSlider::valueChanged, spinBox, &QSpinBox::setValue);
connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged),
slider, &QSlider::setValue);

QProgressBar(进度条)

基本用法

#include <QProgressBar>

// 创建进度条
QProgressBar *progressBar = new QProgressBar(this);

// 设置范围
progressBar->setRange(0, 100);

// 设置当前值
progressBar->setValue(50);

// 设置文本格式
progressBar->setTextVisible(true);
progressBar->setFormat("%p%"); // 显示百分比(默认)
progressBar->setFormat("%v / %m"); // 显示 当前值/最大值
progressBar->setFormat("%p% (%v/%m)"); // 综合格式

// 设置方向
progressBar->setOrientation(Qt::Horizontal); // 水平(默认)
progressBar->setOrientation(Qt::Vertical); // 垂直

// 设置对齐
progressBar->setAlignment(Qt::AlignCenter);

不确定模式

当无法确定进度时,使用不确定模式:

// 进入不确定模式(设置最大值为 0)
progressBar->setRange(0, 0);
// 进度条会显示来回滚动的动画

// 恢复正常模式
progressBar->setRange(0, 100);
progressBar->setValue(50);

实际应用示例

// 模拟文件下载进度
void downloadFile(const QString &url)
{
progressBar->setRange(0, 100);
progressBar->setValue(0);

// 假设这是网络请求的进度回调
connect(networkReply, &QNetworkReply::downloadProgress,
this, [this](qint64 received, qint64 total) {
if (total > 0) {
int percent = static_cast<int>((received * 100) / total);
progressBar->setValue(percent);
}
});

connect(networkReply, &QNetworkReply::finished, this, [this]() {
progressBar->setValue(100);
statusBar()->showMessage("下载完成");
});
}

布局管理

控件需要通过布局管理器组织位置。Qt 提供了多种布局类。

常用布局类型

#include <QVBoxLayout>    // 垂直布局
#include <QHBoxLayout> // 水平布局
#include <QGridLayout> // 网格布局
#include <QFormLayout> // 表单布局

// 垂直布局:控件从上到下排列
QVBoxLayout *vLayout = new QVBoxLayout(this);
vLayout->addWidget(new QLabel("标签1"));
vLayout->addWidget(new QLabel("标签2"));
vLayout->addWidget(new QPushButton("按钮"));

// 水平布局:控件从左到右排列
QHBoxLayout *hLayout = new QHBoxLayout();
hLayout->addWidget(new QPushButton("确定"));
hLayout->addWidget(new QPushButton("取消"));
hLayout->addStretch(); // 弹性空间,将控件推到左边

// 网格布局:按行列组织
QGridLayout *grid = new QGridLayout();
grid->addWidget(new QLabel("姓名:"), 0, 0); // 第0行第0列
grid->addWidget(nameEdit, 0, 1); // 第0行第1列
grid->addWidget(new QLabel("年龄:"), 1, 0); // 第1行第0列
grid->addWidget(ageSpin, 1, 1); // 第1行第1列
grid->addWidget(new QLabel("备注:"), 2, 0); // 第2行第0列
grid->addWidget(noteEdit, 2, 1, 1, 2); // 跨2列

// 表单布局:标签-字段成对
QFormLayout *form = new QFormLayout();
form->addRow("用户名:", usernameEdit);
form->addRow("密码:", passwordEdit);
form->addRow("邮箱:", emailEdit);

布局属性

// 边距设置
layout->setContentsMargins(10, 10, 10, 10); // 左、上、右、下
layout->setMargin(10); // 四边相同边距

// 控件间距
layout->setSpacing(10);

// 拉伸因子
hLayout->addWidget(widget1, 1); // 拉伸因子 1
hLayout->addWidget(widget2, 2); // 拉伸因子 2(占更多空间)

// 设置布局到控件
QWidget *widget = new QWidget();
widget->setLayout(layout);
setCentralWidget(widget); // 对于 QMainWindow

嵌套布局

QVBoxLayout *mainLayout = new QVBoxLayout(this);

// 顶部区域
QHBoxLayout *topLayout = new QHBoxLayout();
topLayout->addWidget(new QLabel("搜索:"));
topLayout->addWidget(searchEdit);
topLayout->addWidget(searchBtn);

// 中间区域
QGridLayout *centerLayout = new QGridLayout();
// ...

// 底部区域
QHBoxLayout *bottomLayout = new QHBoxLayout();
bottomLayout->addStretch();
bottomLayout->addWidget(okBtn);
bottomLayout->addWidget(cancelBtn);

// 组合布局
mainLayout->addLayout(topLayout);
mainLayout->addLayout(centerLayout);
mainLayout->addStretch(); // 弹性空间
mainLayout->addLayout(bottomLayout);

完整示例:登录表单

#include <QDialog>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QCheckBox>
#include <QMessageBox>

class LoginDialog : public QDialog
{
Q_OBJECT

public:
LoginDialog(QWidget *parent = nullptr) : QDialog(parent)
{
setWindowTitle("用户登录");
setFixedSize(400, 220);

setupUI();
setupConnections();
}

private:
void setupUI()
{
// 创建控件
QLabel *titleLabel = new QLabel("欢迎登录");
titleLabel->setAlignment(Qt::AlignCenter);
titleLabel->setStyleSheet("font-size: 18px; font-weight: bold; color: #333;");

QLabel *userLabel = new QLabel("用户名:");
QLabel *pwdLabel = new QLabel("密码:");

usernameEdit = new QLineEdit();
usernameEdit->setPlaceholderText("请输入用户名");
usernameEdit->setClearButtonEnabled(true);

passwordEdit = new QLineEdit();
passwordEdit->setPlaceholderText("请输入密码");
passwordEdit->setEchoMode(QLineEdit::Password);
passwordEdit->setClearButtonEnabled(true);

rememberCheck = new QCheckBox("记住密码");

loginBtn = new QPushButton("登录");
loginBtn->setFixedHeight(36);
loginBtn->setStyleSheet(R"(
QPushButton {
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
font-size: 14px;
}
QPushButton:hover {
background-color: #45a049;
}
QPushButton:pressed {
background-color: #3d8b40;
}
)");

cancelBtn = new QPushButton("取消");
cancelBtn->setFixedHeight(36);

// 表单布局
QGridLayout *formLayout = new QGridLayout();
formLayout->addWidget(userLabel, 0, 0);
formLayout->addWidget(usernameEdit, 0, 1);
formLayout->addWidget(pwdLabel, 1, 0);
formLayout->addWidget(passwordEdit, 1, 1);
formLayout->addWidget(rememberCheck, 2, 1);

// 按钮布局
QHBoxLayout *btnLayout = new QHBoxLayout();
btnLayout->addStretch();
btnLayout->addWidget(loginBtn);
btnLayout->addWidget(cancelBtn);

// 主布局
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(titleLabel);
mainLayout->addSpacing(20);
mainLayout->addLayout(formLayout);
mainLayout->addSpacing(10);
mainLayout->addLayout(btnLayout);
mainLayout->addStretch();

// 设置默认按钮
loginBtn->setDefault(true);
}

void setupConnections()
{
connect(loginBtn, &QPushButton::clicked, this, &LoginDialog::tryLogin);
connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject);

// 回车登录
connect(passwordEdit, &QLineEdit::returnPressed,
loginBtn, &QPushButton::click);
}

private slots:
void tryLogin()
{
QString username = usernameEdit->text().trimmed();
QString password = passwordEdit->text();

// 验证
if (username.isEmpty()) {
QMessageBox::warning(this, "提示", "请输入用户名");
usernameEdit->setFocus();
return;
}

if (password.isEmpty()) {
QMessageBox::warning(this, "提示", "请输入密码");
passwordEdit->setFocus();
return;
}

// 这里应该调用实际的登录逻辑
// 模拟登录成功
if (username == "admin" && password == "123456") {
if (rememberCheck->isChecked()) {
// 保存登录信息
}
accept(); // 关闭对话框,返回 QDialog::Accepted
} else {
QMessageBox::critical(this, "错误", "用户名或密码错误");
passwordEdit->clear();
passwordEdit->setFocus();
}
}

private:
QLineEdit *usernameEdit;
QLineEdit *passwordEdit;
QCheckBox *rememberCheck;
QPushButton *loginBtn;
QPushButton *cancelBtn;
};

下一步

掌握了基础控件后,继续学习 容器控件 了解分组框、标签页、滚动区域等组织控件的容器。