Qt每天一个小技巧之QObject 槽函数相关

说好的计划,就要实现,今年我要把吹过的牛皮,设定的计划,统统给圆了。

我们今天继续打开帮助文档,查看我们今天的小技巧。我们可以看到今天,我们了解一下槽函数的一个小技巧吧。

 这个槽函数部分相信大家是经常使用的部分啦,也算是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函数,更加详细的可以看我以前记录的博客:

c++ 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中可能会用用到,目前我还没有使用这个,如果大家了解这个可以使用这个,可以告知我,这个用到比较少,也许可能是我个人问题,希望以后使用到后在这里添加。

好了,今天的小技巧有点长,希望大家能够喜欢这个,如果喜欢的话,可以多多加油偶。让我们共同进步吧。

 

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值