相同點
- 目的都是為了解決對象間通信的問題。
- 事件發(fā)生或信號發(fā)射時會有相應的處理函數(shù)啃匿。
不同之處
-
返回值
- 事件有返回值蛔外,并且有意義蛆楞,我們要根據(jù)這個返回值來確定是否還要繼續(xù)事件的處理,比如在QT中夹厌,事件處理函數(shù)如果返回true豹爹,則這個事件處理 已完成,QApplication會接著處理下一個事件矛纹,而如果返回false臂聋,那么事件分派函數(shù)會繼續(xù)向上尋找下一個可以處理該事件的注冊方法。
- 信號處理函數(shù)沒有返回值或南。
-
響應時優(yōu)先級
- 在QT中孩等,事件使用了一個事件隊列來維護,如果事件的處理中又產(chǎn)生了新的事件采够,那么新的事件會加入到隊列尾肄方,直到當前事件處理完畢后, QApplication再去隊列頭取下一個事件來處理蹬癌。
- 信號類似于硬件中的中斷权她,當一個信號產(chǎn)生后,他上面注冊的所有槽都會被按順序立即執(zhí)行逝薪。相應的隅要,產(chǎn)生遞歸調(diào)用的問題,比如在某個信號處理槽中又產(chǎn)生了一個信號董济,當前處理會被打斷拾徙,程序會去處理之。
-
有多個響應處理函數(shù)時順序
- 事件因為都是與窗口相關的感局,所以事件回調(diào)時都是從當前窗口開始尼啡,一級一級向上派發(fā),直到有一個窗口返回true询微,截斷了事件的處理為止崖瞭。
- 信號可以在注冊時顯示地指明槽的位置,如果沒有指明撑毛,默認是隨機順序书聚。
信號和槽的機制
當某個事件發(fā)生之后,比如藻雌,按鈕檢測到自己被點擊了一下雌续,它就會發(fā)出一個信號(signal)。這種發(fā)出是沒有目的的胯杭,類似廣播驯杜。如果有對象對這個信號感興趣,它就會使用連接(connect)函數(shù)做个,將想要處理的信號和自己的一個函數(shù)(槽函數(shù)(slot))綁定并處理這個信號鸽心。
- 槽函數(shù)類似于回調(diào)函數(shù)滚局。
- 調(diào)用時機
一般情況下,信號發(fā)出后會立即執(zhí)行顽频。
特殊情況:當使用queued connections時藤肢,情況略有不同,在這種情況下糯景,emit關鍵字后面的代碼將立即繼續(xù)嘁圈,插槽將稍后執(zhí)行。
屬性
聲明
參數(shù)
連接與斷開
信號連接信號
信號與槽(信號)個數(shù)的關系
信號的觸發(fā)
參考
https://blog.csdn.net/maizousidemao/article/details/104199773
Qt信號槽-原理分析
信號與槽的連接方式
- 事件是對象蟀淮〕蠛ⅲ可以被任何QObject的實例收到和處理。
如何傳遞事件
事件發(fā)生時灭贷,qt創(chuàng)建了一個代表它的對象,
事件類型
事件處理
一般的事件傳遞方式是調(diào)用virtual函數(shù)略贮∩跖保可以把自己想做的處理寫在當前函數(shù),然后把其他的都傳遞給base類處理逃延。
void MyCheckBox::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
// handle left mouse button here
} else {
// pass on other buttons to base class
QCheckBox::mousePressEvent(event);
}
}
更通用的處理方式:
bool MyWidget::event(QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *ke = static_cast<QKeyEvent *>(event);
if (ke->key() == Qt::Key_Tab) {
// special tab handling here
return true;
}
} else if (event->type() == MyCustomEventType) {
MyCustomEvent *myEvent = static_cast<MyCustomEvent *>(event);
// custom event handling here
return true;
}
return QWidget::event(event);
}
- 注意返回值是true代表著事件處理完畢览妖,不再傳遞給下一個處理函數(shù)。
事件過濾
可以在事件處理之前先通過filter揽祥,阻止特定的event傳遞給target widget讽膏,通過installEventFilter和removeEventFilter控制。
bool FilterObject::eventFilter(QObject *object, QEvent *event)
{
if (object == target && event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Tab) {
// Special tab handling
return true;
} else
return false;
}
return false;
}
發(fā)送事件
用戶可以自定義自己的事件拄丰,并生成一個實例府树,通過sendEvent()和postEvent()發(fā)送它們。
多線程
兩種方式
1. 利用moveToThread()(推薦使用)
原型:void QObject::moveToThread(QThread *targetThread)
myObject->moveToThread(QApplication::instance()->thread());
改變該對象與線程之間執(zhí)行的關系料按,并不改變誰擁有該對象奄侠,只是讓該對象執(zhí)行成員函數(shù)時利用了target thread≡乜螅「在哪創(chuàng)建就屬于哪」這句話放在任何地方都是適用的垄潮。
- 注意
使用 moveToThread() 有一些需要注意的地方,首先就是類對象不能有父對象闷盔,否則無法將該對象“移動”到新線程弯洗。如果類對象保存在棧上,自然銷毀由操作系統(tǒng)自動完成逢勾;如果是保存在堆上牡整,沒有父對象的指針要想正常銷毀,需要將線程的 finished() 信號關聯(lián)到 QObject 的 deleteLater() 讓其在正確的時機被銷毀溺拱。其次是該對象一旦“移動”到新線程果正,那么該對象中的計時器(如果有 QTimer 等計時器成員變量)將重新啟動炎码。
2. 繼承QThread類,并重寫run()秋泳。
- 全局線程(和UI一直存在的線程)
- 局部線程(用完即釋放的線程)
它們的結(jié)束方式不一樣潦闲。
注意如何正確退出一個線程
參考
https://blog.csdn.net/czyt1988/article/details/64441443
線程池
- 使用QRunnable+QThreadPool
QRunnable表示一個可執(zhí)行的任務,把它放到QThreadPool里的線程中執(zhí)行迫皱。
消息隊列
一歉闰、什么是Qt消息循環(huán)
Qt消息循環(huán),就是從一個隊列中不斷取出消息卓起,并響應消息的過程和敬。窗體的鼠標、鍵盤戏阅、輸入法昼弟、繪制,各種消息奕筐,都來自于Qt的消息循環(huán)舱痘。以Windows操作系統(tǒng)為例,Qt接管Windows原生窗口消息离赫,并翻譯成Qt的消息芭逝,派發(fā)給程序下的各個子對象、子QWidget等渊胸,通過接管層旬盯,可以很好屏蔽不同平臺之間的差異性,開發(fā)人員不需要關心Windows或者X11的消息的差異性翎猛,只需要搞清楚各個QEvent之間是什么含義胖翰。
最開始的Qt消息循環(huán)開始于QCoreApplication::exec()。用戶創(chuàng)建出一個QCoreApplication切厘,或者說更多情況下是QApplication泡态,執(zhí)行QCoreApplication::exec(),一個應用程序便開始了迂卢。QCoreApplication會不斷從操作系統(tǒng)獲取消息某弦,并且分發(fā)給QObject。
如果沒有消息循環(huán)而克,那么Qt的信號和槽無法完全使用靶壮,有些函數(shù)也無法正確執(zhí)行。舉個例子员萍,通過QueuedConnection連接的信號腾降,其實是將一個事件壓入了消息循環(huán),如果沒有QCoreApplication::exec()碎绎,那么這個消息循環(huán)將永遠無法派發(fā)到指定的對象螃壤。
二抗果、什么是線程相關性
準確點來說,應該是指QObject的線程相關性奸晴。以Qt文檔中的示意圖來作說明:
當我們創(chuàng)建一個QObject時冤馏,它會與創(chuàng)建自己所在的線程綁定。它參與的消息循環(huán)寄啼,其實是它所在線程的消息循環(huán)逮光,如上圖所示。假如某個線程沒有默認的QThread::exec()墩划,那么該線程上的QObject則無法接收到事件涕刚。另外,如果兩個不同線程的QObject需要相互通信乙帮,那么只能通過QueuedConnection的方式杜漠,異步通知對方線程,在下一輪消息循環(huán)處理QObject的消息察净。
QObject的線程相關性默認會和它的parent保持一致驾茴。如果一個QObject沒有parent,那么可以通過moveToThread塞绿,將它的線程相關性切換到指定線程。
了解QObject的線程相關性非常重要恤批,很多初學者常常分不清一個多線程中哪些QObject應該由主線程創(chuàng)建异吻,哪些應該由工作線程創(chuàng)建,我的觀點是喜庞,它參與哪個消息循環(huán)诀浪,就由哪個來創(chuàng)建。
正因為這樣的特性延都,我們才可以理解什么叫做AutoConnection雷猪。通過AutoConnect連接的兩個QObject,如果是在同一個線程晰房,那么可以直接調(diào)用(DirectConnection)求摇,如果不是在同一個線程,那么就通過事件通知的方式(QueuedConnection)來調(diào)用殊者。通過信號和槽与境、事件或者QueuedConnection方式來進行線程間的通訊,尤其是與UI線程通訊猖吴,永遠是最優(yōu)雅的方式之一摔刁。