Qt信号槽机制与UI设计完全指南:从基础原理到实战应用

目录

  • 前言
  • 一、信号槽
    • [1.1 传参](#1.1 传参)
    • [1.2 Qt信号与槽的对应关系](#1.2 Qt信号与槽的对应关系)
  • 二、Designer
  • [三、Layout 布局](#三、Layout 布局)
    • [3.1 基础用法](#3.1 基础用法)
    • [3.2 打破布局](#3.2 打破布局)
    • [3.3 贴合窗口](#3.3 贴合窗口)
    • [3.4 伸展器(Spacer)](#3.4 伸展器(Spacer))
    • [3.5 嵌套布局](#3.5 嵌套布局)
  • 四、ui指针
  • 五、QWidget
  • [六、QLabel 标签使用指南](#六、QLabel 标签使用指南)
  • 总结

前言

本篇文章,我们将继续探讨Qt的学习,解锁现代应用开发的无限可能。上一篇文章,我们已经对信号槽进行了初步的探讨,本篇文章我们将会书接上文,继续来探讨信号槽的使用。


一、信号槽

首先补充上篇文章的两个小知识点

需要注意的是,调用connect函数之前,发射者与接收者对象必须均创建完成。

把connect的函数名称改为disconnect,参数不变可以断开信号槽连接。另外,发射者或接收者对象销毁后之前连接的信号槽也会自动断开。

cpp 复制代码
disconnect(sender, SIGNAL(...), receiver, SLOT(...));

1.1 传参

为了理解传参,我们先来做一个例子
【例子】 点击按钮,按钮显示点击的次数。

●text : QString

按钮显示的文字

cpp 复制代码
// int → QString
QString QString::​number(int n)            [static]
cpp 复制代码
dialog.h

#ifndef DIALOG_H
#define DIALOG_H

// 包含必要的头文件
#include <QDialog>       // Qt对话框基类
#include <QPushButton>   // Qt按钮控件

// 自定义对话框类,继承自QDialog
class Dialog : public QDialog
{
    Q_OBJECT  // Qt宏,启用信号槽机制

public:
    // 构造函数
    // @param parent 父窗口指针,默认为nullptr
    Dialog(QWidget *parent = 0);
    
    // 析构函数
    ~Dialog();

private:
    QPushButton* btn;  // 按钮控件指针
    int count;         // 记录按钮被点击的次数

private slots:
    // 按钮点击的槽函数
    void btnClickedSlot();
};

#endif // DIALOG_H
cpp 复制代码
dialog.cpp

// 包含对话框类的头文件
#include "dialog.h"

// 对话框类的构造函数
Dialog::Dialog(QWidget *parent)
    : QDialog(parent), count(0) // 使用构造初始化列表初始化父类和计数器count
{
    // 设置对话框窗口大小为300x300像素
    resize(300, 300);
    
    // 创建一个显示"0"的按钮,并指定父对象为当前对话框
    btn = new QPushButton("0", this);
    
    // 将按钮移动到窗口中央位置
    // 计算方式:窗口宽度/2 - 按钮宽度/2,窗口高度/2 - 按钮高度/2
    btn->move(300/2 - btn->width()/2, 300/2 - btn->height()/2);

    // 连接按钮的clicked信号到本类的btnClickedSlot槽函数
    connect(btn, SIGNAL(clicked()),
            this, SLOT(btnClickedSlot()));
}

// 按钮点击的槽函数实现
void Dialog::btnClickedSlot()
{
    // 计数器自增1
    count++;
    
    // 将整型计数器转换为QString类型
    QString text = QString::number(count);
    
    // 更新按钮上显示的文本为当前计数值
    btn->setText(text);
}

// 对话框类的析构函数
Dialog::~Dialog()
{
    // 释放按钮对象的内存
    delete btn;
}

下面为了讲解信号槽传参,强行多增加一个信号槽的理解,本质上信号槽传参是为了解决后期不同对象之间的传参问题。

cpp 复制代码
dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QPushButton>

// 自定义对话框类,继承自QDialog
class Dialog : public QDialog
{
    Q_OBJECT  // 启用Qt的元对象系统(信号槽机制等)

public:
    Dialog(QWidget *parent = 0);  // 构造函数,parent参数指定父窗口
    ~Dialog();  // 析构函数

private:
    QPushButton* btn;  // 按钮指针
    int count;  // 记录按钮被点击的次数

private slots:
    // 槽函数声明
    void btnClickedSlot();  // 处理按钮点击事件的槽函数
    void countSlot(int);  // 接收int类型参数的槽函数

signals:
    // 信号声明
    void countSignal(int);  // 自定义信号,携带一个int类型参数
};

#endif // DIALOG_H
cpp 复制代码
dialog.cpp

// 包含对话框类的头文件
#include "dialog.h"

/**
 * @brief Dialog类的构造函数
 * @param parent 父窗口指针,默认为nullptr
 */
Dialog::Dialog(QWidget *parent)
    : QDialog(parent), count(0) // 使用构造初始化列表初始化父类和计数器count
{
    // 设置对话框窗口大小为300x300像素
    resize(300, 300);
    
    // 创建一个显示"0"的按钮,并指定父对象为当前对话框
    btn = new QPushButton("0", this);
    
    // 将按钮移动到窗口中央位置
    // 计算方式:窗口宽度/2 - 按钮宽度/2,窗口高度/2 - 按钮高度/2
    btn->move(300/2 - btn->width()/2, 300/2 - btn->height()/2);

    // 连接按钮的clicked信号到btnClickedSlot槽函数
    // 当按钮被点击时,会触发btnClickedSlot函数
    connect(btn, SIGNAL(clicked()),
            this, SLOT(btnClickedSlot()));
            
    // 连接自定义信号countSignal到countSlot槽函数
    // 这是一个自连接,当countSignal信号发射时,会触发countSlot函数
    connect(this, SIGNAL(countSignal(int)),
            this, SLOT(countSlot(int)));
}

/**
 * @brief 按钮点击的槽函数
 * 当按钮被点击时,发射带参数的自定义信号
 */
void Dialog::btnClickedSlot()
{
    // 先递增count,然后发射countSignal信号,携带count值作为参数
    emit countSignal(++count);
}

/**
 * @brief 处理计数信号的槽函数
 * @param count 接收到的计数值
 * 将计数值显示在按钮上
 */
void Dialog::countSlot(int count)
{
    // 将整型count转换为QString类型
    QString text = QString::number(count);
    
    // 设置按钮文本为当前计数值
    btn->setText(text);
}

/**
 * @brief Dialog类的析构函数
 * 释放动态分配的按钮对象
 */
Dialog::~Dialog()
{
    // 删除按钮对象,释放内存
    delete btn;
}

1.2 Qt信号与槽的对应关系

1.2.1一对多关系

概念

同一个信号可以同时连接到多个槽

槽函数也是成员函数,可以直接调用
优化技巧: 可以把多个槽函数放在一个槽函数中调用,将一对多优化为一对一结构

cpp 复制代码
dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>          // 包含QDialog头文件,用于创建对话框窗口
#include <QPushButton>      // 包含QPushButton头文件,用于创建按钮控件
#include <QDebug>           // 包含QDebug头文件,用于调试输出

// Dialog类继承自QDialog,表示一个对话框窗口
class Dialog : public QDialog
{
    Q_OBJECT  // 宏定义,启用Qt的元对象系统(信号槽机制等)

public:
    Dialog(QWidget *parent = 0);  // 构造函数,parent参数指定父窗口
    ~Dialog();                    // 析构函数

private:
    QPushButton* btn;   // 私有成员变量:按钮1
    QPushButton* btn2;  // 私有成员变量:按钮2

private slots:
    void mySlot1();  // 私有槽函数1(用于响应信号)
    void mySlot2();  // 私有槽函数2
    void mySlot3();  // 私有槽函数3
};

#endif // DIALOG_H
cpp 复制代码
dialog.cpp

#include "dialog.h"

// 对话框类的构造函数
Dialog::Dialog(QWidget *parent)
    : QDialog(parent)  // 调用父类QDialog的构造函数
{
    resize(300,300);  // 设置对话框大小为300x300像素
    
    // 创建"一对多"按钮并设置位置
    btn = new QPushButton("一对多",this);
    btn->move(100,100);
    
    // 创建"一对一"按钮并设置位置
    btn2 = new QPushButton("一对一",this);
    btn2->move(100,150);

    // 信号与槽连接:一对多示例
    // 将btn的clicked()信号同时连接到mySlot1()和mySlot2()两个槽函数
    connect(btn,SIGNAL(clicked()),
            this,SLOT(mySlot1()));
    connect(btn,SIGNAL(clicked()),
            this,SLOT(mySlot2()));

    // 信号与槽连接:一对一示例
    // 将btn2的clicked()信号连接到mySlot3()槽函数
    connect(btn2,SIGNAL(clicked()),
            this,SLOT(mySlot3()));
}

// 槽函数1:输出"A"
void Dialog::mySlot1()
{
    qDebug() << "A";
}

// 槽函数2:输出"B"
void Dialog::mySlot2()
{
    qDebug() << "B";
}

// 槽函数3:调用mySlot1和mySlot2
void Dialog::mySlot3()
{
    // 直接调用mySlot1和mySlot2
    mySlot1();
    mySlot2();
}

// 析构函数:释放按钮对象
Dialog::~Dialog()
{
    delete btn;   // 释放"一对多"按钮
    delete btn2;  // 释放"一对一"按钮
}

1.2.2 多对一关系

概念

多个信号可以同时连接到一个槽

可以使用QObject::sender()函数判断当前是哪个发射者触发的槽函数

该函数返回发射者对象的地址(protected成员函数)

cpp 复制代码
// 可以返回发射者对象的地址
QObject * QObject::​sender() const        [protected]

示例代码

cpp 复制代码
dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>       // 包含QDialog头文件,用于创建对话框窗口
#include <QPushButton>   // 包含QPushButton头文件,用于创建按钮控件
#include <QDebug>        // 包含QDebug头文件,用于调试输出

// Dialog类继承自QDialog,表示一个对话框窗口
class Dialog : public QDialog
{
    Q_OBJECT  // 必须的宏,用于支持Qt的信号和槽机制

public:
    Dialog(QWidget *parent = 0);  // 构造函数,parent参数指定父窗口
    ~Dialog();                    // 析构函数

private:
    QPushButton* btn;   // 私有成员变量:按钮1
    QPushButton* btn2;  // 私有成员变量:按钮2

private slots:
    void mySlot();      // 私有槽函数,用于处理按钮点击等信号
};

#endif // DIALOG_H
cpp 复制代码
dialog.cpp

#include "dialog.h"

// 对话框类的构造函数
Dialog::Dialog(QWidget *parent)
    : QDialog(parent)  // 调用父类QDialog的构造函数
{
    resize(300,300);  // 设置对话框窗口大小为300x300像素
    
    // 创建第一个按钮,显示文本为"多对一",父对象为当前对话框
    btn = new QPushButton("多对一",this);
    btn->move(100,100);  // 将按钮移动到(100,100)位置
    
    // 创建第二个按钮,显示文本为"多对一",父对象为当前对话框
    btn2 = new QPushButton("多对一",this);
    btn2->move(100,150);  // 将按钮移动到(100,150)位置

    // 多对一信号槽连接
    // 将btn的clicked()信号连接到当前对象的mySlot()槽
    connect(btn,SIGNAL(clicked()),
            this,SLOT(mySlot()));
    // 将btn2的clicked()信号连接到当前对象的mySlot()槽
    connect(btn2,SIGNAL(clicked()),
            this,SLOT(mySlot()));
}

// 自定义槽函数
void Dialog::mySlot()
{
    // 通过sender()判断是哪个按钮触发的信号
    if(sender() == btn)  // 如果是btn按钮
    {
        qDebug() << "随便输出个什么A";  // 输出调试信息A
    }
    else if(sender() == btn2)  // 如果是btn2按钮
    {
        qDebug() << "随便输出个什么B";  // 输出调试信息B
    }
}

// 对话框类的析构函数
Dialog::~Dialog()
{
    delete btn;   // 释放btn按钮的内存
    delete btn2;  // 释放btn2按钮的内存
}

二、Designer

定义

Qt Designer 是 Qt 框架自带的可视化界面设计工具,用于快速创建 GUI 界面。

核心功能

通过拖拽组件设计界面,自动生成 ui 文件(XML 格式)。

与 Qt Creator 深度集成,双击 .ui 文件即可启动 Designer。

与代码的关系

Designer 的所有操作均可通过 C++ 代码实现,二者功能等价,但C++效率更高。

生成的 ui 文件会被编译为对应的 C++ 头文件(如 ui_xxx.h),供程序调用。

Designer 界面功能分布

组件工具箱:包含按钮、文本框等可拖拽控件。

属性编辑器:调整选中组件的属性(如尺寸、文本)。

信号/槽编辑器:可视化配置组件的事件响应。

布局工具:快速对齐和排列组件。

使用流程示例

  • 在 Qt Creator 中创建项目,添加 .ui 文件。
  • 双击 .ui 文件启动 Designer,拖拽组件设计界面。
  • 保存后,Qt 自动生成 XML 代码,编译时转换为 C++ 代码。
  • 在业务逻辑中通过 ui->组件名 访问界面元素。
    -

三、Layout 布局

3.1 基础用法

定义: 布局是一个透明的容器,内部组件按预设规则自动排列。
主要类型:

垂直布局:组件垂直排列(纵向)。

水平布局:组件水平排列(横向)。

两者除方向外,其他属性基本一致。

常用属性:

间距(Spacing)、对齐方式(Alignment)、边距(Padding)等。


3.2 打破布局

作用: 移除外层布局但保留内部组件。
操作步骤:

选中目标布局。

点击工具栏的 "打破布局" 按钮。
注意: 直接删除布局会连带删除内部组件,需谨慎操作。


3.3 贴合窗口

作用:使最外层布局自适应窗口大小变化。

操作步骤:

选中窗口(或顶层布局)。

点击工具栏的 "贴合窗口" 按钮。

原理:在窗口内嵌套一个自动伸缩的布局。

取消方式:与打破布局相同(选中后点击"打破布局")。


3.4 伸展器(Spacer)

功能: 在布局中填充空白区域,调整组件间距。

应用场景:

实现组件靠左/右/顶部/底部对齐。

动态分配剩余空间。


3.5 嵌套布局

用途:通过多层布局嵌套实现复杂界面设计。

关键点:

内层布局整体被视为外层布局的一个子组件。

可混合使用垂直、水平布局及伸展器。


四、ui指针


五、QWidget

QWidget 是 Qt 框架中所有用户界面组件的基类,提供了基础的属性和功能,其派生类(如按钮、窗口、标签等)会继承这些特性。以下是其常用属性和功能的整理:


六、QLabel 标签使用指南

基本特性

用途:仅用于文字和图片信息展示,不可交互操作。

图片资源管理规范
开发步骤:

准备图片

选择分辨率/大小适中的图片

命名要求:纯英文,不含中文字符

存放位置:项目工作目录

创建资源文件

右键项目 → "Add New File"

选择文件类型 → 命名资源文件 → 完成创建

管理资源文件

右键资源文件 → "Open in Editor"

首次使用需设置虚拟路径前缀

添加图片到资源文件中

使用资源

Designer中需先点击资源按钮

支持C++代码和Designer两种使用方式

代码操作图片
核心函数:

静态图片处理

cpp 复制代码
// 构造函数(虚拟路径参数)
QPixmap::QPixmap(const QString &fileName)
// 图片缩放
// width: 目标宽度 | height: 目标高度
// aspectRatioMode: 缩放模式(默认忽略比例)
// transformMode: 插值方法(默认快速变换)
QPixmap QPixmap::scaled(int width, int height, 
                       Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio,
                       Qt::TransformationMode transformMode = Qt::FastTransformation) const

动态图片处理

cpp 复制代码
// 动图构造函数(必须堆内存分配,参数为虚拟路径)
QMovie::QMovie(const QString &fileName)
// 开始播放
void QMovie::start() [slot]

代码示例
QLabel 代码压缩包


总结

本文全面解析了Qt框架中的信号槽机制与UI设计核心知识,首先详细讲解了信号槽的连接与断开、参数传递以及一对多/多对一的对应关系实现,通过计数器案例演示了信号传参的实际应用;接着系统介绍了Qt Designer可视化设计工具的功能布局、使用流程以及与代码的对应关系;然后深入剖析了布局管理的各类技巧,包括基础布局、打破布局、贴合窗口、伸展器使用和嵌套布局等实用方法;最后讲解了ui指针的访问方式和QWidget/QLabel组件的核心功能,特别提供了图片资源管理规范和动静态图片处理的代码示例,为开发者提供了从原理到实践的完整Qt开发指南。

相关推荐
用户805533698035 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner5 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz10 天前
QML Hello World 入门示例
qt
xcyxiner13 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner13 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner14 天前
DicomViewer (添加模型类)3
qt
xcyxiner14 天前
DicomViewer (目录调整) 2
qt
xcyxiner14 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00616 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术16 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript