目录
1. 信号和槽概述
信号和槽的关系
2. 标准信号槽使用
标准信号/槽
示例:
3. 自定义信号槽使用
自定义信号
自定义槽
示例:
1. 信号和槽概述
信号和槽是一种事件驱动的通信机制,广泛应用于Qt框架的事件处理、GUI编程、网络通信等方面。信号和槽机制实现了对象之间的通信和交互,使程序更加灵活、易于维护和扩展。
信号和槽机制的基本概念:
- 信号:是一种特殊的函数,用于在对象状态发生改变时通知其他对象。信号可以包含参数,但是它们不返回任何值。
- 槽:是一种接收信号的函数,用于响应特定事件。槽函数可以被连接到一个或多个信号,并且可以有自己的参数和返回值。
- 信号槽连接:通过connect函数将信号连接到一个或多个槽,可以实现对象之间的通信。当信号发射时,与该信号连接的槽将被调用。
在Qt框架中,信号和槽机制是基于元对象系统实现的。元对象系统是一种在运行时动态处理对象信息的机制,可以实现信号槽的自动连接和断开连接,避免了手动管理信号槽连接的繁琐过程。
信号和槽机制具有以下优点:
- 松耦合:通过信号和槽机制,对象之间的通信不需要知道彼此的具体实现,只需要知道信号和槽的接口即可实现通信,大大降低了对象之间的耦合度。
- 可扩展性:信号和槽机制支持多个槽连接同一个信号,也支持一个槽连接多个信号。这种机制使得程序的功能可以很容易地进行扩展,增加新的信号和槽即可实现对功能进行扩展。
- 线程安全:信号和槽机制基于元对象系统实现,可以很好地处理线程安全问题,支持在不同的线程中进行信号和槽的连接和调用,保证了程序的健壮性。
总的来说,信号和槽机制是Qt框架中非常重要的机制之一,它的灵活性和可扩展性在GUI编程、事件处理、网络通信等方面都得到了广泛的应用。
信号和槽的关系
信号和槽是一种连接对象之间的机制,是Qt框架中用于事件处理的重要机制。信号是一种特殊的函数,用于在对象状态发生改变时通知其他对象。槽是一种接收信号的函数,用于响应特定事件。信号和槽的关系可以概括为如下几个方面:
1. 信号和槽之间是一种一对多的关系:一个信号可以连接多个槽函数,一个槽函数也可以连接多个信号。这种关系可以实现对象之间的灵活通信。
2. 信号和槽之间通过connect函数进行连接:在需要实现信号和槽之间的连接时,可以使用Qt中提供的connect函数进行连接。该函数的参数包括信号的发射者、信号名称、槽函数的接收者和槽函数名称等信息。
3. 信号和槽之间是异步的:连接的信号和槽之间的函数调用是异步的,也就是说,当信号被发射时,与该信号连接的槽函数不会立即执行,而是等待事件循环。这种机制可以实现多线程通信。
4. 信号和槽之间是基于元对象系统的:在Qt框架中,信号和槽的实现是基于元对象系统(Meta-Object System)实现的。元对象系统是一种在运行时动态处理对象信息的机制,可以实现信号槽的自动连接和断开连接,避免了手动管理信号槽连接的繁琐过程。
总的来说,信号和槽是一种非常重要的机制,在Qt框架中广泛应用于事件处理、GUI编程、网络通信等方面。信号和槽机制可以帮助开发者实现对象之间的通信和交互,使程序更加灵活、易于维护和扩展。
连接信号和槽的connect()
函数原型如下, 其中PointerToMemberFunction
是一个指向函数地址的指针:
QMetaObject::Connection QObject::connect(
const QObject *sender, PointerToMemberFunction signal,
const QObject *receiver, PointerToMemberFunction method,
Qt::ConnectionType type = Qt::AutoConnection);
参数:
- sender: 发出信号的对象
- signal: 属于sender对象, 信号是一个函数, 这个参数的类型是函数
指针, 信号函数地址
- receiver: 信号接收者
- method: 属于receiver对象, 当检测到sender发出了signal信号,
receiver对象调用method方法,信号发出之后的处理动作
// 参数 signal 和 method 都是函数地址, 因此简化之后的 connect() 如下:
connect(const QObject *sender, &QObject::signal,
const QObject *receiver, &QObject::method);
使用connect()进行信号槽连接的注意事项:
connect函数相对于做了信号处理动作的注册
调用conenct函数的sender对象的信号并没有产生, 因此receiver对象的
method也不会被调用method槽函数本质是一个回调函数, 调用的时机是信号产生之后, 调用是Qt框架来执行的
connect中的sender和recever两个指针必须被实例化了, 否则conenct不会成功
2. 标准信号槽使用
标准信号/槽
在Qt提供的很多标准类中都可以对用户触发的某些特定事件进行检测, 因此当用户做了这些操作之后, 事件被触发类的内部就会产生对应的信号, 这些信号都是Qt类内部自带的, 因此称之为标准信号。
同样的,在Qt的很多类内部为我了提供了很多功能函数,并且这些函数也可以作为触发的信号的处理动作,有这类特性的函数在Qt中称之为标准槽函数。
示例(点击窗口关闭按钮,关闭窗口):
先在新建项目的mainwindow.ui界面中拖进Push Button按钮,并对其值进行修改为“closeBtn”
然后在mainwindow.cpp中编写以下代码,并运行
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->closeBtn,&QPushButton::clicked,this,&MainWindow::close);
}
MainWindow::~MainWindow()
{
delete ui;
}
点击“关闭窗口”按钮,则窗口关闭
3. 自定义信号槽使用
Qt框架提供的信号槽在某些特定场景下是无法满足我们的项目需求的,因此我们还设计自己需要的的信号和槽,同样还是使用connect()对自定义的信号槽进行连接。
如果想要在QT类中自定义信号槽, 需要满足一些条件, 并且有些事项也需要注意:
要编写新的类并且让其继承Qt的某些标准类
这个新的子类必须从QObject类或者是QObject子类进行派生
在定义类的头文件中加入 Q_OBJECT 宏
// 在头文件派生类的时候,首先像下面那样引入Q_OBJECT宏:
class MyMainWindow : public QWidget
{
Q_OBJECT
......
}
自定义信号
在Qt中信号的本质是事件, 但是在框架中也是以函数的形式存在的, 只不过信号对应的函数只有声明, 没有定义。如果Qt中的标准信号不能满足我们的需求,可以在程序中进行信号的自定义,当自定义信号对应的事件产生之后,认为的将这个信号发射出去即可(其实就是调用一下这个信号函数)。
自定义信号的要求和注意事项:
信号是类的成员函数
返回值必须是 void 类型
信号的名字可以根据实际情况进行指定
参数可以随意指定, 信号也支持重载
信号需要使用 signals 关键字进行声明, 使用方法类似于public等关键字
信号函数只需要声明, 不需要定义(没有函数体实现)
在程序中发射自定义信号: 发送信号的本质就是调用信号函数
习惯性在信号函数前加关键字: emit, 但是可以省略不写
emit只是显示的声明一下信号要被发射了, 没有特殊含义
底层 emit == #define emit
// 举例: 信号重载
// Qt中的类想要使用信号槽机制必须要从QObject类派生(直接或间接派生都可以)
class Test : public QObject
{
Q_OBJECT
signals:
void testsignal();
// 参数的作用是数据传递, 谁调用信号函数谁就指定实参
// 实参最终会被传递给槽函数
void testsignal(int a);
};
自定义槽
槽函数就是信号的处理动作,在Qt中槽函数可以作为普通的成员函数来使用。如果标准槽函数提供的功能满足不了需求,可以自己定义槽函数进行某些特殊功能的实现。自定义槽函数和自定义的普通函数写法是一样的。
下边给大家阐述一下, 自定义槽的要求和注意事项:
-
返回值必须是 void 类型
-
槽也是函数, 因此也支持重载
-
槽函数需要指定多少个参数, 需要看连接的信号的参数个数
-
槽函数的参数是用来接收信号传递的数据的, 信号传递的数据就是信号的参数
- 举例:
- 信号函数: void testsig(int a, double b);
- 槽函数: void testslot(int a, double b);
- 总结:
- 槽函数的参数应该和对应的信号的参数个数, 从左到右类型依次对应
- 信号的参数可以大于等于槽函数的参数个数 == 信号传递的数据被忽略了
- 信号函数: void testsig(int a, double b);
- 槽函数: void testslot(int a);
- 举例:
-
Qt中槽函数的类型是多样的
Qt中的槽函数可以是
类的成员函数
、全局函数
、静态函数
、Lambda表达式
(匿名函数) -
槽函数可以使用关键字进行声明: slots (Qt5中slots可以省略不写)
- public slots:
- private slots: –> 这样的槽函数不能在类外部被调用
- protected slots: –> 这样的槽函数不能在类外部被调用
// 槽函数书写格式举例
// 类中的这三个函数都可以作为槽函数来使用
class Test : public QObject
{
public:
void testSlot();
static void testFunc();
public slots:
void testSlot(int id);
};
示例:
先添加一个新文件
Base class选择QObject,点击下一步,完成
再按上述方法添加一个新文件
在ui界面中添加一个PushButton按钮,命名为need
在test.h文件中signals一个need函数:
#ifndef TEST_H
#define TEST_H
#include <QObject>
class Test : public QObject
{
Q_OBJECT
public:
explicit Test(QObject *parent = nullptr);
signals:
void need();
};
#endif // TEST_H
在test01.h文件中声明一个what_need函数:
#ifndef TEST01_H
#define TEST01_H
#include <QObject>
class Test01 : public QObject
{
Q_OBJECT
public:
explicit Test01(QObject *parent = nullptr);
//槽函数
public slots:
//槽函数
void what_need();
};
#endif // TEST01_H
在test01.cpp中添加定义
#include "test01.h"
#include <QDebug>
Test01::Test01(QObject *parent)
: QObject{parent}
{
}
void Test01::what_need()
{
qDebug()<<"你需要什么";
}
在mainwindow.h中#include test和test01两个头文件,并且添加need按钮的槽函数,定义两个私有变量m_whatnd和m_need
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include "test.h"
#include "test01.h"
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
//添加need按钮的槽函数
void needSlot();
private:
Ui::MainWindow *ui;
Test01* m_whatnd;
Test* m_need;
};
#endif // MAINWINDOW_HS
在mainwindow.cpp中用connect函数连接信号和信号槽
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_whatnd = new Test01;
m_need = new Test;
connect(m_need,&Test::need,m_whatnd,&Test01::what_need);
connect(ui->need,&QPushButton::clicked,this,&MainWindow::needSlot);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::needSlot()
{
//发射自定义信号
emit m_need->need();
}
运行结果(每次点击need按钮时,应用程序输出“你需要什么”):
总结:本文讲述了Qt中的信号和信号槽的使用和相关函数,演示了标准信号槽和自定义信号槽该如何使用