Qt的信号槽基本用法总结

信号槽是Qt中最重要的机制,现将信号槽的基本用法总结如下。

1、定义

信号槽类似于软件设计模式中的观察者模式,(观察者模式是一种对象行为模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。)被观察者发出的信号(signal),观察者收到自己注册监听signal,就通过槽(slot)关联的槽函数function实现动作操作。

信号槽优点:

(1)类型安全

信号的参数类型、参数个数需要和槽函数的参数类型和参数个数一致。槽函数的个数可以少于信号的参数个数,但缺少的参数必须是信号参数的最后一个或最后几个。

(2)松散耦合

信号发送者不需要知道发出的信号被哪个对象的槽函数接收,槽函数也不需要知道哪些信号关联了自己,Qt的信号槽机制保证了信号与槽函数的调用。支持信号槽机制的类或者父类必须继承于QObject。

(3)效率

信号槽增强了对象间通信的灵活性,同时损失了一些性能,通过信号调用的槽函数比直接调用速度慢约10倍(因为需要定位信号接收者;遍历所有关联;编组/解组传递的参数;多线程时,信号可能需要排队),这种调用速度对性能要求不是非常高的场景是可以忽略的,是满足绝大部分场景。

注:

信号:没有访问权限修饰符,是公有的,没有函数体定义返回类型是 void,信号在 moc 自动产生。

槽函数:需要有访问权限修饰:public,private,protected。

2、关联信号槽的方法

通过connect关联信号和槽函数

(1)Qt5以前的connect有以下几种:

bool connect(const QObject *, const char *,
             const QObject *, const char *,
             Qt::ConnectionType);
 
bool connect(const QObject *, const QMetaMethod &,
             const QObject *, const QMetaMethod &,
             Qt::ConnectionType);
 
bool connect(const QObject *, const char *,
             const char *,
             Qt::ConnectionType) const

        Qt4使用了SIGNAL和SLOT这两个宏,将信号和槽的函数名转换成了字符串。注意,不能将全局函数或者 Lambda 表达式传入connect()。使用字符串导致了Qt4有以下缺点:一旦出现连接不成功的情况,Qt 4 是没有编译错误的(因为一切都是字符串,编译期是不检查字符串是否匹配),而是在运行时给出错误。这无疑会增加程序的不稳定性。

第一种是比较常用的,可简单描述如下:

connect(obj1, SIGNAL(fun1(param1, param2,...)), obj2, SLOT(fun2(param1,...)));

优点:对所有控件都适用。

缺点:书写繁琐,槽函数必须在slot标签下。在程序编译阶段,程序会将函数以字符串的形式进行链接,程序不会检查信号/槽函数是否存在,只有在运行期间才会验证是否正确

(2)Qt5写法有以下几种


QMetaObject::Connection connect(const QObject *, const char *,
                                const QObject *, const char *,
                                Qt::ConnectionType);
 
QMetaObject::Connection connect(const QObject *, const QMetaMethod &,
                                const QObject *, const QMetaMethod &,
                                Qt::ConnectionType);
 
QMetaObject::Connection connect(const QObject *, const char *,
                                const char *,
                                Qt::ConnectionType) const;
 
QMetaObject::Connection connect(const QObject *, PointerToMemberFunction,
                                const QObject *, PointerToMemberFunction,
                                Qt::ConnectionType)
 
QMetaObject::Connection connect(const QObject *, PointerToMemberFunction,
                                Functor);

相对Qt5以前,第四种和第五种是新增的。

第四种比较常用,可简单描述如下:

connect(obj1, &ClassA::fun1, obj2, &ClassB::fun2);

优点:书写简便,编译期间就会检查信号与槽是否存在,参数类型检查,Q_OBJECT是否存在,槽函数不在限定必须是slot,可以是普通的函数、类的普通成员函数、lambda函数。

缺点:函数重载,有可能会造成程序的困扰,不知道该具体链接哪个

解决:a)可以使用Qt5以前的写法。

          b)或者使用函数指针,eg:

 connect(comboBox, static_cast(&QComboBox::activated),
      [=](int index){ /* ... */ });

关于connect几种函数的详细介绍可参考Qt的信号槽机制介绍(含Qt5与Qt4的差异对比)_花园王国---Garden kingdom-CSDN博客

3、信号槽的性质

(1)一个信号可以关联多个槽函数。

connect(obj1,SIGNAL(signal1),obj1,SLOT(fun1()));
connect(obj1,SIGNAL(signal1),obj1,SIGNAL(fun2()));
connect(obj1,SIGNAL(signal1),obj1,SIGNAL(fun3()));

注:Qt5以后,支持按照建立连接的顺序触发槽函数。即如果一个信号连接到多个槽,则在发出信号时,这些槽的激活顺序与建立连接的顺序相同。If a signal is connected to several slots, the slots are activated in the same order in which the connections were made, when the signal is emitted.

Qt5以前:信号发射时,与之相关联的槽的执行顺序将是随机的,且顺序不能指定。执行结果:

(2)多个信号可以关联一个槽函数。

connect(obj1,SIGNAL(signal1),obj1,SLOT(fun1()));
connect(obj2,SIGNAL(signal2),obj1,SIGNAL(fun1()));
connect(obj3,SIGNAL(signal3),obj1,SIGNAL(fun1()));
//槽函数可以根据sender()->objectName()来判断是哪个信号触发的槽函数

(3)一个信号可以关联另一个信号。

connect(obj1,SIGNAL(signal1),obj2,SIGNAL(signal2));

 (4)一个信号关联多个信号

connect(obj1,SIGNAL(signal1),obj2,SIGNAL(signal21));
connect(obj1,SIGNAL(signal1),obj2,SIGNAL(signal22));
connect(obj1,SIGNAL(signal1),obj2,SIGNAL(signal23));

demo如下:

///.h///
#pragma once

#include 
#include "ui_SignalSlot.h"
#include  
class SignalSlot : public QWidget
{
    Q_OBJECT

public:
    SignalSlot(QWidget *parent = Q_NULLPTR);

public slots:

	//响应btn1
	void	OnBtn1();

	void	OnSetTextStyle();

	void	OnSetWindowTitile();

	//响应自定义信号
	void	OnSignalMsg();

	//响应btn2 clicked
	void	OnBtn2Signal();

	//响应自定义信号
	void	OnCustomSignal(int x,int y);

	//自定义信号
signals:
	void signalMsg1();

	void  signalMsg2();

	void  signalMsg3();

	void  signalMsg(int x,int y);
private:
    Ui::SignalSlotClass ui;
	QTimer *m_pTimer;
};



.cpp
#include "SignalSlot.h"
#include 
#include 
SignalSlot::SignalSlot(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);

	///一个信号关联多个槽函数 start//
	connect(ui.pbtn1,SIGNAL(clicked()),this,SLOT(OnBtn1()));
	connect(ui.pbtn1, SIGNAL(clicked()), this, SLOT(OnSetTextStyle()));
	connect(ui.pbtn1, SIGNAL(clicked()), this, SLOT(OnSetWindowTitile()));
	/一个信号关联多个槽函数 end

	///多个信号关联一个槽函数 start//
	connect(this, SIGNAL(signalMsg1()), this, SLOT(OnSignalMsg()()));
	connect(this, SIGNAL(signalMsg2()), this, SLOT(OnSignalMsg()()));
	connect(this, SIGNAL(signalMsg3()), this, SLOT(OnSignalMsg()()));
	///多个信号关联一个槽函数 end//


	///一个信号关联多个信号 start//
	///一个信号关联一个信号 start//
	connect(this, SIGNAL(signalMsg3()), ui.pbtn2, SIGNAL(clicked()));
	connect(ui.pbtn2, SIGNAL(clicked()), this, SLOT(OnBtn2Signal()));
	///一个信号关联一个信号 end//

	connect(this, SIGNAL(signalMsg3()), ui.pbtn2, SIGNAL(pressed()));
	connect(ui.pbtn2, &QPushButton::clicked, this,&SignalSlot::OnBtn2Signal);
	///一个信号关联多个信号 end//



 ///带参信号槽/	
connect(this, SIGNAL(signalMsg(int,int)), this, SLOT(OnCustomSignal(int,int)));

///信号槽lambda写法/
	m_pTimer = new QTimer(this);
	connect(m_pTimer, &QTimer::timeout, [=]() {
	ui.lineEdit3->setText(QString::fromLocal8Bit("你好,中国"));
	ui.lineEdit4->setText(QString::fromLocal8Bit("你好,中国"));
	qDebug() << "11111" << endl;
	m_pTimer->stop();
	});
	m_pTimer->start(1000*10);//启动定时器
}

void SignalSlot::OnBtn1()
{
	ui.lineEdit1->setText(QString::fromLocal8Bit("你好,世界!"));
	qDebug() << "OnBtn1" <setStyleSheet(" QLineEdit{ background: yellow }");
	qDebug() << "OnSetTextStyle" << endl;
}

void SignalSlot::OnSetWindowTitile()
{
	this->setWindowTitle(QString::fromLocal8Bit("测试窗口"));
	qDebug() << "OnSetWindowTitile" << endl;
	emit signalMsg1();
	emit signalMsg2();
	emit signalMsg3();
}

void SignalSlot::OnSignalMsg()
{
	qDebug() << sender()->objectName() << endl;
}

void SignalSlot::OnBtn2Signal()
{
	ui.lineEdit2->setText(QString::fromLocal8Bit("你好,中国"));
	qDebug() << "OnBtn2Signal"<< endl;
}

void SignalSlot::OnCustomSignal(int x, int y)
{
	qDebug() << "x" << x << ",y" << y << endl;
}

执行结果

 Qt的信号槽基本用法总结_第1张图片Qt的信号槽基本用法总结_第2张图片

 

备注:

lambda用法可参考:C++中的Lambda表达式详解_NickWei的博客-CSDN博客_lambda表达式c++

lambda函数定义如下:

[capture](parameters) mutable ->return-type{statement}

(1)[capture]:捕捉列表。捕捉列表总是出现在Lambda函数的开始处。实际上,[]是Lambda引出符。编译器根据该引出符判断接下来的代码是否是Lambda函数。捕捉列表能够捕捉上下文中的变量以供Lambda函数使用;

(2)(parameters):参数列表。与普通函数的参数列表一致。如果不需要参数传递,则可以连同括号“()”一起省略;

(3)mutable:mutable修饰符。默认情况下,Lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略(即使参数为空);

(4)->return-type:返回类型。用追踪返回类型形式声明函数的返回类型。我们可以在不需要返回值的时候也可以连同符号”->”一起省略。此外,在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导;

(5){statement}:函数体。内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获的变量。

与普通函数最大的区别是,除了可以使用参数以外,Lambda函数还可以通过捕获列表访问一些上下文中的数据。具体地,捕捉列表描述了上下文中哪些数据可以被Lambda使用,以及使用方式(以值传递的方式或引用传递的方式)。语法上,在“[]”包括起来的是捕捉列表,捕捉列表由多个捕捉项组成,并以逗号分隔。捕捉列表有以下几种形式:

(1)[var]表示值传递方式捕捉变量var;

(2)[=]表示值传递方式捕捉所有父作用域的变量(包括this);
(3)[&var]表示引用传递捕捉变量var;
(4)[&]表示引用传递方式捕捉所有父作用域的变量(包括this);
(5)[this]表示值传递方式捕捉当前的this指针。

你可能感兴趣的:(Qt,qt,开发语言)