1. 前言
在使用 Qt 进行图形界面开发时,控件和布局是构建界面的两大核心基础。控件(如按钮、标签、输入框)负责与用户交互,而布局则负责合理安排控件的位置和大小,确保界面美观且响应式良好。
本章将围绕三个常见基础控件(QPushButton、QLabel、QLineEdit)以及四种常用布局(水平布局、垂直布局、网格布局、分裂器布局)展开讲解,重点通过示例展示其核心用法、信号槽连接方式和常见应用场景。
2. 常用控件详解
2.1 QPushButton(按钮)
QPushButton 是 Qt 中最常用的控件之一,用于实现按钮功能,用户点击按钮后可触发指定的操作。
2.1.1 基本用法
创建一个按钮通常只需要两行代码:
QPushButton* button = new QPushButton("Click Me");
layout->addWidget(button);
你可以设置按钮的文本、大小、图标、提示文字等:
button->setFixedSize(100, 30); // 设置固定大小
button->setToolTip("点击按钮触发操作"); // 鼠标悬停提示
button->setStyleSheet("font-weight: bold"); // 使用样式表美化
2.1.2 信号与槽
QPushButton 最常用的信号是 clicked(),可以通过 connect() 连接到窗口的槽函数或 lambda 表达式:
connect(button, &QPushButton::clicked, this, &QWidget::close);
上面的代码会在点击按钮时关闭当前窗口。我们也可以使用 lambda:
connect(button, &QPushButton::clicked, [=](){
qDebug() << "按钮被点击了!";
});
2.1.3 常用API
| 方法 | 功能说明 |
|---|---|
setText(const QString&) | 设置按钮显示的文本 |
setIcon(const QIcon&) | 设置图标 |
setCheckable(bool) | 设置是否为可选中按钮 |
setChecked(bool) | 设置按钮初始是否被选中 |
setEnabled(bool) | 设置按钮是否可用 |
setStyleSheet(const QString&) | 设置样式(颜色、边框等) |
clicked() | 按钮被点击的信号 |
2.1.4 示例:点击按钮关闭窗口
#include "MyWidget.h"
#include<qpushbutton.h>
#include<QHBoxLayout>
MyWidget::MyWidget(QWidget *parent)
: QWidget(parent)
{
QPushButton* button = new QPushButton("Close");
QHBoxLayout* hlay = new QHBoxLayout(this);
hlay->addWidget(button);
connect(button, &QPushButton::clicked, this, &QWidget::close);
}
MyWidget::~MyWidget()
{}
运行后会显示一个按钮,点击后窗口关闭。
2.2 QLabel(标签)
QLabel 是 Qt 中用于显示文本或图片的控件。它通常用于界面提示、描述信息,也可用于展示图标或动画。
2.2.1 基本用法
最常见的使用方式是显示一段文本:
QLabel* label = new QLabel("Hello Qt");
layout->addWidget(label);
也可以通过 setText() 动态设置文本内容:
label->setText("新的文本内容");
此外,QLabel 也可以设置为对用户交互不可编辑状态下显示 HTML 内容或图片。
2.2.2 显示图像
QLabel 支持直接显示图像(QPixmap):
QPixmap pix(":/images/logo.png"); // 加载资源图片
label->setPixmap(pix);
默认情况下,图像不会自动缩放。如果希望图像适应标签大小,可以启用缩放:
label->setScaledContents(true);
2.2.3 支持富文本(HTML)
QLabel 支持基本的 HTML 格式,比如字体颜色、加粗等:
label->setText("<b><font color='red'>加粗红色文字</font></b>");
2.2.4 常用API
| 方法 | 功能说明 |
|---|---|
setText(const QString&) | 设置显示文本 |
text() | 获取当前显示文本 |
setPixmap(const QPixmap&) | 设置显示图片 |
setAlignment(Qt::Alignment) | 设置对齐方式 |
setWordWrap(bool) | 设置是否自动换行 |
setScaledContents(bool) | 设置图片是否自动缩放 |
setStyleSheet(const QString&) | 设置样式 |
2.2.5 示例:动态更新标签内容
#include "MyWidget.h"
#include<qpushbutton.h>
#include<QHBoxLayout>
#include<qlabel.h>
MyWidget::MyWidget(QWidget *parent)
: QWidget(parent)
{
setFixedSize(300, 100);
setWindowTitle("常用控件");
QLabel* label = new QLabel;
label->setText("初始内容");
QPushButton* button = new QPushButton("更新标签");
QHBoxLayout* hlay = new QHBoxLayout(this);
hlay->addWidget(label);
hlay->addWidget(button);
connect(button, &QPushButton::clicked, [=](){
label->setText("<b><font color='red'>内容已更新</font></b>");
});
}
MyWidget::~MyWidget()
{}
点击运行,初始界面如下:
点击【更新标签】按钮,效果如下:
2.3 QLineEdit(单行文本输入框)
QLineEdit 是 Qt 中最常用的输入控件之一,用于接收用户输入的单行文本数据,支持输入限制、密码模式、占位文本等功能。
2.3.1 基本用法
创建一个输入框并添加到布局中:
QLineEdit* lineEdit = new QLineEdit;
layout->addWidget(lineEdit);
设置默认文本内容或提示文本:
lineEdit->setText("默认内容");
lineEdit->setPlaceholderText("请输入用户名");
2.3.2 输入限制
1.限制最大长度:
lineEdit->setMaxLength(10); // 限制输入最多10个字符
2.设置只读:
lineEdit->setReadOnly(true);
3.输入掩码(如IP地址格式):
lineEdit->setInputMask("000.000.000.000;_");
4.密码输入模式:
lineEdit->setEchoMode(QLineEdit::Password);
2.3.3 信号与交互
textChanged(const QString&):当文本变化时发出。returnPressed():用户按下回车键时发出。editingFinished():用户结束编辑时(例如按回车或失去焦点)发出。
示例:用户按下回车后打印输入内容:
connect(lineEdit, &QLineEdit::returnPressed, [=]() {
qDebug() << "用户输入:" << lineEdit->text();
});
2.3.4 常用API
| 方法 | 功能说明 |
|---|---|
setText(const QString&) | 设置文本内容 |
text() | 获取当前文本 |
setPlaceholderText(const QString&) | 设置提示占位文字 |
setEchoMode(QLineEdit::EchoMode) | 设置显示模式(如密码) |
setInputMask(const QString&) | 设置输入掩码 |
setMaxLength(int) | 设置最大字符数 |
clear() | 清空输入框内容 |
2.3.5 示例:简单表单验证
这个示例中:
- 用户在
QLineEdit中输入内容; - 点击 “提交” 按钮后程序会验证输入是否为纯数字;
- 若验证成功,则
QLabel显示“输入正确”,颜色为绿色; - 验证失败则显示“输入错误”,颜色为红色。
#include "MyWidget.h"
#include<qpushbutton.h>
#include<QVBoxLayout>
#include<qlabel.h>
#include<qlineedit.h>
#include<qdebug.h>
#include<qregularexpression.h>
MyWidget::MyWidget(QWidget *parent)
: QWidget(parent)
{
setFixedSize(300, 150);
setWindowTitle("常用控件");
QLabel* label = new QLabel;
label->setText("请输入数字:");
QLineEdit* input = new QLineEdit;
QPushButton* button = new QPushButton("提交");
QLabel* resultLabel = new QLabel;
resultLabel->setText("<font color = 'green'>等待验证</font>");
QVBoxLayout* vlay = new QVBoxLayout(this);
vlay->addWidget(label);
vlay->addWidget(input);
vlay->addWidget(button);
vlay->addWidget(resultLabel);
connect(button, &QPushButton::clicked, [=]() {
//获取输入文本
QString text = input->text();
//使用正则判断是否为纯数字
QRegularExpression re("^\\d+$"); //匹配整数
if (re.match(text).hasMatch()) {
resultLabel->setText("<font color = 'green'>输入正确</font>");
}
else {
resultLabel->setText("输入错误");
//使用样式表
resultLabel->setStyleSheet("color:red");
}
});
}
MyWidget::~MyWidget()
{}
我们验证一下:
当输入正确结果:
当输入错误数据:
3. 常用布局管理器
在 Qt 中,布局管理器用于自动管理控件的尺寸和位置,使界面在不同分辨率下保持一致性和整洁性。常见布局管理器包括:
QHBoxLayout:水平布局QVBoxLayout:垂直布局QGridLayout:栅格布局QSplitter:可调分割器布局
3.1 QHBoxLayout(水平布局)
QHBoxLayout 可以将控件横向排列:
QHBoxLayout* layout = new QHBoxLayout(this);
layout->addWidget(new QPushButton("按钮1"));
layout->addWidget(new QPushButton("按钮2"));
layout->addWidget(new QPushButton("按钮3"));
默认对齐方式:
- 控件会从左向右依次排列。
- 默认 顶部对齐,大小自动撑满高度(若布局设定在顶层)。
设置对齐方式:
layout->addWidget(new QPushButton("按钮"), 0, Qt::AlignCenter);
参数 0 表示 拉伸因子(stretch factor),用于控制控件在布局中的空间分配比例。以下是详细说明:
拉伸因子的作用
- 值为
0(默认值):- 控件不会拉伸,仅占用其**理想大小(sizeHint)**的空间。
- 示例中按钮会保持自身合适宽度,不额外扩展。
- 值大于
0:- 控件会按比例分配布局中的剩余空间。
- 例如:两个控件的拉伸因子分别为
1和2,则它们会按 1:2 的比例分配额外空间。
Qt::AlignCenter
表示对其方式:居中对齐;可用 Qt::AlignLeft, AlignRight, AlignTop, AlignBottom, AlignHCenter, AlignVCenter 等来控制控件对其方式。
示例中的 Qt::AlignCenter 仅在以下情况有效:
- 布局方向有剩余空间(如水平布局中控件总宽度小于布局宽度)。
- 控件的尺寸策略(
sizePolicy)允许拉伸。
添加弹簧:
在 Qt 布局中,addSpacerItem() 和 addStretch() 都可以用来添加弹性空间(弹簧),但它们的灵活性和使用场景有所不同。
1.addStretch():快速添加均分弹簧
作用
- 在布局中添加一个可拉伸的空白空间,自动填充剩余区域。
- 多个
addStretch()会均分剩余空间。
示例
QHBoxLayout *layout = new QHBoxLayout(this);
layout->addWidget(new QPushButton("Left"));
layout->addStretch(); // 弹簧1(占据剩余空间的1/2)
layout->addStretch(); // 弹簧2(占据剩余空间的1/2)
layout->addWidget(new QPushButton("Right"));
效果:两个按钮分别位于左右两端,中间空间被两个弹簧均分。
特点
- 简单快捷:无需手动指定大小策略。
- 均分逻辑:多个
addStretch()会按 1:1 的比例分配空间。
2.addSpacerItem():精确控制弹簧属性
作用
- 通过
QSpacerItem对象自定义弹性空间的尺寸和行为。 - 需要手动指定宽度、高度和大小策略。
参数解析
new QSpacerItem(
40, // 初始宽度(像素)
20, // 初始高度(像素)
QSizePolicy::Expanding, // 水平策略
QSizePolicy::Minimum // 垂直策略
)
大小策略(SizePolicy)
| 策略 | 含义 |
|---|---|
QSizePolicy::Fixed | 固定尺寸,不拉伸(严格遵循初始宽高) |
QSizePolicy::Minimum | 控件能缩小到最小尺寸,但不能超过初始大小 |
QSizePolicy::Maximum | 控件能扩展到最大尺寸,但不能小于初始大小 |
QSizePolicy::Expanding | 控件可自由拉伸,优先占用剩余空间 |
QSizePolicy::Preferred | 默认行为,尽量保持初始尺寸,但可适度拉伸/收缩 |
示例
setFixedSize(300, 50);
QHBoxLayout* layout = new QHBoxLayout(this);
QPushButton* btn1 = new QPushButton("Left");
btn1->setFixedWidth(50);
QPushButton* btn2 = new QPushButton("Right");
btn2->setFixedWidth(50);
layout->addWidget(btn1);
layout->addSpacerItem(new QSpacerItem(
40, // 初始宽度(像素)
20, // 初始高度(像素)
QSizePolicy::Expanding, // 水平策略
QSizePolicy::Minimum // 垂直策略
));
layout->addWidget(btn2);
3.核心区别
| 特性 | addStretch() | addSpacerItem() |
|---|---|---|
| 灵活性 | 低(自动均分) | 高(可定制尺寸和策略) |
| 使用场景 | 快速分配剩余空间 | 需要精确控制弹簧行为 |
| 代码复杂度 | 简单(一行代码) | 较复杂(需创建对象) |
| 是否支持固定尺寸 | 否 | 是(通过 Fixed 策略) |
4.何时选择哪种方式?
- 用
addStretch()当:- 只需要简单分隔控件(如工具栏按钮居中)。
- 多个弹簧需要均分空间(如左、中、右布局)。
- 用
addSpacerItem()当:- 需要固定弹簧的初始尺寸(如精确控制间距)。
- 需要非均分拉伸(如某个弹簧占 2/3 空间)。
- 需要限制垂直方向的行为(如禁止弹簧在垂直方向拉伸)。
3.2 QVBoxLayout(垂直布局)
与 QHBoxLayout 类似,但控件纵向排列:
QVBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(new QLabel("标题"));
layout->addWidget(new QLineEdit());
layout->addWidget(new QPushButton("提交"));
默认对齐方式:
- 控件从上往下排列。
- 默认 左对齐。
改变对其方式:垂直居中
layout->addWidget(new QLabel("标签"), 0, Qt::AlignHCenter);
3.3 QGridLayout(栅格布局)
QGridLayout 按网格形式排列控件,可以精确控制行列位置:
QGridLayout* layout = new QGridLayout(this);
layout->addWidget(new QLabel("账号:"), 0, 0);
layout->addWidget(new QLineEdit(), 0, 1);
layout->addWidget(new QLabel("密码:"), 1, 0);
layout->addWidget(new QLineEdit(), 1, 1);
layout->addWidget(new QPushButton("登录"), 2, 0, 1, 2); // 跨两列
默认对齐方式:
- 控件在各自的单元格中默认左上对齐;
- 可通过
setAlignment调整对齐方式。
QGridLayout::addWidget 参数详解:
标准形式:
addWidget(QWidget *widget, int row, int column, int rowSpan = 1, int columnSpan = 1, Qt::Alignment alignment = 0);
| 参数名 | 含义 |
|---|---|
widget | 要添加的控件指针(如 QLabel, QPushButton) |
row | 所在的行位置(第几行,从0开始编号) |
column | 所在的列位置(第几列,从0开始编号) |
rowSpan | 占据的行数(默认值为1,即不跨行) |
columnSpan | 占据的列数(默认值为1,即不跨列) |
alignment | 可选,设置控件对齐方式(例如 Qt::AlignCenter) |
实际效果是这样的:
为什么控件占据位置不同呢?
在 QGridLayout 中,控件默认会根据其 大小策略(size policy) 和 内容需求 自动分配空间。您观察到的现象(Label 很小而 LineEdit 很大)是由以下几个因素共同导致的:
原因分析
- Label 的默认行为:
QLabel的默认水平大小策略是QSizePolicy::Preferred,表示它会尽量保持自身内容的合适宽度(如文本"账号:"的宽度)。如果你使用设计器添加控件就能够看到其默认水平大小策略。- 不会主动拉伸占用额外空间。
- LineEdit 的默认行为:
QLineEdit的默认水平大小策略是QSizePolicy::Expanding,表示它会尽可能拉伸以填充可用空间。- 它会抢占布局中的剩余宽度。
- 栅格布局的列分配规则:
- 默认情况下,
QGridLayout的每一列会平分剩余空间。 - 如果某列包含
Expanding控件(如QLineEdit),则该列会优先扩展。
- 默认情况下,
解决方案
方法1:固定 Label 的最小宽度
QLabel *accountLabel = new QLabel("账号:");
accountLabel->setMinimumWidth(100); // 设置最小宽度
layout->addWidget(accountLabel, 0, 0);
方法2:调整列拉伸比例
// 设置第0列和第1列的拉伸因子比例为1:3
layout->setColumnStretch(0, 1); // Label列
layout->setColumnStretch(1, 3); // LineEdit列
方法3:限制 LineEdit 的最大宽度
QLineEdit *lineEdit = new QLineEdit();
lineEdit->setMaximumWidth(200); // 限制最大宽度
layout->addWidget(lineEdit, 0, 1);
方法4:统一控件大小策略
// 强制Label水平扩展(可能不美观)
QLabel *accountLabel = new QLabel("账号:");
accountLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
layout->addWidget(accountLabel, 0, 0);
自定义调整后的样式:
QGridLayout* layout = new QGridLayout(this);
// 方案1:设置Label最小宽度
QLabel* accountLabel = new QLabel("账号:");
accountLabel->setMinimumWidth(50);
QLabel* pwdLabel = new QLabel("密码:");
pwdLabel->setMinimumWidth(50);
// 方案2:调整列比例
layout->setColumnStretch(0, 1); // Label列
layout->setColumnStretch(1, 3); // LineEdit列
// 添加控件
layout->addWidget(accountLabel, 0, 0);
layout->addWidget(new QLineEdit(), 0, 1);
layout->addWidget(pwdLabel, 1, 0);
layout->addWidget(new QLineEdit(), 1, 1);
layout->addWidget(new QPushButton("登录"), 2, 0, 1, 2);
3.4 QSplitter(分裂器布局)
QSplitter 提供可拖动分隔条的布局方式
特点:
- 交互式调整:用户可拖动分割条改变控件大小
- 嵌套支持:可以嵌套使用实现复杂布局
- 方向灵活:支持水平和垂直两种布局方向
- 比例保存:可记住并恢复分割比例
创建水平分割布局
QSplitter* splitter = new QSplitter(Qt::Horizontal, this); // 水平分割
splitter->addWidget(new QTextEdit);
splitter->addWidget(new QTextEdit);
创建垂直分割布局
QSplitter* splitter = new QSplitter(Qt::Vertical, this); // 垂直分割
splitter->addWidget(new QTextEdit);
splitter->addWidget(new QTextEdit);
常用功能配置
设置初始比例
// 设置第一个控件占40%,第二个占60%
splitter->setSizes(QList<int>() << 400 << 600);
设置控件可折叠
splitter->setChildrenCollapsible(false); // 禁止完全折叠子控件
设置手柄宽度
splitter->setHandleWidth(10); // 设置分割条宽度为10像素
获取当前比例
QList<int> sizes = splitter->sizes();
qDebug() << "当前比例:" << sizes;
3.5 QWidget + 布局结合使用注意事项
在实际开发中,将 QWidget 与各种布局管理器(如 QHBoxLayout、QVBoxLayout、QGridLayout、QSplitter 等)结合使用时,有几个常见但容易忽略的细节需要注意:
1. 不要忘记将布局设置到 QWidget 上
在使用布局时,仅仅创建布局对象并添加控件是不够的,还需要将布局设置到对应的父控件上,否则布局不会生效:
QVBoxLayout* layout = new QVBoxLayout(this); // this 指向 QWidget
this->setLayout(layout); // 必不可少
如果在构造布局对象时已传入父控件(如 new QVBoxLayout(this)),则 setLayout() 可省略,但建议保留以增强可读性。
2. 控件默认尺寸行为可能因布局而异
控件的大小由布局管理器动态控制。若希望改变控件大小或行为:
- 使用
setFixedSize()固定大小; - 使用
setMinimumSize()或setMaximumSize()限制大小范围; - 使用
addStretch()或addSpacerItem()控制控件间距与拉伸行为; - 设置控件的 size policy,例如:
label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
3. 使用弹簧(stretch)或间距(spacing)优化界面美观性
addStretch()会在控件之间加入可扩展空间,常用于居中、对齐等目的;- 可以通过
layout->setSpacing()和layout->setContentsMargins()微调整体布局细节。
4. 多层嵌套布局更灵活
复杂界面常常使用嵌套布局,即某个布局中嵌套其他布局,如:
QVBoxLayout* vLayout = new QVBoxLayout;
QHBoxLayout* hLayout = new QHBoxLayout;
vLayout->addLayout(hLayout); // 嵌套
5. 避免手动使用 move() 设置位置
使用布局后应避免使用 move() 或 resize() 操作控件,因为这会与布局管理器产生冲突,导致布局错乱。
4. 综合示例:一个登录界面
使用 QGridLayout 实现表单布局(账号、密码)
使用 QHBoxLayout 和 QVBoxLayout 嵌套组织布局
使用 QLabel 显示输入状态反馈
使用 QPushButton 提交表单并验证输入
使用 QLineEdit 获取用户输入
使用 QSplitter 实现左右分栏(可伸缩)
LayoutManagement.h
#pragma once
#include <QtWidgets/QWidget>
#include <QWidget>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QSplitter>
#include <QMessageBox>
class LayoutManagement : public QWidget
{
Q_OBJECT
public:
LayoutManagement(QWidget *parent = nullptr);
~LayoutManagement();
private:
QLineEdit* userEdit;
QLineEdit* passEdit;
QLabel* resultLabel;
public slots:
void verifyInput();
};
LayoutManagement.cpp
#include "LayoutManagement.h"
LayoutManagement::LayoutManagement(QWidget *parent)
: QWidget(parent)
{
//设置固定大小
setFixedSize(400, 200);
//窗口标题
setWindowTitle("登录示例");
//创建控件
QLabel* userLabel = new QLabel("账号:");
QLabel* passLabel = new QLabel("密码:");
userEdit = new QLineEdit;
passEdit = new QLineEdit;
passEdit->setEchoMode(QLineEdit::Password);
//登录按钮
QPushButton* loginBtn = new QPushButton("登录");
resultLabel = new QLabel;
//栅格布局:账号和密码输入
QGridLayout* formLayout = new QGridLayout;
formLayout->addWidget(userLabel, 0, 0);
formLayout->addWidget (userEdit, 0, 1);
formLayout->addWidget(passLabel, 1, 0);
formLayout->addWidget (passEdit, 1, 1);
//设置拉伸比例
formLayout->setColumnStretch(0, 1);
formLayout->setColumnStretch(1, 3);
//水平布局:登录按钮
QHBoxLayout* btnLayout = new QHBoxLayout;
btnLayout->addStretch();
btnLayout->addWidget(loginBtn);
btnLayout->addStretch();
//垂直布局:组合所有部分
QVBoxLayout* mainLayout = new QVBoxLayout;
mainLayout->addStretch();
mainLayout->addLayout(formLayout);
mainLayout->addLayout(btnLayout);
mainLayout->addWidget(resultLabel);
mainLayout->addStretch();
//左侧小部件:用于展示分裂器功能
QLabel* sideLabel = new QLabel("功能区域");
sideLabel->setStyleSheet("background-color:#e0e0e0;padding:10px");
sideLabel->setAlignment(Qt::AlignCenter);
//使用QSplitter分栏
QSplitter* splitter = new QSplitter(Qt::Horizontal);
splitter->addWidget(sideLabel);
QWidget* formArea = new QWidget;
formArea->setLayout(mainLayout);
splitter->addWidget(formArea);
splitter->setStretchFactor(1, 1);
//主布局
QVBoxLayout* outerLayout = new QVBoxLayout(this);
outerLayout->addWidget(splitter);
//信号连接
connect(loginBtn, &QPushButton::clicked, this, &LayoutManagement::verifyInput);
}
void LayoutManagement::verifyInput() {
QString username = userEdit->text();
QString password = passEdit->text();
if (!username.isEmpty() && !password.isEmpty()) {
resultLabel->setText("输入有效,正在登录...");
resultLabel->setStyleSheet("color:green");
}
else {
resultLabel->setText("账号或密码不能为空");
resultLabel->setStyleSheet("color:red");
}
}
LayoutManagement::~LayoutManagement()
{}
效果演示
初始界面
简单拉伸后
正确输入
错入输入
5.总结
QWidget 是所有界面控件的基类,理解它对整个 Qt GUI 编程至关重要;
常用控件(按钮、标签、文本输入)均继承自 QWidget,使用简单直观;
布局管理器是 Qt UI 编程的核心,推荐始终使用布局而非手动定位;
使用布局时应注意控件尺寸策略、弹性空间(弹簧)、控件对齐等细节;
多层布局和信号槽联动构成 Qt UI 构建的基础,便于搭建灵活、高效的用户界面。