目录
  1. 1. Qt学习笔记6-信号槽
  2. 2. 有重载的信号
  3. 3. 带有默认参数的槽函数
Qt学习笔记6-信号槽

Qt学习笔记6-信号槽

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//!!! Qt5
#include <QObject>

////////// newspaper.h
class Newspaper : public QObject
{
Q_OBJECT
public:
Newspaper(const QString & name) :
m_name(name)
{
}

void send() const
{
emit newPaper(m_name);
}

signals:
void newPaper(const QString &name) const;

private:
QString m_name;
};

////////// reader.h
#include <QObject>
#include <QDebug>

class Reader : public QObject
{
Q_OBJECT
public:
Reader() {}

void receiveNewspaper(const QString & name) const
{
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();
}

main()函数中,我们使用connect()函数将newspaper对象的newPaper()信号与reader对象的receiveNewspaper()槽函数联系起来。当newspaper发出这个信号时,reader相应的槽函数就会自动被调用。这里我们使用了取址操作符,取到Newspaper::newPaper()信号的地址,同样类似的取到了Reader::receiveNewspaper()函数地址。编译器能够利用这两个地址,在编译期对这个连接操作进行检查,如果有个任何错误(包括对象没有这个信号,或者信号参数不匹配等),编译时就会发现

有重载的信号

如果信号有重载,比如我们向Newspaper类增加一个新的信号:

1
void newPaper(const QString &name, const QDate &date);

此时如果还是按照前面的写法,编译器会报出一个错误:由于这个函数(注意,信号实际也是一个普通的函数)有重载,因此不能用一个取址操作符获取其地址。

对此,我们也给出了一个解决方案,使用一个函数指针来指明到底是哪一个信号:

1
2
3
void (Newspaper:: *newPaperNameDate)(const QString &, const QDate &) = &Newspaper::newPaper;
QObject::connect(&newspaper, newPaperNameDate,
&reader, &Reader::receiveNewspaper);

这样,我们使用了函数指针newspaperNameDate声明一个带有QStringQDate两个参数,返回值是 void 的函数,将该函数作为信号,与Reader::receiveNewspaper()槽连接起来。这样,我们就回避了之前编译器的错误。归根结底,这个错误是因为函数重载,编译器不知道要取哪一个函数的地址,而我们显式指明一个函数就可以了。同时我们还提供了另一种写法:

1
2
3
4
QObject::connect(&newspaper,
static_cast<void (Newspaper:: *)(const QString &, const QDate &)>(&Newspaper::newPaper),
&reader,
&Reader::receiveNewspaper);

带有默认参数的槽函数

Qt 允许信号和槽的参数数目不一致:槽函数的参数数目可以比信号的参数少。这是因为,我们信号的参数实际是作为一种返回值。正如普通的函数调用一样,我们可以选择忽略函数返回值,但是不能使用一个并不存在的返回值。如果槽函数的参数数目比信号的多,在槽函数中就使用到这些参数的时候,实际这些参数并不存在(因为信号的参数比槽的少,因此并没有传过来),函数就会报错。这种情况往往有两个原因:一是槽的参数就是比信号的少,此时我们可以像前面那种写法直接连接。另外一个原因是,信号的参数带有默认值。比如:

1
void QPushButton::clicked(bool checked = false)

然而,有一种情况,槽函数的参数可以比信号的多,那就是槽函数的参数带有默认值。比如,我们的NewspaperReader有下面的代码:

1
2
3
4
5
// Newspaper
signals:
void newPaper(const QString &name);
// Reader
void receiveNewspaper(const QString &name, const QDate &date = QDate::currentDate());

虽然Reader::receiveNewspaper()的参数数目比Newspaper::newPaper()多,但是由于Reader::receiveNewspaper()后面一个参数带有默认值,所以该参数不是必须提供的。但是,如果你按照前面的写法,比如如下的代码

1
2
3
4
QObject::connect(&newspaper,
static_cast<void (Newspaper:: *)(const QString &)>(&Newspaper::newPaper),
&reader,
static_cast<void (Reader:: *)(const QString &, const QDate & =QDate::currentDate())>(&Reader::receiveNewspaper));

就会得到一个断言错误:

1
The slot requires more arguments than the signal provides.

我们不能在函数指针中使用函数参数的默认值。这是 C++ 语言的限制:参数默认值只能使用在直接地函数调用中。当使用函数指针取其地址的时候,默认参数是不可见的!

当然,此时你可以选择 Qt 4 的连接语法。如果你还是想使用 Qt 5 的新语法,目前的办法只有一个:Lambda 表达式。不要担心你的编译器不支持 Lambda 表达式,因为在你使用 Qt 5 的时候,能够支持 Qt 5 的编译器都是支持 Lambda 表达式的。于是,我们的代码就变成了:

1
2
3
QObject::connect(&newspaper,
static_cast<void (Newspaper:: *)(const QString &)>(&Newspaper::newPaper),
[=](const QString &name) { /* Your code here. */ });
文章作者: XyLan
文章链接: https://blog.xylan.cn/2023/04/26/Qt%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B06-%E4%BF%A1%E5%8F%B7%E6%A7%BD/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 XyLan
打赏
  • 微信
  • 支付寶