【Qt之QMetaObject】使用

描述

QMetaObject类包含有关Qt对象的元信息。在Qt中,Qt元对象系统负责信号和槽的对象间通信机制、运行时类型信息以及Qt属性系统。每个应用程序中使用的QObject子类都创建一个QMetaObject实例,该实例存储QObject子类的所有元信息。该对象可通过QObject::metaObject()方法获得。

通常在应用程序编程中不需要使用这个类,但在编写元应用程序(如脚本引擎或GUI构建器)时非常有用。您可能最有用的函数是这些:

  • className()返回类的名称。
  • superClass()返回超类的元对象。
  • method()和methodCount()提供有关类的元方法(信号、槽和其他可调用的成员函数)的信息。
  • enumerator()和enumeratorCount()提供类的枚举器信息。
  • propertyCount()和property()提供类的属性信息。
  • constructor()和constructorCount()提供类的元构造函数信息。

indexOfConstructor()、indexOfMethod()、indexOfEnumerator()和indexOfProperty()等索引函数将构造函数、成员函数、枚举器或属性名称映射到元对象中的索引。例如,在将信号连接到槽时,Qt在内部使用indexOfMethod()。

类还可以具有其他类信息的名称-值对列表,存储在QMetaClassInfo对象中。 classInfoCount()返回对数,classInfo()返回单个对,indexOfClassInfo()可用于搜索对。

###invokeMethod() 用法

invokeMethod()函数是QObject类的一个成员函数,它可以动态调用一个QObject对象的特定方法(成员函数)。这种方法调用开销较大,应该尽可能避免使用。可以直接调用对象方法来执行所需操作。

该函数的原型为:

cpp 复制代码
bool QObject::invokeMethod(const QObject *context, const char *method,
                           Qt::ConnectionType type = Qt::AutoConnection,
                           QGenericReturnArgument ret = QGenericReturnArgument(),
                           QGenericArgument val0 = QGenericArgument(),
                           QGenericArgument val1 = QGenericArgument(),
                           QGenericArgument val2 = QGenericArgument(),
                           QGenericArgument val3 = QGenericArgument(),
                           QGenericArgument val4 = QGenericArgument(),
                           QGenericArgument val5 = QGenericArgument(),
                           QGenericArgument val6 = QGenericArgument(),
                           QGenericArgument val7 = QGenericArgument(),
                           QGenericArgument val8 = QGenericArgument(),
                           QGenericArgument val9 = QGenericArgument());

其中,context参数是一个QObject指针,指定要调用方法的对象;method参数是一个成员函数名,以字符串形式指定;type参数是Qt::ConnectionType类型,指定调用方法时的连接类型;ret参数和val0~val9参数分别是返回值和函数参数,以QGenericReturnArgument和QGenericArgument类型封装。

cpp 复制代码
#include <QCoreApplication>
#include <QObject>
#include <QDebug>
#include <QThread>

class MyObject : public QObject {
    Q_OBJECT

public:
    MyObject(QObject* parent = nullptr) : QObject(parent) {}

public slots:
    void myMethod() {
        qDebug() << "Running in thread" << QThread::currentThread();
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    MyObject obj;
    QThread thread;

    // 将QObject移到另一个线程中
    obj.moveToThread(&thread);

    // 连接线程开始信号到槽
    QObject::connect(&thread, &QThread::started, &obj, [&](){
        // 在另一个线程中调用QObject的myMethod()方法
        QMetaObject::invokeMethod(&obj, "myMethod", Qt::QueuedConnection);
    });

    thread.start();

    return a.exec();
}

上述示例步骤:

  • 创建了一个MyObject对象obj,它有一个名为"myMethod"的方法
  • 将该对象移到另一个线程中,并通过connect()函数连接了线程开始信号到obj对象的lambda槽
  • 使用invokeMethod()函数异步调用obj对象的myMethod()方法,以保证该方法在另一个线程中执行
  • 启动线程并运行应用程序。

运行结果如下所示:

cpp 复制代码
Running in thread QThread(0x7fffc8000820)

从输出结果可以看出,myMethod()方法确实在另一个线程中执行了。这种方式可以用于实现QObject对象的跨线程方法调用。

使用QMetaObject示例

cpp 复制代码
#include <QCoreApplication>
#include <QObject>
#include <QDebug>

class MyObject : public QObject {
    Q_OBJECT

public:
    MyObject(QObject* parent = nullptr) : QObject(parent) {}

public slots:
    void myMethod() {
        qDebug() << "Hello from MyObject!";
    }

    QString myStringMethod(int num) {
        return QString("My number is %1").arg(num);
    }
};

int main(int argc, char* argv[]) {
    QCoreApplication a(argc, argv);

    MyObject obj;
    const QMetaObject* metaObj = obj.metaObject();

    // 获取类名
    qDebug() << "Class name:" << metaObj->className();

    // 获取父类的元对象
    const QMetaObject* superClass = metaObj->superClass();
    qDebug() << "Superclass name:" << superClass->className();

    // 获取方法数量
    int methodCount = metaObj->methodCount();
    qDebug() << "Method count:" << methodCount;

    // 遍历方法
    for (int i = 0; i < methodCount; ++i) {
        QMetaMethod method = metaObj->method(i);
        qDebug() << "Method name:" << method.name();

        // 判断是否是槽函数
        if (method.methodType() == QMetaMethod::Slot) {
            qDebug() << "  Is a slot function!";
        }

        // 判断是否有返回值
        if (method.returnType() != QMetaType::Void) {
            qDebug() << "  Return type:" << method.typeName();
        }

        // 输出参数类型
        int argCount = method.parameterCount();
        for (int j = 0; j < argCount; ++j) {
            qDebug() << "  Arg" << j << "type:" << method.parameterType(j);
        }
    }

    // 调用对象的方法
    QString result = "";
    QMetaObject::invokeMethod(&obj, "myStringMethod", Q_RETURN_ARG(QString, result), Q_ARG(int, 42));
    qDebug() << result;

    return a.exec();
}

输出结果如下所示:

cpp 复制代码
Class name:MyObject
Superclass name:QObject
Method count:3
Method name:destroyed
Method name:deleteLater
Method name:myMethod
  Is a slot function!
Method name:myStringMethod
  Return type:QString
  Arg0 type:int
My number is 42
相关推荐
神秘的猪头10 小时前
🚀 React 开发者进阶:RAG 核心——手把手带你玩转 Milvus 向量数据库
数据库·后端·llm
IvorySQL1 天前
PostgreSQL 技术日报 (3月6日)|为什么 Ctrl-C 在 psql 里让人不安?
数据库·postgresql·开源
Felix_One1 天前
Qt 串口通信避坑指南:QSerialPort 的 5 个常见问题
qt
NineData1 天前
数据库管理工具NineData,一年进化成为数万+开发者的首选数据库工具?
运维·数据结构·数据库
IvorySQL1 天前
PostgreSQL 技术日报 (3月5日)|规划器控制力升级,内核能力再进阶
数据库·postgresql·开源
数据组小组2 天前
免费数据库管理工具深度横评:NineData 社区版、Bytebase 社区版、Archery,2026 年开发者该选哪个?
数据库·测试·数据库管理工具·数据复制·迁移工具·ninedata社区版·naivicat平替
悟空聊架构2 天前
基于KaiwuDB在游乐场“刷卡+投币”双模消费系统中的落地实践
数据库·后端·架构
IvorySQL2 天前
PostgreSQL 技术日报 (3月4日)|硬核干货 + 内核暗流一网打尽
数据库·postgresql·开源
进击的丸子2 天前
虹软人脸服务器版SDK(Linux/ARM Pro)多线程调用及性能优化
linux·数据库·后端