大家好,我是20YC小二!福利时间:欢迎(wx)扫码关注,免费领取《C++程序员入门必修第一课:C++基础课程》在线视频教程,还有更多技术分享!#下面进入今天内容#
1. QObject 介绍
QObject 是 Qt 库中最重要的类之一。作为所有 Qt 类的基类,QObject 提供了信号槽机制、对象树、动态属性、元对象系统、事件处理机制、线程安全、国际化等许多重要特性,这些特性可以帮助开发者轻松实现模块间通信、组件化、程序设计以及事件处理等方面的功能。
QObject 的主要功能和特点包括:
- 信号槽机制:允许定义信号(signal)和槽(slot)来实现对象间的通信。一个信号可以连接到多个槽函数上,也可以将多个信号连接到同一个槽函数。
- 对象树:支持管理对象树结构的方式,允许对象包含一个父对象和零个或多个子对象。当父对象被删除时,其所有子对象也会被自动销毁。
- 动态属性:支持添加动态属性。除了一组已经存在的静态属性之外,每个对象实例还可以动态地创建和设置新的属性,这些属性不需要在编译器层面进行定义和声明。
- 元对象系统:为每个 QObject 和其子类提供运行时类型信息,包括对象的类名、属性、方法和信号等信息,这些信息都可以在运行时被访问或者修改。
- 事件处理机制:支持通过发送和接收事件来实现对象间的通信。Qt 中的事件是指某种对象发生的一些动作或状态变化,通常包含一个事件类型和一些参数。QObject 可以捕获并处理各种类型的事件,也可以派发事件给其他对象进行处理。
- 线程安全:被设计为线程安全的基础构建单元。这意味着 QObject 默认可以在多线程环境下直接使用,而无需考虑同步问题。
QObject 是 Qt 框架的核心,提供了许多基础和关键的功能,可以帮助开发者更轻松、更高效地构建 Qt 应用程序。
-
如何使用:
头文件:#include
cmake:find_package(Qt6 REQUIRED COMPONENTS Core)
target_link_libraries(mytarget PRIVATE Qt6::Core)
qmake:QT += core
-
QObject 派生类例子
#ifndef QOBJECTCHILDCLASS_H
#define QOBJECTCHILDCLASS_H
#include <QObject> // 包含头文件
// 继承于 QObject
class QObjectChildClass : public QObject
{
// 继承于 QObject 必须写的定义宏
Q_OBJECT
public:
// 构造函数
explicit QObjectChildClass(QObject *parent = nullptr)
: QObject{parent}
{
}
void sendSignal(const QString& text)
{
// 发射一个信号
emit sigDoSomething1(text);
}
// signals 用来修饰声明 QObjectChildClass 派生类的信号
signals:
// 信号
void sigDoSomething1(QString text);
// slots 用来修改声明槽函数
private slots:
void slotSomeFunction(int value) {}
};
#endif // QOBJECTCHILDCLASS_H
2. QObject 信号
// 当对象被销毁时,发射该信号。
void destroyed(QObject * obj = nullptr)
// 当对象名称修改时,发射该信号。
void objectNameChanged(const QString &objectName, QPrivateSignal)
3. QObject 基本属性
- objectName 对象名称:
// 访问函数
QString objectName() const
void setObjectName(const QString &name)
- isWidgetType 判断是否 Widget 对象:
// 访问函数
bool isWidgetType() const
- isWindowType 判断是否 Window 对象:
// 访问函数
bool isWindowType() const
- thread 对象线程
对象线程,简单理解就是信号槽函数在哪个线程上执行。
Qt 程序启动默认会创建一个主线程,用来处理主循环消息事件,所有继承于 QObject 类对象在创建时,如果没有指定线程,将默认跑在创建该对象的线程上,一般就是主线程。
// 返回对象所在的线程
QThread *QObject::thread() const
// 更改对象及其子对象的线程关联性。如果一个对象有父对象,则不能移动该对象到另一个线程中
void QObject::moveToThread(QThread *targetThread)
- parent 父对象:
// 访问函数
QObject *parent()
void setParent(QObject *parent)
QObject 对象的销毁会自动销毁所有子对象:
#include <QObject>
// 子对象类
class QChildClass : public QObject
{
Q_OBJECT
public:
QChildClass(QObject* parent)
QObject(parent)
{}
};
// 父对象类
class QParentClass : public QObject
{
Q_OBJECT
public:
QParentClass(QObject* parent = nullptr)
: QObject(parent)
{}
};
QParentClass* pParentClass = new QParentClass();
// 创建子对象,指定父对象 pParentClass
QChildClass* pChildClass = new QChildClass(pParentClass);
// ...
delete pParentClass; // 会自动销毁 pChildClass 子对象
//delete pChildClass; // 在后面 delete 会导致 double free 异常
- children 子对象列表:
// 返回对象所有子对象列表
const QObjectList &children() const
- property 对象属性:
QObject 的 property 对象属性是一个超好用的功能,可以用来存储用户自定义数据,满足各种业务逻辑需求的开发。所有 QObject 派生类都可以使用该属性。
// 访问函数
bool setProperty(const char *name, const QVariant &value)
QVariant property(const char *name) const
QList<QByteArray> dynamicPropertyNames() const
/**** 例子 ****/
QParentClass* pParentClass = new QParentClass();
// 设置属性值
pParentClass->setProperty("user_id", QVariant(100));
// ...
// 取出属性值
int i_user_id = pParentClass->property("user_id").toInt();
// ...
4. connect() 连接函数
connect() 连接函数用于把 signal 信号和 slot 槽函数连接到一起,当 signal 信号发射时,slot 槽函数会响应执行。不再需要的连接需要用 disconnect() 断开。
注意事项:需要避免多次 QObject::connect 连接,否则会有一次发射信号,多次执行槽函数的风险。
-
connect() 例子:
#include <QWidget>
#include <QPushButton>
class QConnectSample : public QWidget
{
Q_OBJECT
public:
QConnectSample(QWidget* parent = nullptr)
: QWidget(parent)
{
// 创建一个按钮
m_pPushButton = new QPushButton("点击我", this);
m_pPushButton->setGeometry(10, 10, 60, 32);
// 连接按钮 clicked 点击事件
QObject::connect(m_pPushButton, &QPushButton::clicked, this, &QConnectSample::slotPushButtonClicked);
}
~QConnectSample()
{
QObject::disconnect(m_pPushButton, &QPushButton::clicked, this, &QConnectSample::slotPushButtonClicked);
}
public Q_SLOTS:
// 按钮点击处理函数
void slotPushButtonClicked(bool checked = false)
{
// ...
}
private:
QPushButton * m_pPushButton = nullptr;
};
-
connect() 连接第五个参数:
一共有五种 connect 连接类型 Qt::ConnectionType:
// Qt::ConnectionType 这个枚举描述了在信号和槽之间的连接类型。
// 它决定一个特定信号被输送到槽函数,是立即执行,还是在放到队列排队执行。
// 五种连接类型如下:
enum ConnectionType {
AutoConnection, // (默认)自动根据信号发送的线程,与执行槽函数线程来选择
DirectConnection, // 立即执行
QueuedConnection, // 队列排队执行
BlockingQueuedConnection, // 与 Qt::QueuedConnection 相同,但会阻塞信号发送线程,直到槽函数执行返回
UniqueConnection = 0x80 // 唯一标识;可以和以上任意连接类型组合,用按位或(|)。当设置了Qt::UniqueConnection,QObject::connect()函数如果连接已经存在(信号、槽函数和对象都相同),将返回失败。
};
5. deleteLater() 延迟销毁函数
调用 deleteLater() 函数不会立即销毁对象,而是在下一个主消息循环才自动销毁该对象,可以减少内存泄露和 double free 异常风险。
/**** 例子 ****/
QPushButton *p_push_button = new QPushButton(this);
// ...
// 当不需要的时候,调用 deleteLater 自动销毁对象
p_push_button->deleteLater();
6. QObject 事件处理
-
事件接收器:
重写 QObject::event() 虚函数,用于接收对象事件,如果事件被处理,则返回 true
virtual bool event(QEvent *event)
-
事件过滤器:
利用注册事件过滤器,可以捕捉处理指定对象事件。
// 注册事件过滤器
void installEventFilter(QObject *filterObj)
// 移除事件过滤器
void removeEventFilter(QObject *obj)
// 重写 QObject::eventFilter() 虚函数,用于接收注册过滤对象的事件;如果事件被处理,则返回 true
virtual bool eventFilter(QObject *watched, QEvent *event)
7. QObject 对象内置定时器
Qt 定时器的主要作用是定时执行特定的任务。它提供了简洁、易于使用的接口,使开发人员可以轻松创建和管理定时器对象。Qt 定时器能够提供高精度的计时能力,通常以毫秒级别为单位,以满足应用程序的需求。
-
启动定时器函数:
/**
* @brief 启动定时器
* @param [in]int interval 定时器间隔时间,单位毫秒
* @param [in]Qt::TimerType timerType 定时器类型(精度)
* - Qt::PreciseTimer -> 精确的精度, 毫秒级
* - Qt::CoarseTimer -> 粗糙的精度, 和1毫秒的误差在5%的范围内, 默认精度
* - Qt::VeryCoarseTimer -> 非常粗糙的精度, 精度在1秒左右
* @return 返回唯一定时器编号
**/
int startTimer(int interval, Qt::TimerType timerType = Qt::CoarseTimer);
-
杀死定时器函数:
/**
* @brief 杀死定时器
* @param [in]int id 定时器编号
* @return void
**/
void killTimer(int id)
-
定时器处理函数:
// 重写 QObject::timerEvent 虚函数,用于处理定时器
virtual void timerEvent(QTimerEvent *event)
-
定时器例子:
#include <QObject>
#include <QTimerEvent>
class QStartTimerSample : public QObject
{
Q_OBJECT
public:
QStartTimerSample(QObject* parent = nullptr)
: QObject(parent)
{
// 启动1秒定时器;1000ms=1秒
m_iTimerId = QObject::startTimer(1000, Qt::PreciseTimer);
}
~QStartTimerSample()
{
// 杀死1秒定时器
QObject::killTimer(m_iTimerId);
}
private:
// 定时器执行函数
virtual void timerEvent(QTimerEvent *event)
{
if (event->timerId() == m_iTimerId)
{
// 1秒定时器ID
// ...
}
QObject::timerEvent(event);
}
private:
int m_iTimerId = 0; // 1秒定时器ID
};