QT中connect()函数的用法

connect()函数实现的是信号与槽的关联。

注意:只有QO bject类及其派生的类才能使用信号和槽的机制

函数原型:

static QMetaObject::Connection connect(const QObject *sender, const char *signal,const QObject *receiver, const char *member, Qt::ConnectionType = Qt::AutoConnection);
 
static QMetaObject::Connection connect(const QObject *sender, const QMetaMethod &signal,const QObject *receiver, const QMetaMethod &method, Qt::ConnectionType type = Qt::AutoConnection);

inline QMetaObject::Connection connect(const QObject *sender, const char *signal,const char *member, Qt::ConnectionType type = Qt::AutoConnection) const;

在第一个函数中:第1个参数为信号发射源对象,例如后面的dlg;第2个参数是所发射的信号,例如后面的SIGNAL(dlgReturn(int));第3个参数是接受信号的对象,例如后面的this,表明是本部件,即Widget,当这个参数是this时,可以将其省略掉,因为在第3个函数中,该参数默认为this;第4个参数是要执行的槽,例如:后面的SLOT(showValue(int)),也可以指定一个信号,实现信号与信号的关联。

注意:

1、对于信号与槽,必须使用SIGNAL()和SLOT()宏,它们将参数转化为const char *类型

2、第四个参数指定的槽声明时必须使用slots关键字。

eg:

private slots://槽的声明
  void showValue(int value);
  void on_label_linkActivated(const QString &link);

第5个参数,只是一般使用默认值,在满足某些特殊需求的时候可能需要手动设置。

Qt::AutoConnection:默认值,使用这个值则连接类型会在信号发送时决定。如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型。如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。

Qt::DirectConnection:槽函数会在信号发送的时候直接被调用,槽函数运行于信号发送者所在线程。效果看上去就像是直接在信号发送位置调用了槽函数。这个在多线程环境下比较危险,可能会造成奔溃。

不在同一个线程用这个,也可能导致另一个线程槽函数始终不响应。

Qt::QueuedConnection:槽函数在控制回到接收者所在线程的事件循环时被调用,槽函数运行于信号接收者所在线程。发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用。多线程环境下一般用这个。

Qt::BlockingQueuedConnection:槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。

Qt::UniqueConnection:这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免重复连接

 

connect()函数的5中用法:

第一种

Qt 4 使用宏,主要通过connect + 宏的方式进行通信连接。

connect(发送对象,信号,接收对象,槽函数),其中发送信号和槽函数需要用 SIGNAL() 和 SLOT() 来进行明确的声明。

以下示例先自定义一个 Button,然后定义两个信号:

class MyButton : public QWidget
{
Q_OBJECT
public:
  explicit MyButton(QWidget *parent = nullptr);

signals:
  void sigClicked();
  void sigClicked(bool check);  //重载信号
};
那么在用这个 Button 的时候连接这两个信号,按照旧版本的写法,应该是这样:

  connect(m_pBtn,SIGNAL(sigClicked()),this,SLOT(onClicked()));

  connect(m_pBtn,SIGNAL(sigClicked(bool)),this,SLOT(onClicked(bool)));

这种写法比较麻烦,常常在用的时候缺少括号,不过该写法很明确,一眼就能看出来是将哪个信号连接到哪个槽。

第二种

Qt 5 推出了新的 connect 函数,不需要使用 SIGNAL() 和 SLOT() 宏,可以在编译时做类型检查:

connect函数原型如下:

[static] QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection)

用 connect 将信号与槽函数连接,不需要再使用 SIGNAL() 和 SLOT() 宏:

  connect(m_pBtn,&MyButton::sigClicked,this,&Widget::onClicked);

这种写法看起来很简洁,但是存在一些坑需要注意,这句写法如果用在上面的示例中,会报错下面的错误:

error: no matching member function for call to 'connect' connect(m_pBtn,&MyButton::sigClicked,this,&Widget::onClicked);

这是因为我们自定义的 Button 中存在两个重载信号,然后用这种 connect 的方式会无法识别到底想要连接哪个信号。所以,如果信号是重载的话,需要用下面的写法来替换:

  connect(m_pBtn, static_cast<void (MyButton::*)(bool)>(&MyButton::sigClicked), this, &Widget::onClicked);

问题又来了,如果我的onClicked槽也是重载的话,还是会报同样的错误。因为编译器不知道你想要真正连接哪个槽。所以这里建议,如果信号重载,可以用上面的方法来写,如果槽重载…还是用第一种方法来 connect 吧,比较保险,虽然比较麻烦点。

第三种
最后来看一种最新的写法,忘记是在 Qt 的哪个版本推出的了,主要针对重载信号的连接做了调整,会更简单些:
同样是上面的示例:

connect(m_pBtn, QOverload<bool>::of(&MyButton::sigClicked),this,&Widget::onClicked);

很显然这种写法相对于第二种会比较简单些,但依然不能连接到重载的槽函数,如果连接重载槽函数,还是会报之前的错误。

第四种:Lambda 函数写法
个人比较喜欢用lambda函数的方式,如果槽函数中的内容比较简单的话,没必要再去单独定义一个槽来连接, 直接用Lambda 函数会更简单。
来看一下示例:

connect(m_pBtn, QOverload<bool>::of(&MyButton::sigClicked), this, [=](bool check){
  qDebug()  << "do something";
});

connect(m_pBtn, static_cast<void (MyButton::*)(bool)>(&MyButton::sigClicked), this, [=](bool check){
  qDebug()  << "do something";
});

connect(ui->lineEdit, &QLineEdit::textEdited, this, [=](QString s){
  qDebug()  << s;
});

 

posted @ 2022-04-09 12:27  云山漫卷  阅读(8445)  评论(0编辑  收藏  举报