Qt的slot的signal是一種對(duì)象之間的通信方式,,在講這個(gè)之前,,要講一下Qt的元對(duì)象系統(tǒng) Qt的元對(duì)象系統(tǒng) Meta Object SystemQt的元對(duì)象系統(tǒng) Meta Object System 主要是分成這個(gè)幾個(gè)部分 (1)所有的類都是QObject的的子類 (2)Q_OBJECT的宏定義會(huì)激活元對(duì)象的功能,例如說(shuō)信號(hào)和槽 (3)如果定義了 Q_OBJECT 宏定義,,會(huì)讓moc編輯器自動(dòng)生成一些代碼和類來(lái)支持元對(duì)象的功能,。 至少是讓該類產(chǎn)生了這么幾個(gè)數(shù)據(jù) static const QMetaObject staticMetaObject; virtual const QMetaObject *metaObject() const; virtual void *qt_metacast(const char *); virtual int qt_metacall(QMetaObject::Call, int, void **); static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **)(Q5.12.4) (1)其中QMetaObject靜態(tài)變量staticMetaObject包含了QObject的所謂的元數(shù)據(jù),也就是QObject信息的一些描述信息:除了類型信息外,,還包含QT中特有的signal&slot信息,。這些信息是整個(gè)類共享的,所用用static來(lái)申明,。
上面是QMetaObject的申明,,就是這些數(shù)據(jù)成員記錄了所有的signal、slot,、property,、class information信息。
(2) virtual QObject::metaObject(); 這個(gè)的方法是返回一個(gè)該類對(duì)應(yīng)的staticMetaObject的const 指針,。也就是引用。 如果說(shuō)這個(gè)類沒(méi)有定義Q_OBJECT 宏定義,,那么這個(gè)類就沒(méi)有staticMetaObject這個(gè)靜態(tài)的QMetaObject對(duì)象,,那么這個(gè)時(shí)候這實(shí)例調(diào)用返回是是其父類的QMetaObject對(duì)象,因?yàn)檫@個(gè)虛函數(shù)沒(méi)有被重載,。所以一般建議都加上這個(gè)宏定義,。 (3)qt_static_metacall()是一個(gè)靜態(tài)函數(shù),主要是用于對(duì)象的函數(shù)的調(diào)用的,。Qt 5.12版本或少以上,,這個(gè)靜態(tài)函數(shù)也會(huì)通過(guò)指針的方式存儲(chǔ)在QMetaObject 里面了。qt_static_metacall()提供了兩種的函數(shù)調(diào)用的方式,,一個(gè)是InvokeMetaMethod調(diào)用,,會(huì)將對(duì)象轉(zhuǎn)換為實(shí)際的對(duì)象類型,然后調(diào)用,。另外一個(gè)是IndexOfMethod調(diào)用,,會(huì)通過(guò)元函數(shù)的索引號(hào)調(diào)用。
Qt的slot和singnalqt的slot和singnal的名稱都會(huì)存在的QMetaObject中,,但是這個(gè)時(shí)候,,slot和signal是沒(méi)有聯(lián)系的。需要進(jìn)行connect才可以使用,。 例如:connect(ui->pushBtn,SIGNAL(clicked()),ui->lineEdit,SLOT(clear())); connect 在幕后到底都做了些什么事情,?為什么emit一個(gè)signal后,相應(yīng)的slot都會(huì)被調(diào)用,? SIGNAL和SLOT宏定義 connect(&obj, SIGNAL(destroyed()), &app, SLOT(aboutQt())); 在這里signal和slot的名字都被包含在了兩個(gè)大寫的SIGNAL和SLOT中,,這兩個(gè)是什么呢?原來(lái)SIGNAL 和 SLOT 是Qt定義的兩個(gè)宏,。 # define SLOT(a) ”1″#a # define SIGNAL(a) ”2″#a Qt把signal和slot都轉(zhuǎn)化成了字符串,,并且還在這個(gè)字符串的前面加上了附加的符號(hào),signal前面加了’2’,,slot前面加了’1’,。也就是說(shuō),我們前面寫了下面的connect調(diào)用,在經(jīng)過(guò)moc編譯器轉(zhuǎn)換之后,,就便成了: 然后,,會(huì)去檢查發(fā)送signal的對(duì)象是否有這個(gè)signal,方法就是查找這個(gè)對(duì)象的class所對(duì)應(yīng)的staticMetaObject對(duì)象中所包含的d.stringdata所指向的字符串中是否包含這個(gè)signal的名字,,在這個(gè)檢查過(guò)程中,,就會(huì)用到d.data所指向的那一串整數(shù),通過(guò)這些整數(shù)值來(lái)計(jì)算每一個(gè)具體字符串的起始地址,。同理,,還會(huì)使用同樣的方法去檢查slot,看響應(yīng)這個(gè)signal的對(duì)象是否包含有相應(yīng)的slot,。這兩個(gè)檢查的任何一個(gè)如果失敗的話,,connect函數(shù)就失敗了,返回false,。 前面的步驟都是在做一些必要的檢查工作,,下一步,就是要把發(fā)送signal的對(duì)象和響應(yīng)signal的對(duì)象關(guān)聯(lián)起來(lái),。在QObject的私有數(shù)據(jù)類QObjectPrivate中,,有下面這些數(shù)據(jù)結(jié)構(gòu)來(lái)保存這些信息:
在發(fā)送signal的對(duì)象中,每一個(gè)signal和slot的connection,,都會(huì)創(chuàng)建一個(gè)QObjectPrivate::Connection對(duì)象,,并且把這個(gè)對(duì)象保存到connectionList這個(gè)Vector里面去。 在響應(yīng)signal的對(duì)象中,,同樣,,也是每一個(gè)signal和slot的connection,都會(huì)一個(gè)創(chuàng)建一個(gè)Sender對(duì)象,,并且把這個(gè)對(duì)象附加在Senders這個(gè)列表中,。
emit幕后的故事 當(dāng)emit signal的時(shí)候,與這個(gè)signal相連的slot函數(shù)就會(huì)被調(diào)用,,那么這個(gè)調(diào)用時(shí)如何發(fā)生的呢? 看段代碼:
每一個(gè)signal都會(huì)被轉(zhuǎn)換為一個(gè)與之相對(duì)應(yīng)的成員函數(shù),。也就是說(shuō),,當(dāng)我們寫下這樣一行代碼:emit sigBtnClicked();當(dāng)程序運(yùn)行到這里的時(shí)候,實(shí)際上就是調(diào)用了void ZMytestObj::sigBtnClicked() 這個(gè)函數(shù),。 void ZMytestObj::sigMenuClicked() void ZMytestObj::sigBtnClicked(),,它們唯一的區(qū)別就是調(diào)用 QMetaObject::activate 函數(shù)時(shí)給出的參數(shù)不同,,一個(gè)是0,一個(gè)是1,,它們的含義是什么呢,?它們表示是這個(gè)類中的第幾個(gè)signal被發(fā)送出來(lái)了,回頭再去看頭文件就會(huì)發(fā)現(xiàn)它們就 是在這個(gè)類定義中,,signal定義出現(xiàn)的順序,,這個(gè)參數(shù)可是非常重要的,它直接決定了進(jìn)入這個(gè)函數(shù)體之后所發(fā)生的事情,。 當(dāng)執(zhí)行流程進(jìn)入到QMetaObject::activate函數(shù)中后,,會(huì)先從connectionLists這個(gè)變量中取出與這個(gè)signal相對(duì)應(yīng)的connection list,它根據(jù)的就是剛才所傳入進(jìn)來(lái)的signal index,。這個(gè)connection list中保存了所有和這個(gè)signal相鏈接的slot的信息,,每一對(duì)connection(即:signal和slot的連接)是這個(gè)list中的一項(xiàng)。 在每個(gè)一具體的鏈接記錄中,,還保存了這個(gè)鏈接的類型,,是自動(dòng)鏈接類型,還是隊(duì)列鏈接類型,,或者是阻塞鏈接類型,,不同的類型處理方法還不一樣的。
如果信號(hào)-槽連接方式為QueuedConnection,不論是否在同一個(gè)線程,,按隊(duì)列處理,。如果信號(hào)-槽連接方式為Auto,且不在同一個(gè)線程,,也按隊(duì)列處理,。 如果信號(hào)-槽連接方式為阻塞隊(duì)列BlockingQueuedConnection,按阻塞處理,。(注意同一個(gè)線程就不要按阻塞隊(duì)列調(diào)用了,。因?yàn)橥粋€(gè)線程,同時(shí)只能做一件事,,本身就是阻塞的,,直接調(diào)用就好了,如果走阻塞隊(duì)列,,則多了加鎖的過(guò)程,。如果槽中又發(fā)了同樣的信號(hào),就會(huì)出現(xiàn)死鎖:加鎖之后還未解鎖,又來(lái)申請(qǐng)加鎖,。) 隊(duì)列處理,,就是把槽函數(shù)的調(diào)用,轉(zhuǎn)化成了QMetaCallEvent事件,,通過(guò)QCoreApplication::postEvent 放進(jìn)了事件循環(huán), 等到下一次事件分發(fā),,相應(yīng)的線程才會(huì)去調(diào)用槽函數(shù)。 對(duì)于直接鏈接的類型,,先找到接收這個(gè)signal的對(duì)象的指針,,然后是處理這個(gè)signal的slot的index,已經(jīng)是否有需要處理的參數(shù),,然后就使用這些信息去調(diào)用receiver的qt_metcall 方法,。在qt_metcall方法中就簡(jiǎn)單了,根據(jù)slot的index,,一個(gè)大switch語(yǔ)句,,調(diào)用相應(yīng)的slot函數(shù)就OK了。 Qt的slot的signal是一種對(duì)象之間的通信方式,,在講這個(gè)之前,,要講一下Qt的元對(duì)象系統(tǒng) Qt的元對(duì)象系統(tǒng) Meta Object SystemQt的元對(duì)象系統(tǒng) Meta Object System 主要是分成這個(gè)幾個(gè)部分 (1)所有的類都是QObject的的子類 (2)Q_OBJECT的宏定義會(huì)激活元對(duì)象的功能,例如說(shuō)信號(hào)和槽 (3)如果定義了 Q_OBJECT 宏定義,,會(huì)讓moc編輯器自動(dòng)生成一些代碼和類來(lái)支持元對(duì)象的功能,。 至少是讓該類產(chǎn)生了這么幾個(gè)數(shù)據(jù) static const QMetaObject staticMetaObject; virtual const QMetaObject *metaObject() const; virtual void *qt_metacast(const char *); virtual int qt_metacall(QMetaObject::Call, int, void **); static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **)(Q5.12.4) (1)其中QMetaObject靜態(tài)變量staticMetaObject包含了QObject的所謂的元數(shù)據(jù),也就是QObject信息的一些描述信息:除了類型信息外,,還包含QT中特有的signal&slot信息,。這些信息是整個(gè)類共享的,所用用static來(lái)申明,。
上面是QMetaObject的申明,,就是這些數(shù)據(jù)成員記錄了所有的signal、slot,、property,、class information信息。
(2) virtual QObject::metaObject(); 這個(gè)的方法是返回一個(gè)該類對(duì)應(yīng)的staticMetaObject的const 指針,。也就是引用。 如果說(shuō)這個(gè)類沒(méi)有定義Q_OBJECT 宏定義,,那么這個(gè)類就沒(méi)有staticMetaObject這個(gè)靜態(tài)的QMetaObject對(duì)象,,那么這個(gè)時(shí)候這實(shí)例調(diào)用返回是是其父類的QMetaObject對(duì)象,因?yàn)檫@個(gè)虛函數(shù)沒(méi)有被重載,。所以一般建議都加上這個(gè)宏定義,。 (3)qt_static_metacall()是一個(gè)靜態(tài)函數(shù),主要是用于對(duì)象的函數(shù)的調(diào)用的,。Qt 5.12版本或少以上,,這個(gè)靜態(tài)函數(shù)也會(huì)通過(guò)指針的方式存儲(chǔ)在QMetaObject 里面了。qt_static_metacall()提供了兩種的函數(shù)調(diào)用的方式,,一個(gè)是InvokeMetaMethod調(diào)用,,會(huì)將對(duì)象轉(zhuǎn)換為實(shí)際的對(duì)象類型,然后調(diào)用,。另外一個(gè)是IndexOfMethod調(diào)用,,會(huì)通過(guò)元函數(shù)的索引號(hào)調(diào)用。
Qt的slot和singnalqt的slot和singnal的名稱都會(huì)存在的QMetaObject中,,但是這個(gè)時(shí)候,,slot和signal是沒(méi)有聯(lián)系的。需要進(jìn)行connect才可以使用,。 例如:connect(ui->pushBtn,SIGNAL(clicked()),ui->lineEdit,SLOT(clear())); connect 在幕后到底都做了些什么事情,?為什么emit一個(gè)signal后,相應(yīng)的slot都會(huì)被調(diào)用,? SIGNAL和SLOT宏定義 connect(&obj, SIGNAL(destroyed()), &app, SLOT(aboutQt())); 在這里signal和slot的名字都被包含在了兩個(gè)大寫的SIGNAL和SLOT中,,這兩個(gè)是什么呢?原來(lái)SIGNAL 和 SLOT 是Qt定義的兩個(gè)宏,。 # define SLOT(a) ”1″#a # define SIGNAL(a) ”2″#a Qt把signal和slot都轉(zhuǎn)化成了字符串,,并且還在這個(gè)字符串的前面加上了附加的符號(hào),,signal前面加了’2’,slot前面加了’1’,。也就是說(shuō),,我們前面寫了下面的connect調(diào)用,在經(jīng)過(guò)moc編譯器轉(zhuǎn)換之后,,就便成了: 然后,會(huì)去檢查發(fā)送signal的對(duì)象是否有這個(gè)signal,,方法就是查找這個(gè)對(duì)象的class所對(duì)應(yīng)的staticMetaObject對(duì)象中所包含的d.stringdata所指向的字符串中是否包含這個(gè)signal的名字,,在這個(gè)檢查過(guò)程中,就會(huì)用到d.data所指向的那一串整數(shù),,通過(guò)這些整數(shù)值來(lái)計(jì)算每一個(gè)具體字符串的起始地址,。同理,還會(huì)使用同樣的方法去檢查slot,,看響應(yīng)這個(gè)signal的對(duì)象是否包含有相應(yīng)的slot,。這兩個(gè)檢查的任何一個(gè)如果失敗的話,connect函數(shù)就失敗了,,返回false,。 前面的步驟都是在做一些必要的檢查工作,下一步,,就是要把發(fā)送signal的對(duì)象和響應(yīng)signal的對(duì)象關(guān)聯(lián)起來(lái),。在QObject的私有數(shù)據(jù)類QObjectPrivate中,有下面這些數(shù)據(jù)結(jié)構(gòu)來(lái)保存這些信息:
在發(fā)送signal的對(duì)象中,,每一個(gè)signal和slot的connection,,都會(huì)創(chuàng)建一個(gè)QObjectPrivate::Connection對(duì)象,并且把這個(gè)對(duì)象保存到connectionList這個(gè)Vector里面去,。 在響應(yīng)signal的對(duì)象中,,同樣,也是每一個(gè)signal和slot的connection,,都會(huì)一個(gè)創(chuàng)建一個(gè)Sender對(duì)象,,并且把這個(gè)對(duì)象附加在Senders這個(gè)列表中。
emit幕后的故事 當(dāng)emit signal的時(shí)候,,與這個(gè)signal相連的slot函數(shù)就會(huì)被調(diào)用,,那么這個(gè)調(diào)用時(shí)如何發(fā)生的呢,? 看段代碼:
每一個(gè)signal都會(huì)被轉(zhuǎn)換為一個(gè)與之相對(duì)應(yīng)的成員函數(shù)。也就是說(shuō),,當(dāng)我們寫下這樣一行代碼:emit sigBtnClicked();當(dāng)程序運(yùn)行到這里的時(shí)候,,實(shí)際上就是調(diào)用了void ZMytestObj::sigBtnClicked() 這個(gè)函數(shù)。 void ZMytestObj::sigMenuClicked() void ZMytestObj::sigBtnClicked(),,它們唯一的區(qū)別就是調(diào)用 QMetaObject::activate 函數(shù)時(shí)給出的參數(shù)不同,,一個(gè)是0,一個(gè)是1,,它們的含義是什么呢,?它們表示是這個(gè)類中的第幾個(gè)signal被發(fā)送出來(lái)了,回頭再去看頭文件就會(huì)發(fā)現(xiàn)它們就 是在這個(gè)類定義中,,signal定義出現(xiàn)的順序,,這個(gè)參數(shù)可是非常重要的,它直接決定了進(jìn)入這個(gè)函數(shù)體之后所發(fā)生的事情,。 當(dāng)執(zhí)行流程進(jìn)入到QMetaObject::activate函數(shù)中后,,會(huì)先從connectionLists這個(gè)變量中取出與這個(gè)signal相對(duì)應(yīng)的connection list,它根據(jù)的就是剛才所傳入進(jìn)來(lái)的signal index,。這個(gè)connection list中保存了所有和這個(gè)signal相鏈接的slot的信息,,每一對(duì)connection(即:signal和slot的連接)是這個(gè)list中的一項(xiàng)。 在每個(gè)一具體的鏈接記錄中,,還保存了這個(gè)鏈接的類型,,是自動(dòng)鏈接類型,還是隊(duì)列鏈接類型,,或者是阻塞鏈接類型,,不同的類型處理方法還不一樣的。
如果信號(hào)-槽連接方式為QueuedConnection,不論是否在同一個(gè)線程,,按隊(duì)列處理,。如果信號(hào)-槽連接方式為Auto,且不在同一個(gè)線程,,也按隊(duì)列處理,。 如果信號(hào)-槽連接方式為阻塞隊(duì)列BlockingQueuedConnection,按阻塞處理,。(注意同一個(gè)線程就不要按阻塞隊(duì)列調(diào)用了,。因?yàn)橥粋€(gè)線程,同時(shí)只能做一件事,,本身就是阻塞的,,直接調(diào)用就好了,,如果走阻塞隊(duì)列,則多了加鎖的過(guò)程,。如果槽中又發(fā)了同樣的信號(hào),,就會(huì)出現(xiàn)死鎖:加鎖之后還未解鎖,又來(lái)申請(qǐng)加鎖,。) 隊(duì)列處理,,就是把槽函數(shù)的調(diào)用,轉(zhuǎn)化成了QMetaCallEvent事件,,通過(guò)QCoreApplication::postEvent 放進(jìn)了事件循環(huán), 等到下一次事件分發(fā),,相應(yīng)的線程才會(huì)去調(diào)用槽函數(shù)。 對(duì)于直接鏈接的類型,,先找到接收這個(gè)signal的對(duì)象的指針,,然后是處理這個(gè)signal的slot的index,已經(jīng)是否有需要處理的參數(shù),,然后就使用這些信息去調(diào)用receiver的qt_metcall 方法。在qt_metcall方法中就簡(jiǎn)單了,,根據(jù)slot的index,,一個(gè)大switch語(yǔ)句,調(diào)用相應(yīng)的slot函數(shù)就OK了,。 |
|
來(lái)自: 懶人海馬 > 《學(xué)習(xí)》