首页 > 其他分享 >Qt | 一文总结QObject

Qt | 一文总结QObject

时间:2024-04-13 22:23:24浏览次数:25  
标签:一文 对象 QObject 线程 Qt 连接 函数

Qt | 一文总结QObject

来源 https://zhuanlan.zhihu.com/p/608004472

 

一、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 */
void QObject::installEventFilter(QObject *filterObj) 

/* 这个虚拟函数接收对象的事件,如果事件e被识别并处理,则返回true */
bool QObject::event(QEvent *e)

2、对象的线程关联API

/* 返回对象所在的线程。 */
QThread *QObject::thread() const  
    
/* 更改对象及其子对象的线程关联性。如果一个对象有父对象,则不能移动该对象到另一个线程中 */
void QObject::moveToThread(QThread *targetThread) 
    

3、获取子对象API

/* 返回该对象具有给定名称的所有可转换为类型T的子对象,如果没有此类对象,则返回一个空列表 */
QList<T> findChildren(const QString &name = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
    
/* 返回子对象列表 */
const QObjectList & children() const

4、qobject_cast

函数原型如下:

T qobject_cast(QObject* object)
T qobject_cast(const QObject* object)

如果对象类型为T(或子类),则将给定的对象转换为类型T;否则返回nullptr。如果对象是nullptr,那么它也将返回nullptr。

注意:类T必须继承(直接或间接)QObject并使用Q_OBJECT宏声明。

qobject_cast()函数的行为类似于标准c++ dynamic_cast(),它的优点是不需要RTTI(Run-Time Type Identification-运行时类型识别)支持,并且可以跨动态库边界工作。

5、事件处理相关函数

//此虚函数用于接收对象的事件,如果事件e被识别和处理,则返回true。
virtual bool event(QEvent *e)

//如果此对象已作为被监视对象的事件过滤器安装,则过滤事件。
virtual bool eventFilter(QObject *watched, QEvent *event)

//从该对象中移除事件筛选器对象obj。
void removeEventFilter(QObject *obj)

//在对象上安装事件筛选器filterObj
void installEventFilter(QObject *filterObj)

6、定时器相关函数

//启动计时器并返回计时器标识符
int startTimer(int interval, Qt::TimerType timerType = Qt::CoarseTimer)

//启动计时器并返回计时器标识符
int startTimer(std::chrono::milliseconds time, Qt::TimerType timerType = Qt::CoarseTimer)

//使用定时器标识符id终止计时器
void killTimer(int id)

7、重要宏定义

  • Q_DISABLE_COPY(Class)
    禁止对给定类使用复制构造函数和赋值运算符。
  • Q_DISABLE_COPY_MOVE(Class)

​ 该宏用于禁用给定类的复制构造函数、赋值运算符、移动构造函数和移动赋值运算符,将Q_DISABLE_COPYQ_DISABLE_MOVE组合在一起。

  • Q_DISABLE_MOVE(Class 5.13)

​ 禁止对给定类使用移动构造函数和移动赋值操作符。

  • Q_EMIT

​ 当希望使用第三方信号/槽函数机制来使用Qt信号和槽函数时,可以使用此宏替代emit关键字来发出信号。

  • Q_ENUM(...)

​ 这个宏用于向元对象系统注册一个枚举类型。该宏必须放在enum声明之后,且放在具有Q_OBJECTQ_GADGET宏的类中。对于命名空间,应该使用Q_ENUM_NS()。例如:

class MyClass : public QObject
{
    Q_OBJECT

public:
    MyClass(QObject *parent = nullptr);
    ~MyClass();

    enum Priority { High, Low, VeryHigh, VeryLow };
    Q_ENUM(Priority)
    void setPriority(Priority priority);
    Priority priority() const;
};
  • Q_ENUM_NS(...)

​ 这个宏向元对象系统注册一个枚举类型。它必须放在enum声明之后,,且具有Q_NAMESPACE宏的名称空间中。与Q_ENUM相同,但在命名空间中。

  • Q_FLAG(...)

​ 这个宏向元对象系统中注册一个单标记类型。它通常用于类定义中,以声明给定enum的值可以用作标志,并使用按位或运算符进行组合。对于命名空间,应该使用Q_FLAG_NS()。例如:

class QLibrary : public QObject
{
    Q_OBJECT

public:
    ...

    enum LoadHint {
        ResolveAllSymbolsHint = 0x01,
        ExportExternalSymbolsHint = 0x02,
        LoadArchiveMemberHint = 0x04
    };
    Q_DECLARE_FLAGS(LoadHints, LoadHint)
    Q_FLAG(LoadHint)
    ...
}
  • Q_INTERFACES(...)

​ 这个宏告诉Qt这个类实现了哪些接口。在宏定义在实现插件的时候使用。例如:

class BasicToolsPlugin : public QObject,
                         public BrushInterface,
                         public ShapeInterface,
                         public FilterInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.PlugAndPaint.BrushInterface" FILE "basictools.json")
    Q_INTERFACES(BrushInterface ShapeInterface FilterInterface)

public:
    ...
};
  • Q_NAMESPACE

​ Q_NAMESPACE宏用于将QMetaObject功能添加到名称空间。Q_NAMESPACE可以有Q_CLASSINFOQ_ENUM_NS、Q_FLAG_NS,但不能有Q_ENUMQ_FLAGQ_PROPERTYQ_INVOKABLE、信号或槽函数。

  • Q_NAMESPACE_EXPORT(EXPORT_MACRO)

​ 该宏的工作原理与Q_NAMESPACE宏完全相同。但是,在名称空间中定义的外部staticMetaObject变量是用提供的EXPORT_MACRO限定符声明的。如果需要从动态库导出对象,该宏定义非常有用。

  • Q_OBJECT

​ Q_OBJECT宏必须出现在类定义的私有部分中,该类定义声明自己的信号和槽函数,或者使用Qt元对象系统提供的其他支持。

  • Q_PROPERTY(...)

​ 此宏用于在继承QObject的类中声明属性。属性的行为类似于类数据成员,但它们具有可通过元对象系统访问的其他特性。如下代码:

Q_PROPERTY(type name
           (READ getFunction [WRITE setFunction] |
            MEMBER memberName [(READ getFunction | WRITE setFunction)])
           [RESET resetFunction]
           [NOTIFY notifySignal]
           [REVISION int]
           [DESIGNABLE bool]
           [SCRIPTABLE bool]
           [STORED bool]
           [USER bool]
           [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()时,对象的成员变量将保持在旧线程中。

『参考链接』:

【1】https://doc.qt.io/qt-5/threads-qobject.html#signals-and-slots-across-threads

【2】https://doc.qt.io/qt-5/qobject.

 

========== End

 

 

标签:一文,对象,QObject,线程,Qt,连接,函数
From: https://www.cnblogs.com/lsgxeva/p/18133478

相关文章

  • 【Qt】编写的qt程序,如何把依赖的dll自动拷贝到exe同级目录
    如果你的Qt程序在VisualStudio中可以正常启动但直接启动exe文件会报错,可能是因为缺少依赖的QtDLL文件导致的。为了确定缺少了哪些DLL文件,你可以尝试以下方法:使用DependencyWalker:DependencyWalker是一个工具,可以帮助你分析可执行文件的依赖关系,并找出缺失的D......
  • 2.创建Qt项目
    2.创建Qt项目2.1使用向导创建打开QtCreator界面选择NewProject或者选择菜单栏【文件】-【新建文件或项目】菜单项弹出NewProject对话框,选择QtWidgetsApplication,选择【Choose】按钮,弹出如下对话框设置项目名称和路径,按照向导进行下一步,选择编译套件向导会默认......
  • ESP32 Arduino开发 MQTT
    ESP32Arduino开发MQTT目录ESP32Arduino开发MQTT1.安装程序库2.编写相关程序2.1.引入头文件2.2.定义MQTT相关参数2.3.创建对象2.4.连接网络2.5.连接MQTT服务器2.6.MQTT回调函数3.完整的代码例程4.MQTT连接测试1.安装程序库打开库管理工具工具->管理库.........
  • 通过 aqtinstall 安装 Qt5 的库
    QtMaintenanceTool(QtOnlineInstaller)可能没有Qt5的安装选项了,但是从Qt官网下载的qt-opensource-windows-x86-5.14.2.exe安装器在安装的时候会安装旧的QtCreator。如果你不想要旧的QtCreator并且需要的版本不是Qt5.14.2,安装起来可能不是很方便。有一个命令行工具可......
  • 短视频app源码,一文带你轻松搞懂前端大文件上传思路
    短视频app源码,一文带你轻松搞懂前端大文件上传思路文件上传是我们在平时开发短视频app源码中经常会遇到的业务,如果只是简单的文件上传那还不足以作为项目亮点,而当我们给它加上切片、续传的功能,就不一样了。本文会带大家搞明白这些功能的实现思路,主要聚焦于前端部分,基于Vue3......
  • windows下的重装是最笨也是最简单的重复解决问题的方法,解决goldendict的qt库链接问题
    goldendict✔goldendict:symbollookuperror:goldendict:undefinedsymbol:_ZN10QAudioSink12stateChangedEN6QAudio5StateE,versionQt_6~yay-Sgoldendict......
  • CANXL标准发布 | (1)一文读懂CANXL数据链路层总线,通信再进化!
        2024年3月22日,期待已久的ISO11898-2:2024版本横空出世,标志着新一代CAN总线的时代即将到来(ISO/FDIS11898-1:2023也处于最终投票阶段)。搭载着2048Byte的CANXL列车已蓄势待发,决心突破10M的高速通道。Autosar、英飞凌、ST、Vector和NXP等行业风向标早已准备迎接CANXL高速通......
  • Qt程序加载Qt platform plugin 'xcb' 出错问题解决
    1.Qt程序运行环境ubuntu16.04Qt5.12.3Qt可执行程序编译后运行Qt可执行程序后出现报错报错内容:qt.qpa.plugin:CouldnotloadtheQtplatformplugin"xcb"in""eventhoughitwasfound.ThisapplicationfailedtostartbecausenoQtplatformplugincouldbe......
  • VS studio 上开发Qt——Qt VS studio Tool的使用
    1.VS上安装Qt扩展点击菜单栏【扩展】->【管理扩展】,在搜索框搜索“Qt”,点击下载QtVisualStudioTools,以2022版为例,需要关闭所有窗口才能执行安装关闭VS后,弹出安装窗口,等待其安装完成2.新建QT工程测试等待安装完成后,添加一个QtVertion后(前提已经安装了Qt开发环境)即可......
  • Qt 如何遍历序列容器(QVector|QMap|...)
    QT提供了两种风格的遍历器:Java和STL一、Java风格遍历器Java风格的遍历器是Qt首先推荐使用的形式。这种风格比起STL风格的遍历器更方便。方便的代价就是不如后者高效。Java风格的遍历器指向的是两个元素之间的位置,而不是指向元素本身。因此,它们可能会指向集合第一......