Qt信号与槽连接
信号与槽连接
qt5格式:
connect(pointer1, pointer2, pointer3, pointer4);
pointer1:指向发送信号的对象的指针
pointer2:发送信号的对象所对应的类的成员函数的指针
pointer3:接收信号的对象的指针
pointer4:接收信号的对象所对应对象的槽函数指针
其中pointer2,和pointer4都是函数指针,必须使用类名::成员函数名,并且pointer2和pointer4函数参数类型一致,pointer4会忽略pointer2传递的多余参数,pointer4可以是普通函数(无需public slots:修饰)。
总结下来就是:
- 信号:只需声明无需实现,且没有返回值;
- 槽函数:QT5类中任意成员函数,静态函数,全局函数,lambda表达式,槽函数可以有返回值,但只有在作为普通函数调用时才能接受,信号触发调用时无法接受;
- 信号与槽使用函数指针形式传入时格式必须是类名::成员函数名(信号或槽);
- 信号和槽的参数列表顺序必须一致;
- 信号和槽的参数个数可以不一致,但要满足信号的参数个数大于等于槽函数参数个数,槽函数会忽略信号传递的多余参数。
例如:
MainWidget.h文件:
class MainWidget : public QWidget
{
Q_OBJECT
public:
explicit MainWidget(QWidget *parent = 0);
QPushButton * pushButton;
SubWidget * subWidget;
signals:
public slots:
void switchWinSlot();
};
MainWidget.cpp文件:
MainWidget::MainWidget(QWidget *parent) : QWidget(parent), pushButton(new QPushButton(this))
{
pushButton->setText(QStringLiteral("切换至子窗口"));
subWidget = new SubWidget;
connect(pushButton, &QPushButton::clicked, this, &MainWidget::switchWinSlot);
connect(subWidget, &SubWidget::switchWin, this, &MainWidget::switchWinSlot);
subWidget->setWindowTitle("sub");
this->setWindowTitle("parent");
this->resize(400, 300);
subWidget->resize(400, 300);
}
void MainWidget::switchWinSlot()
{
this->setVisible(!this->isVisible());
this->subWidget->setVisible(!subWidget->isVisible());
}
另外
- 一个信号可以连接多个槽,槽函数的执行顺序是随机的,无法控制
- 多个信号可以连接一个槽
- 一个信号可以连接另一个信号(这两个信号可以是不同对象的信号)这里对第三点举例
- 信号和槽连接成功后,可以断开连接disconnect
subwidget.h文件:
class SubWidget : public QWidget
{
Q_OBJECT
public:
explicit SubWidget(QWidget *parent = 0);
QPushButton * pushButton;
signals:
void switchWin();
public slots:
};
subwidget.cpp文件:
SubWidget::SubWidget(QWidget *parent) : QWidget(parent)
{
pushButton = new QPushButton(this);
pushButton->setText(QStringLiteral("切换至父窗口"));
connect(pushButton, &QPushButton::clicked, this, &SubWidget::switchWin);
}
此例子是子窗口产生clicked信号时会同时产生自定义的switchWin信号,用来实现主窗口与子窗口之间的切换
此外
信号和槽函数可以进行重载,利用Qt5格式连接重载的信号和槽时,应该使用函数指针变量对重载信号或槽进行区分
subwidget.h文件:
class SubWidget : public QWidget
{
Q_OBJECT
public:
explicit SubWidget(QWidget *parent = 0);
QPushButton * pushButton;
signals:
void switchWin();
void switchWin(bool);
public slots:
};
subwidget.cpp文件:
SubWidget::SubWidget(QWidget *parent) : QWidget(parent)
{
pushButton = new QPushButton(this);
pushButton->setText(QStringLiteral("切换至父窗口"));
void (SubWidget::* pfun)() = &SubWidget::switchWin;
void (SubWidget::* pfun2)(bool) = &SubWidget::switchWin;
connect(pushButton, &QPushButton::clicked, this, pfun);
connect(pushButton, &QPushButton::clicked, this, pfun2);
}
上述函数指针涉及到类成员函数的函数指针,详见:
http://blog.csdn.net/xiaoyink/article/details/79439676
这是我们对SubClass的switchWin进行了重载,所以也要对mainwidget.cpp中connect连接switchWin信号的代码进行修改,这里我们用另一种方法,使用static_cast<>运算符对函数指针进行类型转换来区别switchWin的重载;
mainwidget.cpp修改如下:
#include "mainwidget.h"
#include <QPushButton>
#include "subwidget.h"
MainWidget::MainWidget(QWidget *parent) : QWidget(parent), pushButton(new QPushButton(this))
{
pushButton->setText(QStringLiteral("切换至子窗口"));
subWidget = new SubWidget;
connect(pushButton, &QPushButton::clicked, this, &MainWidget::switchWinSlot);
//connect(subWidget, &SubWidget::switchWin, this, &MainWidget::switchWinSlot);//修改之前
connect(subWidget, static_cast<void(SubWidget::*)()>(&SubWidget::switchWin), this, &MainWidget::switchWinSlot);
subWidget->setWindowTitle("sub");
this->setWindowTitle("parent");
this->resize(400, 300);
subWidget->resize(400, 300);
}
void MainWidget::switchWinSlot()
{
subWidget->setVisible(!subWidget->isVisible());
this->setVisible(!this->isVisible());
}
上述涉及到的函数指针和重载函数的结合使用可以参考:
http://blog.csdn.net/xiaoyink/article/details/79441277
也可以利用Qt4中的信号与槽连接机制对重载进行区分,但不推荐使用,因为qt4使用宏定义会将函数名转化成字符串,所以并不会提供编译的错误检查,并且,SLOT()宏要求所有的槽函数必须使用xxx(public) slots:关键字进行修饰,qt4格式如下:
connect(sender, SIGNAL(signal), receiver, SLOT(slot));
最后
connect函数还可以使用lambda表达式,在pro文件中加入CONFIG+=c++11,上述connect语句可以换成
connect(pushButton, &QPushButton::clicked,
[=](){
emit switchWin();
});
或者
connect(pushButton, &QPushButton::clicked,
[this](){
emit this->switchWin();
});
关于lambda表达式的用法详见:
http://blog.csdn.net/xiaoyink/article/details/79351350
补充
connect()函数可以自己设置第5个参数:
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method,
Qt::ConnectionType type = Qt::AutoConnection)
最后一个参数取值及其含义如下:
Qt::AutoConnection (默认)
Qt::DirectConnection
Qt::QueuedConnection
Qt::BlockingQueuedConnection
Qt::UniqueConnection
Qt::AutoConnection:如果信号发送者和接受者在同一个线程,则选用Qt::DirectConnection,如果不在同一个线程,则使用Qt::QueuedConnection做参数;
Qt::DirectConnection:信号发送后,槽函数立即执行,且会将槽函数拉倒信号发送者所在线程执行,所以,发送者和接收者不在同一个线程时,使用此参数要考虑好后果,除非槽函数应该是可重入的,一般禁用这种情况;
Qt::QueuedConnection:信号发送后立即返回,不等槽函数执行完毕,槽函数在接收者所在线程执行,按照接收者线程的正常事件循环队列执行;(The slot is invoked when control returns to the event loop of the receiver's thread.)
Qt::BlockingQueuedConnection:类似Qt::QueuedConnection,只是信号发送后,发送者所在线程阻塞,等待槽函数返回,当发送者和接受者在同一个线程时,应该禁用这种方式,因为他会导致线程死锁,另外如果接收信号的对象所在线程没有事件循环,也会导致发送信号对象所在线程死锁,因为发送的信号无法被事件循环处理,而发送信号的线程却在等待处理结果,所以死锁;
Qt::UniqueConnection:这个标识可以和其他任何标示联合使用(用按位或操作符),它保证一个信号和一个槽之间只能调用一次connect()函数,如果连接已经存在,则调用失败。