一、環境
- 系統:Window10 64企業版
- Qt:qt5.12.12
- VS:vs2017企業版
- addin:2.8.1.6
二、信號和槽
2.1信號
- Qt通過類中聲明的信號和槽函數實現類(同一個類或不同類)的對象之間信息流的聯動。
- 信號:
- 自定義聲明關鍵字:signal;
- 信號類似void返回值函數的聲明方法;
- 信號沒有實現只有聲明;
- 信號的觸發方式在指定函數位置emit signalName();
- 信號可以不加參數,也可以添加參數。當添加參數時參數分為系統默認支持的數據類型和自定義數據類型的兩種,信號需要傳遞自定義參數需要手動注冊這種數據類型,注冊方法:類的聲明部分需要添加:
1 使用方法(聲明和注冊自定義數據類型) 2 3 1)引入頭文件:#include<QMetaType> 4 5 6 2)添加聲明(一般在.h文件):利用宏 Q_DECLARE_METATYPE 7 8 //這個函數一定要用在調用或者綁定信號槽前 9 3)注冊(一般在.cpp文件構造函數中):利用方法 qRegisterMetaType
2.2槽函數
- 槽函數:
- 當信號被觸發響應信號的響應處理;
- 返回值為void類型,參數和信號一致,可以接收信號傳遞過來的值(自定義參數需要注冊);
- 可以跨線程;
- 信號和槽的關聯方式一般分為兩種:系統根據規則自動關聯信號和槽;手動聲明信號和槽函數關聯關系。
- 信號和槽根據名稱自動關聯的原因是下面這個語句:
1 //在ui文件生成的.h文件會發現下面這個語句 2 3 QMetaObject::connectSlotsByName(xxxClass);
2.3關聯信號和槽的方法
信號和槽的關聯方式:
- QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)
- 參數說明:第一個參數是信號發送對象的地址指針(注意是對象不是類),第二個參數發送的信號地址,第三個接受者對象地址,第四個接收者處理方法,第五個參數關聯類型。
具體使用過程有兩種方式:
- Qt4.0中常用,支持信號和槽函數重載,例如:connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(pushButon1_clicked()));
- Qt5以后使用的新的綁定方法,不支持信號和槽的重載。但是這種新的方法去掉了使用的宏,改為信號和槽的類,例如:
connect(ui->pushButton_2,&QPushButton::clicked,this,&::MainWindow::pushButon2_clicked);
類似lamda表達式,槽函數直接實現:connect(ui->pushButton_4, QOverload<bool>::of(&QPushButton::clicked),[=](bool check){ ui->textBrowser->setText("按鈕4信號綁定成功");});
connect第五個參數:
- connect具體使用過程經常只用四個參數,第五個參數用默認值。
第五個參數使用過程幾個值及相關意義:
- Qt::AutoConnection: 默認值,使用這個值則連接類型會在信號發送時決定。如果接收者和發送者在同一個線程,則自動使用Qt::DirectConnection類型。如果接收者和發送者不在一個線程,則自動使用Qt::QueuedConnection類型。
- Qt::DirectConnection:槽函數會在信號發送的時候直接被調用,槽函數運行於信號發送者所在線程。效果看上去就像是直接在信號發送位置調用了槽函數。這個在多線程環境下比較危險,可能會造成奔潰。
- Qt::QueuedConnection:槽函數在控制回到接收者所在線程的事件循環時被調用,槽函數運行於信號接收者所在線程。發送信號之后,槽函數不會立刻被調用,等到接收者的當前函數執行完,進入事件循環之后,槽函數才會被調用。多線程環境下一般用這個。
- Qt::BlockingQueuedConnection:槽函數的調用時機與Qt::QueuedConnection一致,不過發送完信號后發送者所在線程會阻塞,直到槽函數運行完。接收者和發送者絕對不能在一個線程,否則程序會死鎖。在多線程間需要同步的場合可能需要這個。
- Qt::UniqueConnection:這個flag可以通過按位或(|)與以上四個結合在一起使用。當這個flag設置時,當某個信號和槽已經連接時,再進行重復的連接就會失敗。也就是避免了重復連接。
關於線程:
- QThread 是用來管理線程的,它所依附的線程和它管理的線程並不是同一個線程;
- QThread 所依附的線程,就是執行 QThread t(0) 或 QThread * t=new QThread(0) 的線程。也可以看作主線程
- QThread 管理的線程,就是 run 啟動的線程。也就是子線程,只有run函數中創建的對象,才屬於子線程(注意qudpsocket跨線程報錯),繼承自QThread的線程類的成員函數和成員變量都不在子線程;
- 因為QThread的對象依附在主線程中,所以他的slot函數會在主線程中執行,而不是子線程。除非:QThread 對象依附到子線程中(通過movetoThread)slot 和信號是直接連接,且信號在次線程中發射
但上兩種解決方法都不好,因為QThread不是這么用的(Bradley T. Hughes)
2.4取消信號和槽的關聯
使用方法和connect類似,關鍵字用disconnect即可。此過程不需要第五個參數。
取消方式:
bool QObject::disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
三、自定義類型注冊
注冊方法及步驟:
- 自定義類型(結構體/類),聲明文件包含QMetaType:#include <QMetaType>;
- 自定義類型結尾通過宏Q_DECLARE_METATYPE,注冊:Q_DECLARE_METATYPE(MyClassType);
- 建立信號槽關聯connect之前用qRegisterMetaType注冊(這個類文件也需要包含頭文件QMetaType:#include <QMetaType>)
注意:Q_DECLARE_METATYPE、qRegisterMetaType注冊過程對象、引用、指針是不一樣的。
示例:
1 //自定義類型文件 2 #include <QMetaType> 3 4 // 5 Q_DECLARE_METATYPE(Custom) 6 7 8 //關聯信號槽的類文件 9 qRegisterMetaType<Custom>("Custom&");
四、相關參考
官方參考: