一、QObject的重要知識
QObject
是Qt對象模型的核心。這個模型的核心特性是一個強大的無縫對象通信機制,即信號和槽。可以使用connect()
將信號連接到槽函數,并使用disconnect()
破壞已經存在的連接。為了避免永不結束的通知循環,可以使用blockSignals()
暫時阻塞信號。受保護的函數connectNotify()
和disconnectNotify()
可以用于跟蹤信號連接。
Qt中,以QObject
為基礎形成了一棵“對象樹”,當使用另一個對象作為父對象創建QObject
時,該對象將自動將自己添加到父對象的children()
列表中。此后父對象擁有該對象的所有權,則會自動刪除析構函數中的子元素。在開發中可以使用findChild()
或findChildren()
根據名稱和可選的類型查找子對象。
每個對象都有一個objectName()
,可以通過相應的metaObject()
找到它的類名(函數:QMetaObject::className()
)。在實際開發中可以使用inherits()
函數確定對象的類是否繼承了QObject
繼承層次結構中的另一個類。當一個對象被刪除時,會發出destroyed()
信號,通過這一點可以捕獲此信號,避免對QObject
進行懸掛引用。
二、QObject重要成員函數
本小節總結在開發中,QObject中常使用的成員函數和重要宏定義。
1、事件獲取和處理API
/*在此對象上安裝事件篩選器filterObj*/
voidQObject::installEventFilter(QObject*filterObj)
/*這個虛擬函數接收對象的事件,如果事件e被識別并處理,則返回true*/
boolQObject::event(QEvent*e)
2、對象的線程關聯API
/*返回對象所在的線程。*/
QThread*QObject::thread()const
/*更改對象及其子對象的線程關聯性。如果一個對象有父對象,則不能移動該對象到另一個線程中*/
voidQObject::moveToThread(QThread*targetThread)
3、獲取子對象API
/*返回該對象具有給定名稱的所有可轉換為類型T的子對象,如果沒有此類對象,則返回一個空列表*/
QListfindChildren(constQString&name=QString(),Qt::FindChildOptionsoptions=Qt::FindChildrenRecursively)const
/*返回子對象列表*/
constQObjectList&children()const
4、qobject_cast
函數原型如下:
Tqobject_cast(QObject*object)
Tqobject_cast(constQObject*object)
如果對象類型為T(或子類),則將給定的對象轉換為類型T;否則返回nullptr。如果對象是nullptr,那么它也將返回nullptr。
注意:類T必須繼承(直接或間接)
QObject
并使用Q_OBJECT
宏聲明。
qobject_cast()
函數的行為類似于標準c++ dynamic_cast()
,它的優點是不需要RTTI
(Run-Time Type Identification-運行時類型識別)支持,并且可以跨動態庫邊界工作。
5、事件處理相關函數
//此虛函數用于接收對象的事件,如果事件e被識別和處理,則返回true。
virtualboolevent(QEvent*e)
//如果此對象已作為被監視對象的事件過濾器安裝,則過濾事件。
virtualbooleventFilter(QObject*watched,QEvent*event)
//從該對象中移除事件篩選器對象obj。
voidremoveEventFilter(QObject*obj)
//在對象上安裝事件篩選器filterObj
voidinstallEventFilter(QObject*filterObj)
6、定時器相關函數
//啟動計時器并返回計時器標識符
intstartTimer(intinterval,Qt::TimerTypetimerType=Qt::CoarseTimer)
//啟動計時器并返回計時器標識符
intstartTimer(std::millisecondstime,Qt::TimerTypetimerType=Qt::CoarseTimer)
//使用定時器標識符id終止計時器
voidkillTimer(intid)
7、重要宏定義
-
Q_DISABLE_COPY(Class)
禁止對給定類使用復制構造函數和賦值運算符。
-
Q_DISABLE_COPY_MOVE(Class)
該宏用于禁用給定類的復制構造函數、賦值運算符、移動構造函數和移動賦值運算符,將Q_DISABLE_COPY
和Q_DISABLE_MOVE
組合在一起。
- Q_DISABLE_MOVE(Class 5.13)
禁止對給定類使用移動構造函數和移動賦值操作符。
- Q_EMIT
當希望使用第三方信號/槽函數機制來使用Qt信號和槽函數時,可以使用此宏替代emit
關鍵字來發出信號。
- Q_ENUM(...)
這個宏用于向元對象系統注冊一個枚舉類型。該宏必須放在enum
聲明之后,且放在具有Q_OBJECT
或Q_GADGET
宏的類中。對于命名空間,應該使用Q_ENUM_NS()
。例如:
classMyClass:publicQObject
{
Q_OBJECT
public:
MyClass(QObject*parent=nullptr);
~MyClass();
enumPriority{High,Low,VeryHigh,VeryLow};
Q_ENUM(Priority)
voidsetPriority(Prioritypriority);
Prioritypriority()const;
};
- Q_ENUM_NS(...)
這個宏向元對象系統注冊一個枚舉類型。它必須放在enum
聲明之后,,且具有Q_NAMESPACE
宏的名稱空間中。與Q_ENUM
相同,但在命名空間中。
- Q_FLAG(...)
這個宏向元對象系統中注冊一個單標記類型。它通常用于類定義中,以聲明給定enum
的值可以用作標志,并使用按位或運算符進行組合。對于命名空間,應該使用Q_FLAG_NS()
。例如:
classQLibrary:publicQObject
{
Q_OBJECT
public:
...
enumLoadHint{
ResolveAllSymbolsHint=0x01,
ExportExternalSymbolsHint=0x02,
LoadArchiveMemberHint=0x04
};
Q_DECLARE_FLAGS(LoadHints,LoadHint)
Q_FLAG(LoadHint)
...
}
- Q_INTERFACES(...)
這個宏告訴Qt這個類實現了哪些接口。在宏定義在實現插件的時候使用。例如:
classBasicToolsPlugin:publicQObject,
publicBrushInterface,
publicShapeInterface,
publicFilterInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID"org.qt-project.Qt.Examples.PlugAndPaint.BrushInterface"FILE"basictools.json")
Q_INTERFACES(BrushInterfaceShapeInterfaceFilterInterface)
public:
...
};
- Q_NAMESPACE
Q_NAMESPACE
宏用于將QMetaObject
功能添加到名稱空間。Q_NAMESPACE
可以有Q_CLASSINFO
、Q_ENUM_NS
、Q_FLAG_NS,但不能有Q_ENUM
、Q_FLAG
、Q_PROPERTY
、Q_INVOKABLE
、信號或槽函數。
- Q_NAMESPACE_EXPORT(EXPORT_MACRO)
該宏的工作原理與Q_NAMESPACE
宏完全相同。但是,在名稱空間中定義的外部staticMetaObject
變量是用提供的EXPORT_MACRO
限定符聲明的。如果需要從動態庫導出對象,該宏定義非常有用。
- Q_OBJECT
Q_OBJECT宏必須出現在類定義的私有部分中,該類定義聲明自己的信號和槽函數,或者使用Qt元對象系統提供的其他支持。
- Q_PROPERTY(...)
此宏用于在繼承QObject
的類中聲明屬性。屬性的行為類似于類數據成員,但它們具有可通過元對象系統訪問的其他特性。如下代碼:
Q_PROPERTY(typename
(READgetFunction[WRITEsetFunction]|
MEMBERmemberName[(READgetFunction|WRITEsetFunction)])
[RESETresetFunction]
[NOTIFYnotifySignal]
[REVISIONint]
[DESIGNABLEbool]
[SCRIPTABLEbool]
[STOREDbool]
[USERbool]
[CONSTANT]
[FINAL])
- Q_SIGNAL
這是一個額外的宏,允許我們將單個函數標記為信號。當使用不支持信號或Q_SIGNALS
組的第三方源代碼解析器時,使用這個宏。
- Q_SLOT
這是一個額外的宏,它允許將單個函數標記為槽函數。當使用不支持槽函數或Q_SLOT
組的第三方源代碼解析器時,使用這個宏。
三、信號和槽的連接機制注意事項
1、【自動連接(默認)】如果信號是在接收對象具有關聯的線程中發出的,那么行為與直接連接相同。否則,行為與隊列連接相同。
2、【直接連接】當信號發出時,槽函數被立即調用。槽函數在發射器的線程中執行,而發射器的線程不一定是接收器的線程。
3、【隊列連接】當控制返回到接收方線程的事件循環時調用槽,槽函數在接收方的線程中執行。
4、【阻塞排隊連接】槽被調用為排隊連接,除非當前線程阻塞直到槽函數返回。
注意:使用該阻塞排隊連接類型連接同一線程中的對象將導致死鎖。
5、【唯一連接】與自動連接相同,但只在不復制現有連接的情況下才建立連接。也就是說,如果相同的信號已經為相同的對象連接到相同的槽,那么就不創建連接,并且connect()
返回false
。
連接類型可以通過向connect()
傳遞一個附加參數來指定。注意,如果事件循環運行在接收方的線程中,當發送方和接收方位于不同線程中時使用直接連接是不安全的,這與調用位于另一個線程中的對象上的函數是不安全的原因相同。
QObject::connect()本身是線程安全的。
對于隊列連接,傳遞的參數必須是Qt元對象系統已知的類型,因為Qt需要復制參數,以便存儲參數;如果參數類型不是Qt元對象系統已知的,使用隊列連接,將獲得錯誤提示信息,這時候則需要在創建連接之前,調用qRegisterMetaType()
向元對象系統注冊該數據類型。
四、線程關聯性
在Qt中,QObject實例具有線程相關性,或者可以理解成QObject
存在于某個線程中。當QObject接收到排隊的信號或發布的事件時,槽函數或事件處理程序將在該對象所在的線程中運行,這一點很重要。
注意:如果一個QObject沒有線程關聯(也就是說,如果
thread()
返回0),或者如果它存在于一個沒有運行事件循環的線程中,那么它就不能接收排隊的信號或發布的事件。
默認情況下,QObject實例存在于創建它的線程中,在實際開發中可使用thread()
查詢對象的線程關聯,并使用moveToThread()
更改對象的線程關聯。
注意:所有QObject必須與它們的父對象生活在同一個線程中。除此之外,還需要知道:
(1)如果涉及的兩個QObject位于不同的線程中,setParent()
將失敗。
(2)當一個QObject被移動到另一個線程時,它的所有子線程也會被自動移動。
(3)如果QObject有一個父對象,moveToThread()
將失敗。
(4)如果QObject是在QThread::run()
中創建的,它們不能成為QThread對象的子對象,因為QThread并不存在于調用QThread::run()
的線程中。
QObject的父子關系必須通過傳遞一個指向子構造函數的指針或調用
setParent()
來設置。如果沒有這個步驟,當調用moveToThread()時,對象的成員變量將保持在舊線程中。
審核編輯 :李倩
-
API
+關注
關注
2文章
1485瀏覽量
61814 -
函數
+關注
關注
3文章
4304瀏覽量
62429 -
Qt
+關注
關注
1文章
301瀏覽量
37829
原文標題:Qt的萬能承載者QObject,了解多少?
文章出處:【微信號:嵌入式小生,微信公眾號:嵌入式小生】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論