说好的计划,就要实现,今年我要把吹过的牛皮,设定的计划,统统给圆了。
我们今天继续打开帮助文档,查看我们今天的小技巧。我们可以看到今天,我们了解一下槽函数的一个小技巧吧。
这个槽函数部分相信大家是经常使用的部分啦,也算是qt的一个特色,今天就简单的记录一下我们常用的部分,如果大家有更好的用法,请大家多多指出来。
1.connect基本使用
槽函数链接,大家都非常的熟悉了下面来总结一些下槽函数的写法吧。
QObject::connect() 函数有多重参数形式, 一种参数形式的函数原型是:
QMetaObject::Connection QObject::connect(const QObject *sender, canst char *signal,
const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)
使用这种参数形式的 connect()进行信号与槽函数的连接时, 一般句法如下 :
connect(sender, SIGNAL(signal()),receiver,SLOT(slot()));
这里使用了宏 SIGNAL()和l SLOT()指定信号和槽函数 , 而且如果信号和槽函数带有参数 , 还需注明参数类型, 如 :
connect (spinNum,SIGNAL(valueChanged(int)),this,SLOT(updateStatus(int)));
上面也就是我们常用的qt4的书写方式。下面是Qt5的书写方式:
另外一种参数形式的 connect()函数的原型是 :
QMetaObject: : Connection QObject : : connect(const QObject *sender , const QMetaMethod
&signal, const QObject *receiver, const QMetaMethod &method, Qt: :ConnectionType type =
Qt: :AutoConnection)
对于具有默认参数的信号与槽(即信号名称是唯一的 , 没有参数不同而同名的两个信号) ,可以使用这种函数指针形式进行关联 ,如 :
connect (lineEdit , &QLineEdit :: textChanged , this , &widget :: ontextChanged ) ;
这也就是我们常用的qt5 的书写方式,但是我们也看出来了,当遇到重载函数的时候,由于没有参数类型就会出现问题。
比如:
QSpinBox 有两个 valueChanged ()信号 ,分别是 :
void QSpinBox ::valueChanged(int i)
void QSpinBox ::valueChanged(const QString &text)
即使在自定义窗体 widget 里定义 了 一个槽函数,如:
void onValueChanged(int i);
我们使用qt5写的时候,就会报错:
connect (spinNum, &QSpinBox :: valueChanged , this, QWidget :: onValueChanged) ;
对于上面的方式我们该如何解决呢?
解决方案
方案1:
就采用qt4的写法,可以直接带参数写:
connect (spinNum,SIGNAL(valueChanged(int)),this,SLOT(onValueChanged(int)));
不过这个方式虽然可以解决问题,但是毕竟比较过去的写法了,比较不推荐此写法,但是说你喜欢这样写,就用这样来写,毕竟自习喜欢就可以。
方案2:
采用qt5的写法的时候,就要使用QOverload了。
可以写如下:
connect(spinNum,QOverload<int>::of(&QSpinBox::valueChanged),this,QWidget::onValueChanged);
方法3:
我们要记录对应的函数指针,然后再调用:
void(QSpinBox ::*valueChangedInt)(int) = &QSpinBox::valueChanged;
void(QSpinBox ::*valueChangedString)(QString) = &QSpinBox::valueChanged;
connect(spinNum,valueChangedInt,this,QWidget::onValueChanged);
上面就是常用的几种方式,如果大家有更好的写法,请留言,让大家共同进步。
2.connect 第5个参数
不管是哪种参数形式的 connect()函数,最后都有一个参数 Qt: :ConnectionTypetype,缺省值为Qt::AutoConnection。枚举类型 Qt::ConnectionType 表示了信号与槽之间的关联方式,有以下几种取值。
- Qt: :AutoConnection (缺省值) : 如果信号的接收者与发射者在同一个线程 , 就使用 Qt::DirectConnection 方式 ; 否则使用 Qt:: QueuedConnection 方式 , 在信号发射时自动确 定关联方式。
- Qt: :DirectConnection : 信号被发射时-槽函数立即执行 , 槽函数与信号在同 一个线程。
- Qt: :QueuedConnection : 在事件循环回到接收者线程后执行槽函数,槽函数与信号在不同的线程。
- Qt: :BlockingQueuedConnection : 与 Qt::QueuedConnection 相似,只是信号线程会阻塞直到槽函数执行完毕。当信号与槽函数在同一个线程时绝对不能使用这种方式 , 否则会造成死锁。
3.connect 应用 c++11标准 lamda
在c++11中,我们了解了仿函数,匿名函数也可以使用到,我们可以让第4个参数使用匿名函数来替代,这样也方便。
connect(ui->pushButton,&QPushButton::clicked,[=](){qDebug()<<"lambda 表达式";});
这里简单了解一下lamda函数,更加详细的可以看我以前记录的博客:
这里的槽就是一个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}:函数体。内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获的变量。
捕捉列表有以下几种形式:
- [var]表示值传递方式捕捉变量var;
- [=]表示值传递方式捕捉所有父作用域的变量(包括this);
- [&var]表示引用传递捕捉变量var;
- [&]表示引用传递方式捕捉所有父作用域的变量(包括this);
- [this]表示值传递方式捕捉当前的this指针
这些就是基本的对应东西啦。不懂lamda函数一定要看看我以前记录的博客偶。
4.sender()获取信号发送过来的值
在槽函数里, 使用 QObject :: sender()可以获取信号发射者的指针 。 如果知道信号发射者的类型 ,可以将指针投射为确定的类型 , 然后使用这个确定类的接口函数。
例如 , 在 QSpinBox 的 valueChanged(int )信号的槽函数里,可以通过 sender()和 qobject_cast获得信号发射者的指针,从而对信号发射者进行操作 。
QSpinBox *spinBox = qobject cast<QSpinBox *>(sender()) ;
这样我们可以对发送者进行其它处理操作,这个时候我们发现,这个功能只能满足,发送者只能是一个发送,不能使用多个发送者发送到同一个槽函数,那如何解决这个问题呢?
QSignalMapper 就可以解决这个问题
5.QSignalMapper 的使用
Qt 提供了QObject::sender()函数来返回发送该信号的对象的指针。但是如果有多个信号关联到了同一个槽上,而该槽中需要对每一个信号进行不同的处理,则使用这种方法就麻烦了,这是可以使用QSignalMapper 类。
下面是帮助文档中的,QSignalMapper被叫做信号映射器,可以实现对多个相同部件的相同信号进行处理。
QSignalMapper(QObject *parent = nullptr)
virtual ~QSignalMapper()
QObject *mapping(int id) const
QObject *mapping(const QString &id) const
QObject *mapping(QWidget *widget) const
QObject *mapping(QObject *object) const
void removeMappings(QObject *sender)
void setMapping(QObject *sender, int id)
void setMapping(QObject *sender, const QString &text)
void setMapping(QObject *sender, QWidget *widget)
void setMapping(QObject *sender, QObject *object)
下面也是官方的例子:
class ButtonWidget : public QWidget
{
Q_OBJECT
public:
ButtonWidget(const QStringList &texts, QWidget *parent = 0);
signals:
void clicked(const QString &text);
private:
QSignalMapper *signalMapper;
};
对应初识化函数
ButtonWidget::ButtonWidget(const QStringList &texts, QWidget *parent)
: QWidget(parent)
{
signalMapper = new QSignalMapper(this);
QGridLayout *gridLayout = new QGridLayout;
for (int i = 0; i < texts.size(); ++i) {
QPushButton *button = new QPushButton(texts[i]);
connect(button, SIGNAL(clicked()), signalMapper, SLOT(map()));
signalMapper->setMapping(button, texts[i]);
gridLayout->addWidget(button, i / 3, i % 3);
}
connect(signalMapper, SIGNAL(mapped(QString)),
this, SIGNAL(clicked(QString)));
setLayout(gridLayout);
}
其实就是个筛选功能,map映射道理一样,在map找到对应的值,然后我们知道对应的对象指针。
6.自定义类型函数变量
有关这个问题可以看我博客:
比如我们自己定义一个结构体,或者类,想要通过槽函数,使用:
struct FbceData
{
int a;
int b;
}
//发送的函数为
void sendData(FbceData * abc)
如果我们使用普通的链接,会提示错误。
于是我们要在对应的连接槽函数的时候要注册这个类。我这里采用的是指针的方式。
#include <QMetaType>
qRegisterMetaType<FbceData *>("FbceData *");
然后我们使用qt5的链接槽
connect(FinsAll,&FINSCenter::sendData,this,&MainWindow::on_GetData);//关联到槽
这样我们就解决了对应的问题。
7.disconnect()的使用
其实顾名思义,你能链接槽函数,当然要断开链接,基本上3种方式吧
bool QObject::disconnect(const QObject * sender, const char * signal, const QObject * receiver, const char * method)
我们常写的方式:
disconnect(myObject, 0, 0, 0); //方法1
disconnect(myObject, SIGNAL(mySignal()), 0, 0);//方式2
disconnect(myObject, 0, myReceiver, 0);//方式3
方法1是断开所有链接的槽,方法2是断开了链接对象,方法3是断开两个对象。
8.dumpObjectInfo() 与 dumpObjectTree()
这两个函数是让将此对象的信号连接等信息转储到调试输出中。在debug中可能会用用到,目前我还没有使用这个,如果大家了解这个可以使用这个,可以告知我,这个用到比较少,也许可能是我个人问题,希望以后使用到后在这里添加。
好了,今天的小技巧有点长,希望大家能够喜欢这个,如果喜欢的话,可以多多加油偶。让我们共同进步吧。