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; });