走进Qt--常用控件1&布局

1,066 阅读15分钟

1. 前言

在使用 Qt 进行图形界面开发时,控件和布局是构建界面的两大核心基础。控件(如按钮、标签、输入框)负责与用户交互,而布局则负责合理安排控件的位置和大小,确保界面美观且响应式良好。

本章将围绕三个常见基础控件(QPushButtonQLabelQLineEdit)以及四种常用布局(水平布局、垂直布局、网格布局、分裂器布局)展开讲解,重点通过示例展示其核心用法、信号槽连接方式和常见应用场景。


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()
{}

点击运行,初始界面如下:

image.png

点击【更新标签】按钮,效果如下:

image.png

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()
{}

我们验证一下:

当输入正确结果:

image.png

当输入错误数据:

image.png


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),用于控制控件在布局中的空间分配比例。以下是详细说明:

拉伸因子的作用

  1. 值为 0(默认值)
    • 控件不会拉伸,仅占用其**理想大小(sizeHint)**的空间。
    • 示例中按钮会保持自身合适宽度,不额外扩展。
  2. 值大于 0
    • 控件会按比例分配布局中的剩余空间
    • 例如:两个控件的拉伸因子分别为 12,则它们会按 1:2 的比例分配额外空间。

Qt::AlignCenter

表示对其方式:居中对齐;可用 Qt::AlignLeft, AlignRight, AlignTop, AlignBottom, AlignHCenter, AlignVCenter 等来控制控件对其方式。

示例中的 Qt::AlignCenter 仅在以下情况有效:

  1. 布局方向有剩余空间(如水平布局中控件总宽度小于布局宽度)。
  2. 控件的尺寸策略(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);

image.png

3.核心区别
特性addStretch()addSpacerItem()
灵活性低(自动均分)高(可定制尺寸和策略)
使用场景快速分配剩余空间需要精确控制弹簧行为
代码复杂度简单(一行代码)较复杂(需创建对象)
是否支持固定尺寸是(通过 Fixed 策略)
4.何时选择哪种方式?
  1. addStretch() 当:
    • 只需要简单分隔控件(如工具栏按钮居中)。
    • 多个弹簧需要均分空间(如左、中、右布局)。
  2. 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)

实际效果是这样的:

image.png

为什么控件占据位置不同呢?

QGridLayout 中,控件默认会根据其 大小策略(size policy)内容需求 自动分配空间。您观察到的现象(Label 很小而 LineEdit 很大)是由以下几个因素共同导致的:

原因分析

  1. Label 的默认行为
    • QLabel 的默认水平大小策略是 QSizePolicy::Preferred,表示它会尽量保持自身内容的合适宽度(如文本"账号:"的宽度)。如果你使用设计器添加控件就能够看到其默认水平大小策略。
    • 不会主动拉伸占用额外空间。
  2. LineEdit 的默认行为
    • QLineEdit 的默认水平大小策略是 QSizePolicy::Expanding,表示它会尽可能拉伸以填充可用空间。
    • 它会抢占布局中的剩余宽度。
  3. 栅格布局的列分配规则
    • 默认情况下,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);

image.png

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 与各种布局管理器(如 QHBoxLayoutQVBoxLayoutQGridLayoutQSplitter 等)结合使用时,有几个常见但容易忽略的细节需要注意:

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 实现表单布局(账号、密码)

使用 QHBoxLayoutQVBoxLayout 嵌套组织布局

使用 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()
{}

效果演示

初始界面

image.png

简单拉伸后

image.png

正确输入

image.png

错入输入

image.png

5.总结

QWidget 是所有界面控件的基类,理解它对整个 Qt GUI 编程至关重要;

常用控件(按钮、标签、文本输入)均继承自 QWidget,使用简单直观;

布局管理器是 Qt UI 编程的核心,推荐始终使用布局而非手动定位;

使用布局时应注意控件尺寸策略、弹性空间(弹簧)、控件对齐等细节;

多层布局和信号槽联动构成 Qt UI 构建的基础,便于搭建灵活、高效的用户界面。