自学内容网 自学内容网

QT元对象系统特性详细介绍(信号槽、类型信息、动态设置属性)(注释)

目  录

一、元对象系统简介

二、信号和槽

三、类型信息

四、动态设置属性


一、元对象系统简介

QT中的元对象系统Q_OBJECT并不是C++标准代码,因此在使用时需要QT的MOC(元对象编译器)进行预处理,MOC会在编译时期读取C++代码中的特定宏(如Q_OBJECT),再由标准的C++编译器进行重新编译。

Q_OBJECT的使用:必须要在类中定义元对象系统Q_OBJECT 宏才能使用(在类定义时,如果没有指定public或者private,则默认为private(私有))。程序运行时,moc会扫描此类,并生成元对象信息,包括但不限于类名、父类、属性、信号、槽函数等;

Q_OBJECT的特性:

  • 类型信息:Qt使用元对象系统来存储关于对象的信息,如类名和父类。
  • 属性系统:支持动态的属性机制,允许在运行时查询和修改对象的属性。
  • 信号和槽的动态连接:元对象系统允许在运行时创建和解除信号与槽之间的连接。

二、信号和槽

(1)信号与槽是对象间进行通信的机制,使用QObject::connect函数连接信号和槽时,元对象系统会在运行时查找信号(signals)和槽(slots),并建立连接。使用方式如下:定义两个类,名为QtWidgetsApplication3、obj,互相通信。

QtWidgetsApplication3.h

#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_QtWidgetsApplication3.h"
#include <QObject> //包含头文件,以使用信号槽

class QtWidgetsApplication3 : public QMainWindow
{
    //启用Qt的元对象系统,允许使用信号和槽等特性
    Q_OBJECT //默认私有
public:
    QtWidgetsApplication3(QWidget *parent = nullptr);
    ~QtWidgetsApplication3();
private:
    Ui::QtWidgetsApplication3Class ui;
signals:    //声明信号
    void testsignal();
};

class obj : public QObject //继承QObject使用信号槽
{
    //启用Qt的元对象系统,允许使用信号和槽等特性
    Q_OBJECT 
public: //声明构造函数,接受一个QObject指针作为父对象
    obj(QObject* parent = nullptr);
public slots: //声明槽
    void testslot();
};

QtWidgetsApplication3.cpp

#include "QtWidgetsApplication3.h"
QtWidgetsApplication3::QtWidgetsApplication3(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
    obj* qobj = new obj(this);  //创建obj的实例
    QObject::connect(this,&QtWidgetsApplication3::testsignal, qobj,&obj::testslot); //连接信号和槽
    emit testsignal();          //触发信号
}

void obj::testslot()
{   qDebug() << "obj::testslot()";   }

QtWidgetsApplication3::~QtWidgetsApplication3()
{}

obj::obj(QObject* parent)
{}

 运行效果:

(2)信号和槽的五种写法与使用方式

(3)信号和槽之间的关系:

1、信号的参数类型必须与槽函数参数的类型相对应

signals:      //声明信号
    void testsignal(int);

public slots: //声明槽
    void testslot(int);

emit testsignal(100);  //触发信号

2、信号的参数个数大于等于槽函数的参数个数

signals:      //声明信号
    void testsignal(int,int);

public slots: //声明槽
    void testslot(int);

emit testsignal(100,100);  //触发信号

3、信号和槽函数之间的关系是多对多,信号也可以 连接到另一个信号上

signals:    //声明信号
    void testsignal(int);
public slots: //声明槽
    void testslot(int);
public slots: //声明槽
    void testslot2(int);

 QObject::connect(this,&QtWidgetsApplication3::testsignal, qobj,&obj::testslot); //连接信号和槽
 QObject::connect(this,&QtWidgetsApplication3::testsignal, qobj,&obj::testslot2); //连接信号和槽

emit testsignal(100);  //触发信号

4、信号和槽的连接机制原理:通过QObject::connect来实现的

QObject::connect(sender, &Sender::signal, receiver, &Receiver::slot);
//在上述代码中,sender对象的signal信号与receiver对象的slot槽函数被连接起来。

5、信号和槽的连接类型:

  • 直接连接(Direct Connection):这种连接方式类似于自动连接,但是即使在不同线程中也会立即调用槽函数。需要注意的是,这种方式可能导致竞态条件,因为它绕过了事件队列,所以使用时需要特别小心。
  • 队列连接(Queued Connection):在这种模式下,信号的发射不会立即导致槽函数的执行。相反,信号会被放入事件队列中,在下一个事件循环开始时才执行槽函数。这种方式常用于跨线程通信,因为可以避免直接从非GUI线程中调用可能会修改GUI组件的方法。
  • 自动连接(Auto Connection):这是默认的连接方式。当信号被发射时,Qt会立即调用相关的槽函数。这种方式适用于大多数情况,因为它是最快的方式,并且不需要额外的线程同步机制。
//直接连接
connect(button, &QPushButton::clicked, this, &MyWidget::handleClick, Qt::DirectConnection);
//队列连接
connect(button, &QPushButton::clicked, this, &MyWidget::handleClick, Qt::QueuedConnection);
//自动连接
connect(button, &QPushButton::clicked, this, &MyWidget::handleClick);

6、信号发射原理

emit mySignal(data);
//在上述代码中,当调用emit mySignal(data);时,所有连接到mySignaal的槽函数都会被调用。

7、槽函数调用过程

当一个信号被发射时,Qt负责按连接顺序调用与该信号连接的所有槽函数。

8、信号发射与线程

  • 信号可以安全地跨线程发射,如果信号接收者位于不同线程,Qt会自动将信号的处理放入目标线程的事件循环中,确保线程安全。
  • 信号的发射和槽的执行会自动适应线程间的通信机制,通过Qt的事件循环和消息队列机制来实现。
  • 线程安全:Qt的跨线程信号和槽机制是线程安全的,开发者无需担心常见的多线程问题,如竞态条件和死锁。
  • 自动同步:Qt处理所有线程间的通信细节,确保数据在线程间传递时的完整性和一致性。

7、信号和槽异步调用

当信号和槽位于不同线程时,Qt使用事件循环来实现异步调用。信号的发射将产生一个事件,该事件被放入目标线程的事件队列中。当事件循环处理到这个事件时,与之关联的槽函数被调用。

8、事件队列的角色

事件队列在信号和槽的跨线程通信中起着至关重要的作用。每个线程都有自己的事件队列和事件循环。当一个线程向另一个线程发出信号时,这个信号被封装成一个事件,然后被加入接收线程的事件队列。这确保了即使在高度并发的环境下,槽函数的执行也是线程安全的。

三、类型信息

Q_OBJECT 宏使得类可以通过 QMetaObject获取详细的运行时类型信息。这些信息包括类名、父类、信号、槽、属性等。

QtWidgetsApplication4.h

#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_QtWidgetsApplication4.h"
#include <QMetaObject>   //获取元对象
#include <QMetaMethod>   //信号与槽
#include <QMetaProperty> //属性
#include <QDebug>
#include <QObject>

class QtWidgetsApplication4 : public QMainWindow
{
    Q_OBJECT
public:
    QtWidgetsApplication4(QWidget *parent = nullptr);
    ~QtWidgetsApplication4();
private:
    Ui::QtWidgetsApplication4Class ui;
signals:
    void mysignal(int); //设置类的信号
private slots: 
    void myslot(int);   //设置类的槽
private:
    int myProperty=false;
};

QtWidgetsApplication4.cpp

#include "QtWidgetsApplication4.h"

QtWidgetsApplication4::QtWidgetsApplication4(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);

    //获取元对象
    const QMetaObject *metaObject = this->metaObject();

    //打印类名
    qDebug() << metaObject->className();

    //打印所有信号
    qDebug() << "Signals:";
    for (int i = 0; i < metaObject->methodCount(); i++)
    {
        QMetaMethod  method = metaObject->method(i);
        qDebug() << method.name();
    }

    //打印所有槽
    qDebug() << "Slots:";
    for (int i = 0; i < metaObject->methodCount(); i++)
    {
        QMetaMethod  method = metaObject->method(i);
        if (method.methodType() == QMetaMethod::Slot)
            qDebug() << method.name();
    }

    //打印所有属性
    qDebug() << "Properties:";
    for (int i = 0; i < metaObject->propertyCount(); i++)
    {
        QMetaProperty property = metaObject->property(i);
        qDebug() << property.name();
    }
}

QtWidgetsApplication4::~QtWidgetsApplication4()
{}

void QtWidgetsApplication4::myslot(int)
{}

运行效果:

 四、动态设置属性

在QT中,动态属性设置的添加与修改,可以应用于数据绑定、状态管理、主题样式、事件处理等;例如:可以将 UI 控件的属性与模型数据绑定,这样当模型数据发生变化时,UI 控件的属性会自动更新。

QtWidgetsApplication5.cpp

#include "QtWidgetsApplication5.h"

QtWidgetsApplication5::QtWidgetsApplication5(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
    //获取当前对象的元对象
    const QMetaObject *metaObject = this->metaObject();

    //打印类名
    qDebug() << metaObject->className();

    //动态设置属性
    this->setProperty("dynamicProperty",QVariant(42));

    //动态获取属性
    QVariant value = this->property("dynamicProperty");
    qDebug() << "dynamicProperty:" << value.toInt();

    //检查属性是否存在
    QVariant checkValue =this->property("dynamicProperty");
    if (checkValue.isValid()) { qDebug() << "Property exists and its value is:" << checkValue.toInt(); }
    else { qDebug() << "Property does not exist."; }

    //修改属性
    this->setProperty("dynamicProperty", QVariant(100));

    //获取修改后的属性值
    value = this->property("dynamicProperty");
    qDebug() << "Modified dynamic property value:" << value.toInt();

    //删除属性
    this->setProperty("dynamicProperty",QVariant());
    checkValue = this->property("dynamicProperty");
    if (checkValue.isValid()) {qDebug() << "Property exists and its value is:" << checkValue.toInt();}
    else {qDebug() << "Property has been 'deleted'.";}
}

QtWidgetsApplication5::~QtWidgetsApplication5()
{}

QtWidgetsApplication5.h

#pragma once

#include <QtWidgets/QMainWindow>
#include "ui_QtWidgetsApplication5.h"
#include <QMetaObject>   //获取元对象
#include <QMetaMethod>   //信号与槽
#include <QMetaProperty> //属性
#include <QDebug>

class QtWidgetsApplication5 : public QMainWindow
{
    Q_OBJECT
public:
    QtWidgetsApplication5(QWidget *parent = nullptr);
    ~QtWidgetsApplication5();
private:
    Ui::QtWidgetsApplication5Class ui;
};

运行效果:


原文地址:https://blog.csdn.net/ZJQSDSA/article/details/142501491

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!