《OpenCV》Part11 OpenCV3.1.0 Qt中的信號(hào)槽函數(shù)傳遞cv::Mat格式參數(shù)
一、信號(hào)傳送問(wèn)題
1、寫(xiě)Qt的多線程程序,用到信號(hào)槽函數(shù)傳遞cv::Mat格式的參數(shù),編譯通過(guò),但是debug時(shí)提示下列錯(cuò)誤:
QObject::connect: Cannot queue arguments of type 'Mat'(Make sure 'Mat' is registered using qRegisterMetaType().)意思是說(shuō),信號(hào)槽隊(duì)列中的數(shù)據(jù)類(lèi)型必須是系統(tǒng)能識(shí)別的元類(lèi)型,不然得用qRegisterMetaType()進(jìn)行注冊(cè)。原因:當(dāng)一個(gè)signal被放到隊(duì)列中(queued)時(shí),它的參數(shù)(arguments)也會(huì)被一起一起放到隊(duì)列中(queued起來(lái)),這就意味著參數(shù)在被傳送到slot之前需要被拷貝、存儲(chǔ)在隊(duì)列中(queue)中;為了能夠在隊(duì)列中存儲(chǔ)這些參數(shù)(argument),Qt需要去construct、destruct、copy這些對(duì)象,而為了讓Qt知道怎樣去作這些事情,參數(shù)的類(lèi)型需要使用qRegisterMetaType來(lái)注冊(cè)(如錯(cuò)誤提示中的說(shuō)明)步驟:(以自定義TextAndNumber類(lèi)型為例)自定一種類(lèi)型,在這個(gè)類(lèi)型的頂部包含:#include <QMetaType>在類(lèi)型定義完成后,加入聲明:Q_DECLARE_METATYPE(TextAndNumber);在main()函數(shù)中注冊(cè)這種類(lèi)型:qRegisterMetaType<TextAndNumber>("TextAndNumber");如果還希望使用這種類(lèi)型的引用,可同樣要注冊(cè):qRegisterMetaType<TextAndNumber>("TextAndNumber&");
#include <QMetaType> //必須包含QMetaType,否則會(huì)出現(xiàn)下面錯(cuò)誤: //error: expected constructor, destructor, or type conversion before ‘;’ token #include <QString> class TextAndNumber { public: TextAndNumber(); TextAndNumber(int, QString); int count(); QString text(); PRivate: int m_count; QString m_text; }; Q_DECLARE_METATYPE(TextAndNumber); #endif // TEXTANDNUMBER_H int main(int argc, char *argv[]) { Qapplication app(argc, argv); qRegisterMetaType<TextAndNumber>("TextAndNumber"); qRegisterMetaType<TextAndNumber>("TextAndNumber&"); TextDevice device; TextThread foo("foo"), bar("bar"); QObject::connect(&foo, SIGNAL(writeText(TextAndNumber&)), &device, SLOT(write(TextAndNumber&))); QObject::connect(&bar, SIGNAL(writeText(TextAndNumber&)), &device, SLOT(write(TextAndNumber&)));上面是解決方法和步驟。網(wǎng)上對(duì)這個(gè)問(wèn)題的解決有兩種方案,除了上述的方法外,還有一種簡(jiǎn)單粗暴的方法,但是可能會(huì)造成風(fēng)險(xiǎn)。———————————————————————————————————————————————————
二、解決方法:第一種注冊(cè)法:qRegisterMetatType<MoSystemLog>("MoSystemLog")第二種修改Connect,加一個(gè)屬性Qt::directConnection.connect(cm, SIGNAL(sendLog(QUuid, QByteArray, bool)),this,SLOT(sendRes(QUuid,QByteArray,bool)),
Qt::DirectConnection);三、方法解釋?zhuān)?、第一種解決方法,即使用排隊(duì)方式的信號(hào)-槽機(jī)制,Qt的元對(duì)象系統(tǒng)(meta-object system)必須知道信號(hào)傳遞的參數(shù)類(lèi)型。這樣元系統(tǒng)可以將信號(hào)參數(shù)COPY下來(lái),放在隊(duì)列中等待事件喚醒,供槽函數(shù)調(diào)用。Just a note here, if you would have to pass custom data types between threads in Qt. As we know, a signal-slot connection is then (by default) of type Qt::QueuedConnection. Because in such a situation Qt needs to store passed parameters for a while, it creates their temporary copies. If it doesn’t recognize the passed data type, throws out an error:2、第二種方法,直接調(diào)用對(duì)方槽函數(shù),不需要保存參數(shù)。但是官方認(rèn)為這樣做有風(fēng)險(xiǎn)。四、背景知識(shí):1、首先要了解enum Qt::ConnectionTypeQt支持6種連接方式,其中3種最主要:Qt::DirectConnection(直連方式)當(dāng)信號(hào)發(fā)出后,相應(yīng)的槽函數(shù)將立即被調(diào)用。emit語(yǔ)句后的代碼將在所有槽函數(shù)執(zhí)行完畢后被執(zhí)行。(信號(hào)與槽函數(shù)關(guān)系類(lèi)似于函數(shù)調(diào)用,同步執(zhí)行)Qt::QueuedConnection(排隊(duì)方式)當(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í)行完畢。(此時(shí)信號(hào)被塞到信號(hào)隊(duì)列里了,信號(hào)與槽函數(shù)關(guān)系類(lèi)似于消息通信,異步執(zhí)行)Qt::AutoConnection(自動(dòng)方式)Qt的默認(rèn)連接方式,如果信號(hào)的發(fā)出和接收這個(gè)信號(hào)的對(duì)象同屬一個(gè)線程,那個(gè)工作方式與直連方式相同;否則工作方式與排隊(duì)方式相同。
Qt官方文檔中寫(xiě)明:With queued connections, the parameters must be of types that are known to Qt's meta-object system, because Qt needs to copy the arguments to store them in an event behind the scenes. If you try to use a queued connection and get the error messageQObject::connect: Cannot queue arguments of type 'MyType'callqRegisterMetaType() to register the data type before you establish the connection.注意上面紫色的內(nèi)容。connect的第五個(gè)參數(shù)用于指定反應(yīng)速度:若將:connect(cm, SIGNAL(sendLog(QUuid, QByteArray, bool)),this,SLOT(sendRes(QUuid,QByteArray,bool)));改為:connect(cm, SIGNAL(sendLog(QUuid, QByteArray, bool)),this,SLOT(sendRes(QUuid,QByteArray,bool)), Qt::DirectConnection);可解決因信號(hào)沒(méi)有及時(shí)發(fā)送,致使connect的接收方的槽不能作進(jìn)一步處理,但是有風(fēng)險(xiǎn)。
———————————————————————————————————————————————————
對(duì)于結(jié)構(gòu)體的信號(hào)槽傳輸,這里也給出一個(gè)例子。
解決方案:將結(jié)構(gòu)體搬出類(lèi),而不是寫(xiě)在類(lèi)public里面,可以寫(xiě)在同一個(gè)頭文件中。例子:
#include <QtGui/QApplication>#include <QVariant> //*.hstructstruct1{ inta; doubleb;}; structstruct2{ struct1 s; intc;}; Q_DECLARE_METATYPE(struct1) //struct1與struct2 誰(shuí)先誰(shuí)后,沒(méi)有影響Q_DECLARE_METATYPE(struct2) class Data(){ public: int x,y;} // *.cpp int main(int argc,char *argv[]){ QApplication a(argc, argv); struct1 v1 = {1, 2.0}; QVariant var1; var1.setValue(v1); if(var1.canConvert<struct1>()) //判斷能否轉(zhuǎn)化為相應(yīng)類(lèi)型 { struct1 v11 = var1.value<struct1>(); } struct2 v2 = {{2, 3.0}, 5}; QVariant var2; if(var2.canConvert<struct2>()) { var2.setValue(v2); struct2 v22 = var2.value<struct2>(); } returna.exec();}結(jié)束語(yǔ):將結(jié)構(gòu)體搬出類(lèi),而不是寫(xiě)在類(lèi)public里面,可以寫(xiě)在同一個(gè)頭文件中。這個(gè)注冊(cè)函數(shù)暫時(shí)還沒(méi)用到,看看宏定義,Q_DECLARE_METATYPE(),里面好像已近帶了這個(gè)注冊(cè)功能。qRegisterMetaType<QVariant>("QVariant"); //寫(xiě)在構(gòu)造函數(shù)里
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注