Qt自定义信号槽

Qt自定义信号槽,在控制台程序中实现(qt5)
代码如下:

//!!! Qt5
#include <QObject>
////////// newspaper.h
class Newspaper : public QObject
{
Q_OBJECT
public:
Newspaper(const QString & name) :
m_name(name)
{
}
void send()
{
emit newPaper(m_name);
}
signals:
void newPaper(const QString &name);
private:
QString m_name;
};
////////// reader.h
#include <QObject>
#include <QDebug>
class Reader : public QObject
{
Q_OBJECT
public:
Reader() {}
void receiveNewspaper(const QString & name)
{
qDebug() << "Receives Newspaper: " << name;
}
};
////////// main.cpp
#include <QCoreApplication>
#include "newspaper.h"
#include "reader.h"
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
Newspaper newspaper("Newspaper A");
Reader reader;
QObject::connect(&newspaper, &Newspaper::newPaper,
&reader, &Reader::receiveNewspaper);
newspaper.send();
return app.exec();
}

(上述 头文件是在编译过程中自己添加)

当我们运行上面的程序时,会看到终端输出 Receives Newspaper: Newspaper A 这样的字样。

这段代码放在了三个文件,分别是 newspaper.h,reader.h 和 main.cpp。为了减少文件数量,
可以把 newspaper.h 和 reader.h 都放在 main.cpp 的 main() 函数之前吗?答案是,可以,
但是需要有额外的操作。具体问题,我们在下面会详细说明。
首先看 Newspaper 这个类。这个类继承了 QObject 类。只有继承了 QObject 类的类,才具
有信号槽的能力。所以,为了使用信号槽,必须继承 QObject。凡是 QObject 类(不管是直接
子类还是间接子类),都应该在第一行代码写上 Q_OBJECT。不管是不是使用信号槽,都应该
添加这个宏。这个宏的展开将为我们的类提供信号槽机制、国际化机制以及 Qt 提供的不基于
C++ RTTI 的反射能力。因此,如果你觉得你的类不需要使用信号槽,就不添加这个宏,就是错
误的。其它很多操作都会依赖于这个宏。注意,这个宏将由 moc(我们会在后面章节中介绍 moc。
这里你可以将其理解为一种预处理器,是比 C++ 预处理器更早执行的预处理器。) 做特殊处
理,不仅仅是宏展开这么简单。moc 会读取标记了 Q_OBJECT 的头文件,生成以 moc_ 为前
缀的文件,比如 newspaper.h 将生成 moc_newspaper.h。你可以到构建目录查看这个文件,
看看到底增加了什么内容。注意,由于 moc 只处理头文件中的标记了 Q_OBJECT 的类声明,
不会处理 cpp 文件中的类似声明。因此,如果我们的 Newspaper 和 Reader 类位于 main.cpp
中,是无法得到 moc 的处理的。解决方法是,我们手动调用 moc 工具处理 main.cpp,并且
将 main.cpp 中的 include “newspaper.h” 改为 include “moc_newspaper.h” 就可以了。不过,
这是相当繁琐的步骤,为了避免这样修改,我们还是将其放在头文件中。许多初学者会遇到莫名
其妙的错误,一加上 Q_OBJECT 就出错,很大一部分是因为没有注意到这个宏应该放在头文件
中。
Newspaper 类的 publicprivate 代码块都比较简单,只不过它新加了一个 signals。signals
块所列出的,就是该类的信号。信号就是一个个的函数名,返回值是 void(因为无法获得信号
的返回值,所以也就无需返回任何值),参数是该类需要让外界知道的数据。信号作为函数名,
不需要在 cpp 函数中添加任何实现(我们曾经说过,Qt 程序能够使用普通的 make 进行编译。
没有实现的函数名怎么会通过编译?原因还是在 moc,moc 会帮我们实现信号函数所需要的函
数体,所以说,moc 并不是单纯的将 Q_OBJECT 展开,而是做了很多额外的操作)。
Newspaper 类的 send() 函数比较简单,只有一个语句 emit newPaper(m_name);。emit 是 Qt
对 C++ 的扩展,是一个关键字(其实也是一个宏)。emit 的含义是发出,也就是发出 newPaper()
信号。感兴趣的接收者会关注这个信号,可能还需要知道是哪份报纸发出的信号?所以,我们将
实际的报纸名字 m_name 当做参数传给这个信号。当接收者连接这个信号时,就可以通过槽函
数获得实际值。这样就完成了数据从发出者到接收者的一个转移。
Reader 类更简单。因为这个类需要接受信号,所以我们将其继承了 QObject,并且添加了
Q_OBJECT 宏。后面则是默认构造函数和一个普通的成员函数。Qt 5 中,任何成员函数、static
函数、全局函数和 Lambda 表达式都可以作为槽函数。与信号函数不同,槽函数必须自己完成
实现代码。槽函数就是普通的成员函数,因此也会受到 publicprivate 等访问控制符的影响。
(我们没有说信号也会受此影响,事实上,如果信号是 private 的,这个信号就不能在类的外面
连接,也就没有任何意义。)
main() 函数中,我们首先创建了 Newspaper 和 Reader 两个对象,然后使用
QObject::connect() 函数。这个函数我们上一节已经详细介绍过,这里应该能够看出这个连接的
含义。然后我们调用 Newspaper 的 send() 函数。这个函数只有一个语句:发出信号。由于我
们的连接,当这个信号发出时,自动调用 reader 的槽函数,打印出语句。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值