徐州哪有做網(wǎng)站的熱門seo推廣排名穩(wěn)定
【QT八股文】系列之篇章2 | QT的信號(hào)與槽機(jī)制及通訊流程
- 前言
- 2. 信號(hào)與槽
- 信號(hào)與槽機(jī)制介紹/本質(zhì)/原理,什么是Qt信號(hào)與槽機(jī)制?如何在Qt中使用?
- 信號(hào)與槽機(jī)制原理,解析流程
- Qt信號(hào)槽的調(diào)用流程
- 信號(hào)與槽機(jī)制的優(yōu)缺點(diǎn)
- 信號(hào)與槽機(jī)制需要注意的問(wèn)題
- 信號(hào)的注意點(diǎn)
- 信號(hào)與槽與回調(diào)函數(shù)區(qū)別
- Qt信號(hào)與槽的多種用法
- PYQT5 connect 函數(shù)
- Qt connect 函數(shù)的連接方式
- PYQT5信號(hào)槽的鏈接方式
- 信號(hào)槽同步與異步/多線程下,信號(hào)槽分別在什么線程中執(zhí)行,如何控制——`Qt connect 函數(shù)的連接方式`來(lái)控制
- 3. 通訊流程
- QT的TCP通訊流程
- QT的UDP通訊流程
- 下一章筆記
- 說(shuō)明
前言
第一篇章主要是基礎(chǔ)定義及QT中重要的事件機(jī)制
筆記鏈接:【QT八股文】系列之篇章1 | QT的基礎(chǔ)知識(shí)及事件/機(jī)制
這里我們?cè)诹私饬薗T的大概后,我們將來(lái)了解QT中的核心機(jī)制:信號(hào)與槽
因?yàn)榻榻B到信號(hào)與槽,所以筆者我會(huì)講通訊流程提前在前面來(lái)介紹
原創(chuàng)文章,未經(jīng)同意請(qǐng)勿轉(zhuǎn)載
2. 信號(hào)與槽
信號(hào)與槽機(jī)制介紹/本質(zhì)/原理,什么是Qt信號(hào)與槽機(jī)制?如何在Qt中使用?
-
定義
Qt信號(hào)與槽機(jī)制是一種基于事件機(jī)制的編程模型,用于對(duì)象之間的通信。信號(hào)是由發(fā)送方對(duì)象發(fā)射的事件,而槽是接收方對(duì)象用于處理這些事件的函數(shù)。在Qt中,我們可以使用QObject類中的信號(hào)和槽機(jī)制來(lái)實(shí)現(xiàn)對(duì)象間的通信。通過(guò)定義信號(hào)和槽函數(shù),在信號(hào)發(fā)射時(shí),會(huì)自動(dòng)調(diào)用對(duì)應(yīng)的槽函數(shù)進(jìn)行處理。 -
使用
PyQt的內(nèi)置信號(hào)是自動(dòng)定義的,使用PyQt5.QtCore.pyqtSignal函數(shù)可以為QObject對(duì)象創(chuàng)建一個(gè)信號(hào),使用pyqtSignal函數(shù)可以把信號(hào)定義為類的屬性。使用connect函數(shù)可以將信號(hào)綁定到槽函數(shù)上,使用disconnect函數(shù)可以解除信號(hào)與槽函數(shù)的綁定,使用emit函數(shù)可以發(fā)射信號(hào)。 -
本質(zhì)(就是回調(diào)函數(shù))
在事件的處理方面,信號(hào)槽相比回調(diào)函數(shù),具有類型安全、松耦合、任意參數(shù)的優(yōu)勢(shì),但執(zhí)行效率會(huì)有一點(diǎn)損失。信號(hào)相當(dāng)于傳遞參數(shù)(指實(shí)參,用于傳遞值/動(dòng)作變化),槽函數(shù)像用于傳遞函數(shù)體(形參/函數(shù)體,用于接收值/根據(jù)動(dòng)作變化來(lái)做出對(duì)應(yīng)操作。) -
原理
- Qt 中的信號(hào)與槽機(jī)制是一種事件處理機(jī)制,它允許程序在接收到特定事件時(shí)執(zhí)行特定的操作。在 Qt 中,信號(hào)與槽機(jī)制被廣泛應(yīng)用于組件之間的通信和事件處理。
- 具體來(lái)說(shuō),Qt 中的信號(hào)與槽機(jī)制是基于 QObject 類的。任何一個(gè) QObject 對(duì)象都可以作為一個(gè)信號(hào)源,它可以通過(guò) emit() 方法發(fā)出信號(hào)。同時(shí),任何一個(gè) QObject 對(duì)象都可以作為一個(gè)槽,它可以接受并處理來(lái)自信號(hào)源的信號(hào)。當(dāng)一個(gè)信號(hào)源發(fā)出信號(hào)時(shí),它會(huì)連接到相應(yīng)的槽。這些槽可以是與信號(hào)源同一個(gè)對(duì)象,也可以是其他 QObject 對(duì)象。當(dāng)信號(hào)源接收到信號(hào)時(shí),它會(huì)將信號(hào)傳遞給所有已經(jīng)連接到該槽的對(duì)象。這些對(duì)象會(huì)在接收到信號(hào)時(shí)執(zhí)行相應(yīng)的操作。
信號(hào)與槽機(jī)制原理,解析流程
-
原理
- Qt 中的信號(hào)與槽機(jī)制是一種事件處理機(jī)制,它允許程序在接收到特定事件時(shí)執(zhí)行特定的操作。在 Qt 中,信號(hào)與槽機(jī)制被廣泛應(yīng)用于組件之間的通信和事件處理。
- 具體來(lái)說(shuō),Qt 中的信號(hào)與槽機(jī)制是基于 QObject 類的。任何一個(gè) QObject 對(duì)象都可以作為一個(gè)信號(hào)源,它可以通過(guò) emit() 方法發(fā)出信號(hào)。同時(shí),任何一個(gè) QObject 對(duì)象都可以作為一個(gè)槽,它可以接受并處理來(lái)自信號(hào)源的信號(hào)。當(dāng)一個(gè)信號(hào)源發(fā)出信號(hào)時(shí),它會(huì)連接到相應(yīng)的槽。這些槽可以是與信號(hào)源同一個(gè)對(duì)象,也可以是其他 QObject 對(duì)象。當(dāng)信號(hào)源接收到信號(hào)時(shí),它會(huì)將信號(hào)傳遞給所有已經(jīng)連接到該槽的對(duì)象。這些對(duì)象會(huì)在接收到信號(hào)時(shí)執(zhí)行相應(yīng)的操作。
-
解析流程
- moc查找頭文件中的signals,slots,標(biāo)記出信號(hào)和槽。
- 將信號(hào)槽信息存儲(chǔ)到類靜態(tài)變量staticMetaObject中,并且按聲明順序進(jìn)行存放,建立索引。
- 當(dāng)發(fā)現(xiàn)有connect連接時(shí),將信號(hào)槽的索引信息放到一個(gè)map中,彼此配對(duì)。
- 當(dāng)調(diào)用emit時(shí),調(diào)用信號(hào)函數(shù),并且傳遞發(fā)送信號(hào)的對(duì)象指針,元對(duì)象指針,信號(hào)索引,參數(shù)列表到active函數(shù)
- 通過(guò)active函數(shù)找到在map中找到所有與信號(hào)對(duì)應(yīng)的槽索引
- 根據(jù)槽索引找到槽函數(shù),執(zhí)行槽函數(shù)。
Qt信號(hào)槽的調(diào)用流程
注意:信號(hào)槽的實(shí)現(xiàn):元對(duì)象編譯器MOC,MOC的本質(zhì)就是反射器
- MOC(元對(duì)象編譯器)查找頭文件中的signal與slots,標(biāo)記出信號(hào)槽。將信號(hào)槽信息儲(chǔ)存到類靜態(tài)變量staticMetaObject中,并按照聲明的順序進(jìn)行存放,建立索引。
- connect鏈接,將信號(hào)槽的索引信息放到一個(gè)雙向鏈表中,彼此配對(duì)。
- emit被調(diào)用,調(diào)用信號(hào)函數(shù),且傳遞發(fā)送信號(hào)的對(duì)象指針,元對(duì)象指針,信號(hào)索引,參數(shù)列表到active函數(shù)。
- active函數(shù)在雙向鏈表中找到所有與信號(hào)對(duì)應(yīng)的槽索引,根據(jù)槽索引找到槽函數(shù),執(zhí)行槽函數(shù)。
信號(hào)與槽機(jī)制的優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn):
-
類型安全。需要關(guān)聯(lián)的信號(hào)槽的簽名必須是等同的。即信號(hào)的參數(shù)類型和參數(shù)個(gè)數(shù)同接受該信號(hào)的槽的參數(shù)類型和參數(shù)個(gè)數(shù)相同。若信號(hào)和槽簽名不一致,編譯器會(huì)報(bào)錯(cuò)。
信號(hào)的參數(shù)可以多于槽,槽參數(shù)數(shù)量不能大于于信號(hào)
。💡 槽函數(shù)的參數(shù)是否可以比信號(hào)的參數(shù)多?
也可以。唯一的情況就是槽函數(shù)參數(shù)帶有默認(rèn)參數(shù),除去默認(rèn)參數(shù)外,槽函數(shù)的參數(shù)必須小于等于信號(hào)的參數(shù)。 -
松散耦合。QT的信號(hào)槽的建立和解除綁定十分自由。信號(hào)和槽機(jī)制減弱了Qt對(duì)象的耦合度。激發(fā)信號(hào)的Qt對(duì)象無(wú)需知道是那個(gè)對(duì)象的那個(gè)信號(hào)槽接收它發(fā)出的信號(hào),它只需在適當(dāng)?shù)臅r(shí)間發(fā)送適當(dāng)?shù)男盘?hào)即可,而不需要關(guān)心是否被接受和那個(gè)對(duì)象接受了。Qt就保證了適當(dāng)?shù)牟鄣玫搅苏{(diào)用,即使關(guān)聯(lián)的對(duì)象在運(yùn)行時(shí)被刪除。程序也不會(huì)奔潰。
💡 信號(hào)重載了,如何確定連接哪個(gè)信號(hào)?
采用函數(shù)指針確定連接哪個(gè)信號(hào)。 -
靈活性。一個(gè)信號(hào)可以關(guān)聯(lián)多個(gè)槽,或多個(gè)信號(hào)關(guān)聯(lián)同一個(gè)槽。
-
- 不足:
- 速度較慢。與回調(diào)函數(shù)相比,信號(hào)和槽機(jī)制運(yùn)行速度比直接調(diào)用非虛函數(shù)慢10倍。信號(hào)槽同真正的回調(diào)函數(shù)比起來(lái)時(shí)間的耗損還是很大的,所以在嵌入式實(shí)時(shí)系統(tǒng)中應(yīng)當(dāng)慎用。
- 原因:
- ①需要定位接收信號(hào)的對(duì)象。
- ②安全地遍歷所有關(guān)聯(lián)槽。
- ③編組、解組傳遞參數(shù)。
- ④多線程的時(shí)候,信號(hào)需要排隊(duì)等待。(
然而,與創(chuàng)建對(duì)象的new操作及刪除對(duì)象的delete操作相比,信號(hào)和槽的運(yùn)行代價(jià)只是他們很少的一部分。信號(hào)和槽機(jī)制導(dǎo)致的這點(diǎn)性能損耗,對(duì)實(shí)時(shí)應(yīng)用程序是可以忽略的。
)
- 原因:
- 不能出現(xiàn)宏定義。信號(hào)槽的參數(shù)限定很多例如不能攜帶模板類參數(shù),不能出現(xiàn)宏定義等等。
- 速度較慢。與回調(diào)函數(shù)相比,信號(hào)和槽機(jī)制運(yùn)行速度比直接調(diào)用非虛函數(shù)慢10倍。信號(hào)槽同真正的回調(diào)函數(shù)比起來(lái)時(shí)間的耗損還是很大的,所以在嵌入式實(shí)時(shí)系統(tǒng)中應(yīng)當(dāng)慎用。
信號(hào)與槽機(jī)制需要注意的問(wèn)題
信號(hào)與槽機(jī)制是比較靈活的,但有些局限性我們必須了解,這樣在實(shí)際的使用過(guò)程中才能夠做到有的放矢,避免產(chǎn)生一些錯(cuò)誤。下面就介紹一下這方面的情況。
- 信號(hào)與槽的效率是非常高的,但是同真正的回調(diào)函數(shù)比較起來(lái),由于增加了靈活 性,因此在速度上還是有所損失,當(dāng)然這種損失相對(duì)來(lái)說(shuō)是比較小的,通過(guò)在一臺(tái) i586- 133 的機(jī)器上測(cè)試是 10 微秒(運(yùn)行 Linux),可見這種機(jī)制所提供的簡(jiǎn)潔性、靈活性還是 值得的。但如果我們要追求高效率的話,比如在實(shí)時(shí)系統(tǒng)中就要盡可能的少用這種機(jī)制。
- 信號(hào)與槽機(jī)制與普通函數(shù)的調(diào)用一樣,如果使用不當(dāng)?shù)脑?#xff0c;在程序執(zhí)行時(shí)也有可能 產(chǎn)生死循環(huán)。因此,在定義槽函數(shù)時(shí)一定要注意避免間接形成無(wú)限循環(huán),即在槽中再次發(fā)射 所接收到的同樣信號(hào)。
- 如果一個(gè)信號(hào)與多個(gè)槽相關(guān)聯(lián)的話,那么,當(dāng)這個(gè)信號(hào)被發(fā)射時(shí),與之相關(guān)的槽被 激活的順序?qū)⑹请S機(jī)的,并且我們不能指定該順序。
- 宏定義不能用在 signal 和 slot 的參數(shù)中。
- 構(gòu)造函數(shù)不能用在 signals 或者 slots 聲明區(qū)域內(nèi)。
- 函數(shù)指針不能作為信號(hào)或槽的參數(shù)。
- 信號(hào)與槽不能有缺省參數(shù)。
- 信號(hào)與槽也不能攜帶模板類參數(shù)。
信號(hào)的注意點(diǎn)
- 所有的信號(hào)聲明都是公有的,所以Qt規(guī)定不能在signals前面加public,private, protected。
- 所有的信號(hào)都沒有返回值,所以返回值都用void。
- 所有的信號(hào)都不需要定義。
- 必須直接或間接繼承自QOBject類,并且開頭私有聲明包含Q_OBJECT。
- 在同一個(gè)線程中,當(dāng)一個(gè)信號(hào)被emit發(fā)出時(shí),會(huì)立即執(zhí)行其槽函數(shù),等槽函數(shù)執(zhí)行完畢后,才會(huì)執(zhí)行emit后面的代碼,如果一個(gè)信號(hào)鏈接了多個(gè)槽,那么會(huì)等所有的槽函數(shù)執(zhí)行完畢后才執(zhí)行后面的代碼,槽函數(shù)的執(zhí)行順序是按照它們鏈接時(shí)的順序執(zhí)行的。不同線程中(即跨線程時(shí)),槽函數(shù)的執(zhí)行順序是隨機(jī)的。
- 在鏈接信號(hào)和槽時(shí),可以設(shè)置鏈接方式為:在發(fā)出信號(hào)后,不需要等待槽函數(shù)執(zhí)行完,而是直接執(zhí)行后面的代碼,是通過(guò)connect的第5個(gè)參數(shù)。
- 信號(hào)與槽機(jī)制要求信號(hào)和槽的參數(shù)一致,所謂一致,是參數(shù)類型一致。如果不一致,允許的情況是,信號(hào)的參數(shù)可以比槽函數(shù)的參數(shù)多,即便如此,槽函數(shù)存在的那些參數(shù)的順序也必須和信號(hào)的前面幾個(gè)一致起來(lái)。這是因?yàn)?#xff0c;你可以在槽函數(shù)中選擇忽略信號(hào)傳來(lái)的數(shù)據(jù)(也就是槽函數(shù)的參數(shù)比信號(hào)的少),但是不能說(shuō)信號(hào)根本沒有這個(gè)數(shù)據(jù),你就要在槽函數(shù)中使用(就是槽函數(shù)的參數(shù)比信號(hào)的多,這是不允許的)。
信號(hào)與槽與回調(diào)函數(shù)區(qū)別
-
鏈接的不同
- 回調(diào)函數(shù)使用函數(shù)指針來(lái)實(shí)現(xiàn)的,如果多個(gè)類都關(guān)注一個(gè)類的動(dòng)態(tài)變化,這樣就會(huì)需要寫出一個(gè)比較長(zhǎng)的列表來(lái)管理這些類之間的關(guān)系。稍微在編碼方面不那么靈活,稍顯冗余。
- QT使用信號(hào)與槽來(lái)解決這個(gè)連接問(wèn)題,這種方式比較清晰簡(jiǎn)單一些,一個(gè)類只需要清楚自己有幾個(gè)槽函數(shù)有幾個(gè)信號(hào),然后將信號(hào)與槽進(jìn)行連接,QT會(huì)自己處理函數(shù)的調(diào)用關(guān)系。這樣在軟件設(shè)計(jì)角度更加的清晰,靈活,不容易出錯(cuò)。
-
執(zhí)行順序/時(shí)間的不同
- Qt 信號(hào)與槽機(jī)制中的槽函數(shù)在接收到信號(hào)時(shí)會(huì)自動(dòng)執(zhí)行,而回調(diào)函數(shù)通常是在調(diào)用時(shí)立即執(zhí)行。Qt 信號(hào)與槽機(jī)制可以在信號(hào)觸發(fā)時(shí)立即執(zhí)行槽函數(shù),也可以延遲執(zhí)行槽函數(shù),而回調(diào)函數(shù)通常是立即執(zhí)行的。
- 信號(hào)與槽機(jī)制中的信號(hào)與槽之間的執(zhí)行順序是不確定的,可以是任意順序,也可以是逆序;而回調(diào)函數(shù)機(jī)制中的回調(diào)函數(shù)之間的執(zhí)行順序通常是確定的,按照函數(shù)聲明的順序執(zhí)行。
-
對(duì)象綁定
信號(hào)與槽機(jī)制可以實(shí)現(xiàn)對(duì)象之間的動(dòng)態(tài)綁定,可以在運(yùn)行時(shí)動(dòng)態(tài)地綁定信號(hào)與槽;而回調(diào)函數(shù)機(jī)制通常只能在程序啟動(dòng)時(shí)進(jìn)行綁定。
-
主要用途不同
信號(hào)和槽機(jī)制是用于在程序運(yùn)行時(shí)傳遞數(shù)據(jù)和事件的機(jī)制,而回調(diào)函數(shù)則通常被用于函數(shù)或方法的調(diào)用。因此,信號(hào)和槽機(jī)制可以用于模塊之間的通信和交互,而回調(diào)函數(shù)則通常用于函數(shù)或方法的調(diào)用。
Qt信號(hào)與槽的多種用法
- 一個(gè)信號(hào)可以和多個(gè)槽相連
這時(shí)槽的執(zhí)行順序和在不在同一個(gè)線程上有關(guān),同一線程,槽的執(zhí)行順序和聲明順序有關(guān),跨線程時(shí),執(zhí)行順序是不確定的。
- 多個(gè)信號(hào)可以連接到一個(gè)槽
只要任意一個(gè)信號(hào)發(fā)出,這個(gè)槽就會(huì)被調(diào)用。 - 一個(gè)信號(hào)可以連接到另外的一個(gè)信號(hào)
當(dāng)?shù)谝粋€(gè)信號(hào)發(fā)出時(shí),第二個(gè)信號(hào)被發(fā)出。除此之外,這種信號(hào)-信號(hào)的形式和信號(hào)-槽的形式?jīng)]有什么區(qū)別。 - 槽可以被取消鏈接
這種情況并不經(jīng)常出現(xiàn),因?yàn)楫?dāng)一個(gè)對(duì)象delete之后,Qt自動(dòng)取消所有連接到這個(gè)對(duì)象上面的槽。想主動(dòng)取消連接就用disconnect()函數(shù)中添加任何實(shí)現(xiàn)。 - 可以使用Lambda 表達(dá)式
在使用 Qt 5 的時(shí)候,能夠支持 Qt 5 的編譯器都是支持 Lambda 表達(dá)式的。
PYQT5 connect 函數(shù)
注:在Qt中第五個(gè)參數(shù)用于指定信號(hào)與槽的匹配規(guī)則。而PYQT5是第四個(gè)參數(shù)
在 PyQt5 中,connect 函數(shù)【connect: PyQt5.QtWidgets.QSignalMapper()
】是一個(gè)用于連接信號(hào)與槽的函數(shù)。它通常被用于將對(duì)象的信號(hào)與槽函數(shù)進(jìn)行連接。
列子:mapper = Qt.QSignalMapper() mapper.setMapping(button, button.clicked.connect(mapper.setCurrentIndex))
第一個(gè)參數(shù)是一個(gè)可選的參數(shù),用于指定要連接的信號(hào)源。如果該參數(shù)為 None,則表示連接的是系統(tǒng)提供的信號(hào)。如果該參數(shù)不為 None,則表示要連接自定義信號(hào)。
第二個(gè)參數(shù)是一個(gè)可選的參數(shù),用于指定要連接的槽函數(shù)。如果該參數(shù)為 None,則表示連接的是默認(rèn)槽函數(shù)。如果該參數(shù)不為 None,則表示要連接指定的槽函數(shù)。
第三個(gè)參數(shù)是一個(gè)字符串,用于指定信號(hào)與槽之間的映射關(guān)系。該字符串通常由信號(hào)名稱和槽函數(shù)名稱組成。例如,“clicked” 表示將按鈕的點(diǎn)擊信號(hào)與按鈕的 clicked 槽函數(shù)進(jìn)行連接。
第四個(gè)參數(shù)是一個(gè) PyQt5 中的 QSignalMapper 對(duì)象,用于指定信號(hào)與槽的匹配規(guī)則。該對(duì)象應(yīng)該實(shí)現(xiàn) QSignalMapper 類中的方法,例如 setMapping() 和 currentIndex() 等。
第五個(gè)參數(shù)是一個(gè)可選的參數(shù),用于指定信號(hào)中斷連接的函數(shù)。如果連接的信號(hào)源對(duì)象被刪除或重新分配,則連接將被中斷。默認(rèn)情況下,連接不會(huì)自動(dòng)中斷。
Qt connect 函數(shù)的連接方式
-
自動(dòng)連接
Qt::AutoConnection
默認(rèn)值,使用這個(gè)值則連接類型會(huì)在信號(hào)發(fā)送時(shí)決定。如果接收者和發(fā)送者在同一個(gè)線程,則自動(dòng)使用
多線程時(shí)為隊(duì)列連接函數(shù),單線程時(shí)為直接連接函數(shù)。 -
直接連接
Qt::DirectConnection
== 如果接收者和發(fā)送者不在一個(gè)線程,則自動(dòng)使用Qt::QueuedConnection類型。==
Qt::DirectConnection:槽函數(shù)會(huì)在信號(hào)發(fā)送的時(shí)候直接被調(diào)用,槽函數(shù)和信號(hào)發(fā)送者在同一線程。效果看上去就像是直接在信號(hào)發(fā)送位置調(diào)用了槽函數(shù),效果上看起來(lái)像函數(shù)調(diào)用,同步執(zhí)行。
emit語(yǔ)句后面的代碼將在與信號(hào)關(guān)聯(lián)的所有槽函數(shù)執(zhí)行完畢后才被執(zhí)行。
信號(hào)/槽在信號(hào)發(fā)出者所在的線程中執(zhí)行 -
隊(duì)列連接
Qt::QueuedConnection
信號(hào)發(fā)出后,信號(hào)會(huì)暫時(shí)被放到一個(gè)消息隊(duì)列中,需等到接收對(duì)象所屬線程的事件循環(huán)取得控制權(quán)時(shí)才取得該信號(hào),然后執(zhí)行和信號(hào)關(guān)聯(lián)的槽函數(shù),這種方式既可以在同一線程內(nèi)傳遞消息也可以跨線程操作。
emit語(yǔ)句后的代碼將在發(fā)出信號(hào)后立即被執(zhí)行,無(wú)需等待槽函數(shù)執(zhí)行完畢
信號(hào)在信號(hào)發(fā)出者所在的線程中執(zhí)行,槽函數(shù)在信號(hào)接收者所在的線程中執(zhí)行 -
Qt::BlockingQueuedConnection
槽函數(shù)的調(diào)用時(shí)機(jī)與Qt::QueuedConnection一致,不過(guò)發(fā)送完信號(hào)后發(fā)送者所在線程會(huì)阻塞,直到槽函數(shù)運(yùn)行完。而且接收者和發(fā)送者絕對(duì)不能在一個(gè)線程,否則程序會(huì)死鎖。在多線程間需要同步的場(chǎng)合可能需要這個(gè)。 -
Qt::UniqueConnection:這個(gè)flag可以通過(guò)按位或(|)與以上四個(gè)結(jié)合在一起使用。當(dāng)這個(gè)flag設(shè)置時(shí),當(dāng)某個(gè)信號(hào)和槽已經(jīng)連接時(shí),再進(jìn)行重復(fù)的連接就會(huì)失敗。也就是為了避免重復(fù)連接。
PYQT5信號(hào)槽的鏈接方式
在 PyQt5 中,信號(hào)與槽的連接方式有兩種:1. 使用 connect() 函數(shù);2. 裝飾器@pyqtSlot() 。
@pyqtSlot()
優(yōu)點(diǎn)是方式書寫比較簡(jiǎn)潔。缺點(diǎn)是但函數(shù)名稱不能自由定義,在想自定義參數(shù)時(shí)沒有詳細(xì)說(shuō)明。
connect()
方式優(yōu)點(diǎn)是理解和學(xué)習(xí)起來(lái)比較簡(jiǎn)單,而且函數(shù)名稱可以自由定義。缺點(diǎn)是但如果信號(hào)比較多時(shí),書寫就比較混亂。
使用信號(hào)處理器的優(yōu)點(diǎn)是可以在信號(hào)發(fā)生時(shí)執(zhí)行復(fù)雜的操作,而缺點(diǎn)是連接信號(hào)處理器需要花費(fèi)更多的內(nèi)存和時(shí)間,并且連接信號(hào)處理器需要手動(dòng)管理連接關(guān)系。因此,使用信號(hào)處理器僅適用于需要執(zhí)行復(fù)雜操作的情況。
-
裝飾器方法:
@pyqtSlot()
裝飾器@pyqtSlot():修飾關(guān)鍵詞,表明下面是完整的信號(hào)槽函數(shù)
# 需要引入 pyqtSlot 庫(kù)函數(shù) from PyQt5.QtCore import pyqtSlot@pyqtSlot() #裝飾器,此函數(shù)沒有connect直接通過(guò)裝飾器初始化連接槽函數(shù) def on_pushButton_clicked(self)print("我點(diǎn)擊了")
在@pyqtSlot()方式里,函數(shù)名稱有特殊要求,如下:
def on_(控件對(duì)象名)_信號(hào)名(self,內(nèi)置參數(shù)):
@pyqtSlot()控制控件的多信號(hào)
@pyqtSlot() def on_lineEdit_returnPressed(self):print('觸發(fā)了信號(hào) returnPressed')def on_lineEdit_textChanged(self):print('觸發(fā)了信號(hào) textChanged')
注意:
一個(gè)控件同時(shí)要寫多個(gè)信號(hào)與槽函數(shù)時(shí),只需要寫一遍@pyqtSlot()關(guān)鍵詞
,中間可以有其他函數(shù)隔開。一定是一個(gè)類里面的,一個(gè)控件只寫一遍@pyqtSlot(),不是所有控件信號(hào)只寫一次@pyqtSlot(),有多少控件的信號(hào)還是要寫。 -
connect連接法
使用 connect() 函數(shù)將信號(hào)與槽函數(shù)連接起來(lái)。connect() 函數(shù)接受兩個(gè)參數(shù):要連接的信號(hào)和要連接的槽函數(shù)。連接成功后,當(dāng)信號(hào)發(fā)生時(shí),槽函數(shù)將被調(diào)用。
# 在初始化函數(shù)中信號(hào)連接槽函數(shù) self.pushButton.clicked.connect(self.test) # 槽函數(shù) def test(self):print("點(diǎn)擊了一下")
規(guī)則:
- 語(yǔ)法規(guī)則:
self.控件對(duì)象名稱.信號(hào)名稱.connect(self.槽函數(shù)名稱)
- 有參數(shù)時(shí),
槽函數(shù)名稱
部分寫成lambda 參數(shù)名: 函數(shù)名(參數(shù)名)
- 沒有參數(shù)時(shí),槽函數(shù)不用寫括號(hào)
()
- 語(yǔ)法規(guī)則:
信號(hào)槽同步與異步/多線程下,信號(hào)槽分別在什么線程中執(zhí)行,如何控制——Qt connect 函數(shù)的連接方式
來(lái)控制
可以通過(guò)QT的connect函數(shù)的第五個(gè)參數(shù)(PYQT5中是第四個(gè)參數(shù))來(lái)控制, 信號(hào)槽執(zhí)行時(shí)所在的線程。
通常使用的connect,實(shí)際上最后一個(gè)參數(shù)使用的是Qt::AutoConnection類型:Qt支持6種連接方式,其中3中最主要:
-
Qt::AutoConnection(自動(dòng)方式)
信號(hào)槽在信號(hào)發(fā)出者所在的線程中執(zhí)行
Qt的默認(rèn)連接方式,如果信號(hào)的發(fā)出和接收這個(gè)信號(hào)的對(duì)象同屬一個(gè)線程,那個(gè)工作方式與直連方式相同(會(huì)自動(dòng)使用Qt::DirectConnection類型);否則工作方式與排隊(duì)方式相同(會(huì)自動(dòng)使用Qt::QueuedConnection類型)。
即多線程時(shí)為隊(duì)列連接函數(shù),單線程時(shí)為直接連接函數(shù)。
-
Qt::DirectConnection(直連方式)(信號(hào)與槽函數(shù)關(guān)系類似于函數(shù)調(diào)用,同步執(zhí)行)
當(dāng)信號(hào)發(fā)出后,相應(yīng)的槽函數(shù)將立即被調(diào)用。emit語(yǔ)句后的代碼將在所有槽函數(shù)執(zhí)行完畢后被執(zhí)行。
-
Qt::QueuedConnection(排隊(duì)方式)(此時(shí)信號(hào)被塞到信號(hào)隊(duì)列里了,信號(hào)與槽函數(shù)關(guān)系類似于消息通信,異步執(zhí)行)
信號(hào)在信號(hào)發(fā)出者所在的線程中執(zhí)行,槽函數(shù)在信號(hào)接收者所在的線程中執(zhí)行
當(dāng)信號(hào)發(fā)出后,排隊(duì)到信號(hào)隊(duì)列中,需等到接收對(duì)象所屬線程的事件循環(huán)取得控制權(quán)時(shí)才取得該信號(hào),調(diào)用相應(yīng)的槽函數(shù)。emit語(yǔ)句后的代碼將在發(fā)出信號(hào)后立即被執(zhí)行,無(wú)需等待槽函數(shù)執(zhí)行完畢。
-
Qt::BlockingQueuedConnection(信號(hào)和槽必須在不同的線程中,否則就產(chǎn)生死鎖)
這個(gè)是完全同步隊(duì)列只有槽線程執(zhí)行完成才會(huì)返回,否則發(fā)送線程也會(huì)一直等待,相當(dāng)于是
不同的線程可以同步起來(lái)執(zhí)行
。與Qt::QueuedConnection相同,除了信號(hào)線程阻塞直到槽返回。如果接收方處于發(fā)送信號(hào)的線程中,則不能使用此連接,否則應(yīng)用程序?qū)⑺梨i。
-
Qt::UniqueConnection
與默認(rèn)工作方式相同,只是不能重復(fù)連接相同的信號(hào)和槽,因?yàn)槿绻貜?fù)連接就會(huì)導(dǎo)致一個(gè)信號(hào)發(fā)出,對(duì)應(yīng)槽函數(shù)就會(huì)執(zhí)行多次。
這個(gè)flag可以通過(guò)按位或(|)與以上四個(gè)結(jié)合在一起使用。當(dāng)這個(gè)flag設(shè)置時(shí),當(dāng)某個(gè)信號(hào)和槽已經(jīng)連接時(shí),再進(jìn)行重復(fù)的連接就會(huì)失敗。也就是為了避免重復(fù)連接。
-
Qt::AutoCompatConnection
是為了連接Qt4與Qt3的信號(hào)槽機(jī)制兼容方式,工作方式與Qt::AutoConnection一樣。
3. 通訊流程
QT的TCP通訊流程
QT如果要進(jìn)行網(wǎng)絡(luò)編程首先需要在.pro中添加如下代碼:QT += network
-
服務(wù)端:(QTcpServer)
① 創(chuàng)建QTcpServer對(duì)象
② 監(jiān)聽list需要的參數(shù)是地址和端口號(hào)
③ 當(dāng)有新的客戶端連接成功回發(fā)送newConnect信號(hào)
④ 在newConnection信號(hào)槽函數(shù)中,調(diào)用nextPendingConnection函數(shù)獲取新連接QTcpSocket對(duì)象
⑤ 連接QTcpSocket對(duì)象的readRead信號(hào)
⑥ 在readRead信號(hào)的槽函數(shù)使用read接收數(shù)據(jù)
⑦ 調(diào)用write成員函數(shù)發(fā)送數(shù)據(jù) -
服務(wù)器端
-
創(chuàng)建用于監(jiān)聽的套接字
-
給套接字設(shè)置監(jiān)聽
-
如果有連接到來(lái), 監(jiān)聽的套接字會(huì)發(fā)出信號(hào)newConnected
-
接收連接, 通過(guò)nextPendingConnection()函數(shù), 返回一個(gè)QTcpSocket類型的套接字對(duì)象(用于通信)
-
使用用于通信的套接字對(duì)象通信 1>. 發(fā)送數(shù)據(jù): write 2>. 接收數(shù)據(jù): readAll/read
-
代碼
Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget) {ui->setupUi(this);tcpServer = new QTcpServer;tcpServer->listen(QHostAddress("192.168.0.111"),1234);connect(tcpServer,SIGNAL(newConnection()),this,SLOT(new_connect())); }Widget::~Widget() {delete ui; }void Widget::new_connect() {qDebug("--new connect--");QTcpSocket* tcpSocket = tcpServer->nextPendingConnection();connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(read_data()));socketArr.push_back(tcpSocket);}void Widget::read_data() {for(int i=0; i<socketArr.size(); i++){if(socketArr[i]->bytesAvailable()){char buf[256] = {};socketArr[i]->read(buf,sizeof(buf));qDebug("---read:%s---",buf);}} }
-
-
-
客戶端:(QTcpSocket)
① 創(chuàng)建QTcpSocket對(duì)象
② 當(dāng)對(duì)象與Server連接成功時(shí)會(huì)發(fā)送connected 信號(hào)
③ 調(diào)用成員函數(shù)connectToHost連接服務(wù)器,需要的參數(shù)是地址和端口號(hào)
④ connected信號(hào)的槽函數(shù)開啟發(fā)送數(shù)據(jù)
⑤ 使用write發(fā)送數(shù)據(jù),read接收數(shù)據(jù) -
客戶端:
- 創(chuàng)建用于通信的套接字
- 連接服務(wù)器: connectToHost
- 連接成功與服務(wù)器通信
1 >. 發(fā)送數(shù)據(jù): write 2>. 接收數(shù)據(jù): readAll/read
-
代碼
Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget) {ui->setupUi(this);tcpSocket = new QTcpSocket;connect(tcpSocket,SIGNAL(connected()),this,SLOT(connect_success()));tcpSocket->connectToHost("172.20.10.3",1234); }Widget::~Widget() {delete ui; }void Widget::on_send_clicked() {std::string msg = ui->msg->text().toStdString();int ret = tcpSocket->write(msg.c_str(),msg.size()+1);qDebug("--send:%d--",ret); }void Widget::connect_success() {ui->send->setEnabled(true); }
QT的UDP通訊流程
UDP(User Datagram Protocol即用戶數(shù)據(jù)報(bào)協(xié)議)是一個(gè)輕量級(jí)的,不可靠的,面向數(shù)據(jù)報(bào)的無(wú)連接協(xié)議。在網(wǎng)絡(luò)質(zhì)量令人十分不滿意的環(huán)境下,UDP協(xié)議數(shù)據(jù)包丟失嚴(yán)重。由于UDP的特性:它不屬于連接型協(xié)議,因而具有資源消耗小,處理速度快的優(yōu)點(diǎn),所以通常音頻、視頻和普通數(shù)據(jù)在傳送時(shí)使用UDP較多,因?yàn)樗鼈兗词古紶杹G失一兩個(gè)數(shù)據(jù)包,也不會(huì)對(duì)接收結(jié)果產(chǎn)生太大影響。所以QQ這種對(duì)保密要求并不太高的聊天程序就是使用的UDP協(xié)議。
在Qt中提供了QUdpSocket 類來(lái)進(jìn)行UDP數(shù)據(jù)報(bào)(datagrams)的發(fā)送和接收。Socket簡(jiǎn)單地說(shuō),就是一個(gè)IP地址加一個(gè)port端口 。
QT下UDP通信服務(wù)器端和客戶端的關(guān)系是對(duì)等的, 做的處理也是一樣的:
- 創(chuàng)建套接字對(duì)象 2. 如果需要接收數(shù)據(jù), 必須綁定端口 3. 發(fā)送數(shù)據(jù): writeDatagram 4. 接收數(shù)據(jù): readDatagram
流程:①創(chuàng)建QUdpSocket套接字對(duì)象 ②如果需要接收數(shù)據(jù),必須綁定端口 ③發(fā)送數(shù)據(jù)用writeDatagram,接收數(shù)據(jù)用 readDatagram 。
下一章筆記
下篇筆記鏈接:【QT的多線程以及QThread與QObject】
下篇筆記主要內(nèi)容:QT的多線程以及QThread與QObject
說(shuō)明
碼字不易,可能當(dāng)中存在某些字體錯(cuò)誤(筆者我沒有發(fā)現(xiàn)),如果有錯(cuò)誤,歡迎大家指正。🤗
另外因?yàn)楣P記是之前做的,這里我只把我之前做的搬移和重新排版過(guò)來(lái),如果有知識(shí)上的錯(cuò)誤也歡迎大家指正。