前言
最近在写一个小项目时,遇到了一个自己万万没有想到的一个很深很深的坑,是关于信号与槽的。信号与槽进行了连接,槽函数却没有执行?
简单的坑描述:类A与类B进行信号与槽的绑定,类B与类C进行信号与槽的绑定,最后类C与类A进行信号与槽的绑定;
最后运行发现会报错,于是换种方式绑定;
类A与类B进行信号与槽的绑定,类B与类C进行信号与槽的绑定,最后类A与类C进行信号与槽的绑定;
可以正常运行,却发现类A无法与类C进行信号与槽的绑定。
坑🕳差不多就是这个意思,看不懂没关系,下面用一个小故事展开。
坑🕳关系图:
故事:
过年了,小明到处走访亲戚,领到了一些零花钱。他想用这笔钱去购买玩具车玩,却发现手上的零花钱不够,这该怎么办呢?于是,小明将零花钱交给老妈,老妈拿出一笔钱与小明的零花钱加在一起刚好够买玩具车;于是,老妈将钱交给超市进行购买玩具车;此时问题就来了,超市应该将玩具车交个老妈,还是将玩具车交给小明呢?
正常我们写代码的思路是,为了方便,直接将玩具车交给了小明;但是这样做的话就完全正中踩中了那个坑。
正确的做法应该是,超市将玩具车交给老妈,老妈再将玩具车交给小明,因为超市和小明是没有任何关系的。
故事完!
下面将通过代码方法将过程完整奉献出来!
购买玩具车
小明类 - XiaoMing
ui文件
通过在输入框中输入小明的零花钱,然后点击去购物按钮,将钱交给老妈。
最后在Label中显示购买的玩具车。
下面只是简单列举相关代码,具体代码请看最下面全部代码处!
-
小明的.h文件中定义发射给老妈的信号
signals: void andMomGoToShopping(double _money);
-
在构造函数中将小明的信号与老妈的槽函数进行连接
this->m_pMom = new Mom(66.6); // 小明的信号与老妈的槽函数绑定 connect(this, &XiaoMing::andMomGoToShopping, m_pMom, &Mom::andSonGoShopping);
-
实现按钮槽函数,并在槽函数中发射信号
void XiaoMing::on_goShoppingBtn_clicked() { this->xiaoMing_Money = ui.lineEdit->text().toInt(); // 1.将小明的钱随信号一起发射给老妈 emit andMomGoToShopping(this->xiaoMing_Money); }
-
实现购买到玩具车后,在Label处显示
void XiaoMing::onToyCar(QString _commodity) { QString str = QString("小明买到了%1").arg(_commodity); ui.label->setText(str); }
老妈类 - Mom
-
老妈的.h文件中定义发射给超市的信号
signals: void andSonGoToShopping(double _money);
-
构造函数中将信号于超市进行连接
this->m_pSupermarket = new Supermarket; // 老妈的信号与超市的槽函数绑定 connect(this, &Mom::andSonGoToShopping, m_pSupermarket, &Supermarket::onCommodity);
-
实现与小明连接的槽函数,并发射信号给超市
void Mom::andSonGoShopping(double _money) { // 小明的钱和老妈的钱加起来,再去购物 double money = this->momMoney + _money; // 2. 老妈的钱和小明的钱加起来随信号发射给超市进行购物 emit(andSonGoToShopping(money)); }
超市类 - Supermarket
-
超市的.h文件中定义发射出去的信号
signals: void giveCommodity(QString _commodity);
-
实现与老妈连接的槽函数,并将玩具车随信号发射出去
void Supermarket::onCommodity(double _money) { // 超市收到了钱,打印提示已购买 qDebug() << "花费" << _money << "¥购买了" << this->commodity; // 3.超市将玩具车交出去 emit giveCommodity(this->commodity); }
超市交玩具车
好了,到这这里,已经单向将信号与槽连接好了,钱已经交给超市了,那么超市应该将玩具车交给谁呢?老妈?小明?下面我们每种情况都试一下。
情况一:超市将玩具车交给小明
当然,信号与槽的连接是在超市 的构造函数中进行连接!
我们在超市的类中加上小明的头文件,然后在构造函数中进行与小明信号与槽的连接,将玩具车交给小明。
this->m_pXiaoMing = new XiaoMing;
// 超市的信号与小明的槽函数进行连接
connect(this, &Supermarket::giveCommodity, m_pXiaoMing, &XiaoMing::onToyCar);
运行程序:
显然,这种方式是不行,代码中我仔细的看过了,没有任何的语法上的错误!
出现这种报错我觉得原因是:小明头文件中包含了老妈的头文件,老妈的头文件中包含了超市的头文件,最后超市的头文件包含了小明的头文件;这样就形成了三角循环,显然是有问题的。如下图。
显示这是无限包含。。。
情况二:超市将玩具车交给小明
当然,信号与槽的连接是在小明 的构造函数中进行连接!
现在换种方式绑定,在小明的构造函数进行超市的信号与小明的槽函数进行绑定。首先将之前的绑定代码注释掉,然后进行下面代码的绑定。
我们在小明的类中加上超市的头文件,然后在构造函数中进行与超市信号与槽的连接,将玩具车交给小明。
this->m_pSupermarket = new Supermarket;
// 超市的信号与小明的槽函数进行绑定
connect(m_pSupermarket, &Supermarket::giveCommodity, this, &XiaoMing::onToyCar);
运行程序:
发现可以正常运行,但是,输入框中输入钱后单击按钮Label却没有反应;我们在超市类的槽函数中,购买玩具车后会输出一句话:
可以看到玩具车是已经购买成功的了,该槽函数是已经执行的了。问题就出在了超市与小明信号与槽的连接上!
如下图:
具体关系就是这样,但是我并不知道问题出在了那里!
其实情况一和情况二都有相同的问题,就是超市都与小明扯上了关系,我觉得这是主要触发问题的地方,如下图:
正确的做法:超市将玩具车交给老妈,老妈再将玩具车交给小明。
因为再怎么说,玩具车是老妈拿钱去超市买的,超市没理由将玩具车交给小明。
情况三:正确的做法
超市将玩具车交给老妈,老妈再将玩具车交给小明。
将之前错误的操作代码都注释掉。
-
在老妈类里加都一个信号,然后再构造函数中,将超市的信号与老妈的信号进行绑定;当超市触发该信号,随之而然的触发老妈的信号。
signals: void giveCommodity(QString _commodity); 构造函数中的操作: // 超市的信号与老妈的信号进绑定 connect(m_pSupermarket, &Supermarket::giveCommodity, this, &Mom::giveCommodity);
-
在小明的构造函数中将老妈的信号与小明的槽函数进行连接即可
// 老妈的信号与小明的槽函数进行绑定 connect(m_pMom, &Mom::giveCommodity, this, &XiaoMing::onToyCar);
运行程序:
可以看到已经完全没有问题了,玩具车已经经由老妈之手交到了小明的手上!
下面将奉上全部代码!
全部代码
代码全由VS2017编写!
XiaoMing.h
#pragma once
#include <QtWidgets/QWidget>
#include "ui_XiaoMing.h"
#include "Mom.h"
//#include "Supermarket.h"
class XiaoMing : public QWidget {
Q_OBJECT
public:
XiaoMing(QWidget *parent = Q_NULLPTR);
signals:
void andMomGoToShopping(double _money);
public slots:
void onToyCar(QString _commodity);
private slots:
void on_goShoppingBtn_clicked();
private:
Ui::XiaoMingClass ui;
double xiaoMing_Money;
Mom *m_pMom;
//Supermarket *m_pSupermarket;
};
XiaoMing.cpp
#include "XiaoMing.h"
#pragma execution_character_set("utf-8") // qt支持显示中文
XiaoMing::XiaoMing(QWidget *parent) : QWidget(parent) {
ui.setupUi(this);
this->xiaoMing_Money = 0.0;
this->m_pMom = new Mom(66.6);
// 小明的信号与老妈的槽函数绑定
connect(this, &XiaoMing::andMomGoToShopping, m_pMom, &Mom::andSonGoShopping);
//this->m_pSupermarket = new Supermarket;
超市的信号与小明的槽函数进行绑定
//connect(m_pSupermarket, &Supermarket::giveCommodity, this, &XiaoMing::onToyCar);
// 老妈的信号与小明的槽函数进行绑定
connect(m_pMom, &Mom::giveCommodity, this, &XiaoMing::onToyCar);
}
void XiaoMing::on_goShoppingBtn_clicked() {
this->xiaoMing_Money = ui.lineEdit->text().toInt();
// 1.将小明的钱随信号一起发射给老妈
emit andMomGoToShopping(this->xiaoMing_Money);
}
void XiaoMing::onToyCar(QString _commodity) {
QString str = QString("小明买到了%1").arg(_commodity);
ui.label->setText(str);
}
Mom.h
#pragma once
#include <qobject.h>
#include "Supermarket.h"
class Mom : public QObject {
Q_OBJECT
public:
Mom(double _money, QObject *parent = Q_NULLPTR);
signals:
void andSonGoToShopping(double _money);
void giveCommodity(QString _commodity);
public slots:
void andSonGoShopping(double _money);
private:
double momMoney;
Supermarket *m_pSupermarket;
};
Mom.cpp
#include "Mom.h"
Mom::Mom(double _money, QObject *parent) : QObject(parent) {
this->momMoney = _money;
this->m_pSupermarket = new Supermarket;
// 老妈的信号与超市的槽函数绑定
connect(this, &Mom::andSonGoToShopping, m_pSupermarket, &Supermarket::onCommodity);
// 超市的信号与老妈的信号进绑定
connect(m_pSupermarket, &Supermarket::giveCommodity, this, &Mom::giveCommodity);
}
void Mom::andSonGoShopping(double _money) {
// 小明的钱和老妈的钱加起来,再去购物
double money = this->momMoney + _money;
// 2. 老妈的钱和小明的钱加起来随信号发射给超市进行购物
emit(andSonGoToShopping(money));
}
Supermarket.h
#pragma once
#include <qobject.h>
//#include "XiaoMing.h"
class Supermarket : public QObject {
Q_OBJECT
public:
Supermarket(QObject *parent = Q_NULLPTR);
signals:
void giveCommodity(QString _commodity);
public slots:
void onCommodity(double _money);
private:
QString commodity;
//XiaoMing *m_pXiaoMing;
};
Supermarket.cpp
#include "Supermarket.h"
#include <qdebug.h>
#pragma execution_character_set("utf-8") // qt支持显示中文
Supermarket::Supermarket(QObject *parent) : QObject(parent) {
this->commodity = "玩具车";
//this->m_pXiaoMing = new XiaoMing;
超市的信号与小明的槽函数进行连接
//connect(this, &Supermarket::giveCommodity, m_pXiaoMing, &XiaoMing::onToyCar);
}
void Supermarket::onCommodity(double _money) {
// 超市收到了钱,打印提示已购买
qDebug() << "花费" << _money << "¥购买了" << this->commodity;
// 3.超市将玩具车交出去
emit giveCommodity(this->commodity);
}
例子比较简单,但意义非凡!
总结
这小坑🕳是本人在写小项目时遇到的,花了一天时间将其原因找出来了。虽然不是什么大风大浪,但是也确确实实是我们偶尔会遇到的。
没有必要去看上面那些代码,只需要结合我画的图片,将其思路过程理解就好了,下次你们遇到类似的问题,就知道怎么去解决啦!