QT中connect、disconnect和信号槽实现原理

connect

connect用于连接信号和槽函数,但是我们经常忽略最后一个参数,本人在写代码时遇到了bug,选择一个符号进行多次标绘,再选择其他符号时,会将上一次选择的符号也进行标绘,主要时connect最后一个参数搞的鬼。

当conncet最后一个参数不写时,connect多少次,槽函数就执行多少次,本人遇到的bug需要将最后一个参数设为Qt::UniqueConnection,避免多次连接,只连接一次。

Qt::AutoConnection: 自动。一般不写就默认是这个,使用这个值会根据实际情况去判断,如果sender和receiver在同一个线程,则自动使用Qt::DirectConnection,如果不在一个线程,则自动使用Qt::QueuedConnection。
Qt::DirectConnection:直连。槽函数会在信号发送的时候直接被调用,槽函数运行于信号发送者所在线程。效果看上去就像是直接在信号发送位置调用了槽函数。这个在多线程环境下比较危险,可能会造成奔溃。
Qt::QueuedConnection:队列连接。槽函数在控制回到接收者所在线程的事件循环时被调用,槽函数运行于信号接收者所在线程。发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用。多线程环境下一般用这个。
Qt::BlockingQueuedConnection:阻塞队列连接。槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。
Qt::UniqueConnection:唯一连接。这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免了重复连接。

diconnect

disconnect(myObject, nullptr, nullptr, nullptr)等同于myObject->disconnect();, 断开myObject的所有信号与任何槽的关联;
disconnect(myObject, SIGNAL(mySignal()), nullptr, nullptr) 等同于myObject>disconnect(SIGNAL(mySignal()));断开myObject得mySignal信号与任何槽的关联;
disconnect(myObject, nullptr, myReceiver, nullptr);等同于myObject->disconnect(myReceiver);断开信号发送者myObject和信号接收者myReceiver之间的关联;
QObject::disconnect(lineEdit, &QLineEdit::textChanged, label, &QLabel::setText);断开特定信号发送者和接收者之间的关联

其中conncet必须与disconnect成对出现,不然,disconncet之后就连接不上信号

信号槽实现原理

1、信号与槽的实现是借助了Qt 的元对象系统,元对象系统有一个元对象编译器,程序编译之前会有一个预处理过程,预处理将一个类/对象中的信号,槽的字符串值分别保存在一个容器中,可能是字符串或者其他的有序容器。

2、现在有两个容器,比如string_sig,保存了所有的信号的字符串值;string_slot保存了所有的槽函数的字符窜值。

3、每次调用connect()函数建立信号与槽的连接,就是将给定信号与给定的接收方及其槽函数存储在发送方对象的映射容器中,比如multimap,建立起信号与槽的一一对应关系。

4、发射信号其实就是调用信号函数(信号就是一种函数),根据该信号的索引在multimap中找到对应的槽函数,在调用槽函数即可。

5、信号与槽的本质就是函数的调用。

例子:

#include <map>
#include<iostream>
# define slot
# define siginal protected
# define emit
using namespace std;
class Object;

struct MetaObject//元对象 每个Object有一个,保存自己拥有的信号和槽函数
{
	const char* sig_names;
	const char* slts_names;
	static void active(Object* sender, int idx);
};

struct Connection//保存接收方及其序号
{
	Object* receiver;
	int method;
};

typedef std::multimap<int, Connection> ConnectionMap;
//<信号,接收方>每个Object有一个,保存自己的信号发出时需要通知的接收方。每个信号的接收方可能不止一个,用multimap
typedef std::multimap<int, Connection>::iterator ConnectionMapIt;

static int find_string(const char* str, const char* substr)
{
	if (strlen(str) < strlen(substr))
		return -1;
	int idx = 0;
	int len = strlen(substr);
	bool start = true;
	const char* pos = str;
	while (*pos)
	{
		if (start && !strncmp(pos, substr, len) && pos[len] == '\n')
			return idx;
		start = false;
		if (*pos == '\n')
		{
			idx++;
			start = true;
		}
		pos++;
	}
	return -1;
}

class Object
{
	friend class MetaObject;
public:
	Object() {};
	virtual ~Object() {};
	static void connect(Object* sender, const char* sig, Object* receiver, const char* slt);
	void testSignal() { emit sig1(); }//测试函数

siginal:
	void sig1() //信号
	{
		cout << "信号发出" << endl;
		MetaObject::active(this, 0);
	}

	public slot:
	void slot1() { cout << "槽函数执行" << endl; }//槽函数

private:
	static MetaObject meta;//保存自己拥有的信号和槽函数
	ConnectionMap connections;//multimap<int, Connection>,
	void metacall(int idx);//根据key去查询对应的槽函数
};

static const char sig_names[] = "sig1\n";//保存对象中所有信号的字符串的容器
static const char slts_names[] = "slot1\n";//保存对象中所有槽函数字符串的容器
MetaObject Object::meta = { sig_names, slts_names };//初始化元对象

void Object::metacall(int idx)//根据key去查询对应的槽函数
{
	switch (idx)
	{
	case 0:
		slot1();
		break;
	default:
		break;
	};
}

void Object::connect(Object* sender, const char* sig, Object* receiver, const char* slt)//静态成员函数  connect建立信号与槽的连接
{
	int sig_idx = find_string(sender->meta.sig_names, sig);//查询有没有这个信号和槽函数
	int slt_idx = find_string(receiver->meta.slts_names, slt);
	if (sig_idx == -1 || slt_idx == -1)
	{
		perror("signal or slot not found!");
	}
	else //添加
	{
		Connection c = { receiver, slt_idx };//保存接收方及其槽函数id
		sender->connections.insert(std::pair<int, Connection>(sig_idx, c));//发送方保存接收方信息
	}
}

void MetaObject::active(Object* sender, int idx)//信号发出,根据发出者找出它的接收方
{
	ConnectionMapIt it;
	std::pair<ConnectionMapIt, ConnectionMapIt> ret;
	ret = sender->connections.equal_range(idx);//equal_range主要是找在multimap中的key相等的value,也是一个迭代器
	for (it = ret.first; it != ret.second; ++it)
	{
		Connection c = (*it).second;//获取之前保存的接受方消息
		c.receiver->metacall(c.method);//接收方执行之前绑定的槽函数
	}
}

int main()
{
	Object obj1, obj2;
	Object::connect(&obj1, "sig1", &obj2, "slot1");//连接信号
	obj1.testSignal();
	system("pause");
	return 0;
}
  • 9
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

vegetablesssss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值