我们在书写qml程序的时候经常需要使用到qml的cpp的对象注册,以此来在qml中使用cpp的对象,下面来详细了解一下qml程序的运行逻辑,尤其是qml对象的注册

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    qRegisterMetaType<MapData *>("MapData*");

    qmlRegisterType<gameTools::MapRenderer>("RpgGame", 1, 0, "MapRenderer");
    qmlRegisterType<gameTools::MapLoading>("RpgGame", 1, 0, "MapLoading");
    qmlRegisterType<gameTools::Player>("RpgGame", 1, 0, "Player");
    qmlRegisterType<gameTools::GameRenderer>("RpgGame", 1, 0, "GameRenderer");
    qmlRegisterType<gameTools::Camera>("RpgGame", 1, 0, "Camera");
    qmlRegisterType<gameTools::NPC>("RpgGame", 1, 0, "Npc");
    qmlRegisterType<CustomTableModel>("RpgGame", 1, 0, "CustomTableModel");
    qmlRegisterType<UserConsole>("RpgGame", 1, 0, "UserConsole");

    QQmlApplicationEngine engine;
    qmlRegisterSingletonType<TeamManager>("RpgGame", 1, 0, "TeamManager",
                                          [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject *
                                          {
                                              Q_UNUSED(engine)
                                              Q_UNUSED(scriptEngine)
                                              return TeamManager::instance();
                                          });
    QObject::connect(
        &engine,
        &QQmlApplicationEngine::objectCreationFailed,
        &app,
        []()
        { QCoreApplication::exit(-1); },
        Qt::QueuedConnection);
    engine.loadFromModule("RpgGame", "Main");

    return app.exec();
}

下面我们进行详细的拆解

QGuiApplication app(argc, argv);

创建了qt gui程序的核心管理对象,用来初始化qml系统,包括资源和界面,同时接收运行程序的时候传递进来的参数。

需要注意的是这个代码要放到程序main函数的第一位,并且只能有一个这个对象,必须在主线程创建

qmlRegisterType<gameTools::MapRenderer>("RpgGame", 1, 0, "MapRenderer");

如果没有注册时的问题

  • 无法在跨线程信号槽中使用

  • 无法在QML中访问

  • 无法在QVariant中存储

  • Qt的元对象系统无法识别该类型

 engine.loadFromModule("RpgGame", "Main");

    return app.exec();

加载rpggame模块下的main.qml

QGuiApplication进行事件循环处理,接收事件处理事件

注册的另外两种方式

一、基本区别对比

特性 setContextProperty 静态类/Singleton
创建时机 在 main() 中显式创建 第一次访问时创建(延迟初始化)
生命周期 由 C++ 控制(main 函数内) 由 QML 引擎管理
QML 访问方式 直接使用变量名 通过导入的类型名访问
内存管理 开发者负责 QML 引擎管理
可扩展性 需要手动添加新属性 可以在单例中扩展方法
线程安全 取决于实现 提供函数中可实现线程安全

一般来说我们直接使用类的单例即可

6. qvariant注意事项

  1. 类型注册:自定义类型必须使用 Q_DECLARE_METATYPE 和 qRegisterMetaType

  2. 类型匹配:使用 value<T>() 时,类型必须完全匹配或可转换

  3. 性能考虑:频繁的类型检查可能影响性能

  4. 线程安全:自定义类型需要线程安全地使用

7转换规则

# QML var 转换为 QVariant 的详细规则

当 QML 中的 var 类型数据赋值给 C++ 的 QVariant 属性时,Qt 引擎会自动进行类型转换。以下是详细的转换规则:

1. **基本类型转换**
property var data1: 42        // int → QVariant(int)
property var data2: 3.14      // double → QVariant(double)
property var data3: "hello"   // string → QVariant(QString)
property var data4: true      // bool → QVariant(bool)
property var data5: null      // null → QVariant() (无效的 QVariant)
property var data6: undefined // undefined → QVariant() (无效的 QVariant)
```

 2. **对象转换规则**
// JavaScript 对象 → QVariantMap
property var obj: {
    "x": 10,                    // int
    "y": 20.5,                  // double
    "name": "test",             // QString
    "visible": true,            // bool
    "list": [1, 2, 3],          // QVariantList
    "nested": { "a": 1 }        // QVariantMap
}
// 转换为:QVariantMap<QString, QVariant>

 3. **数组转换规则**


// JavaScript 数组 → QVariantList
property var arr1: [1, 2, 3]                // [int, int, int] → QVariantList
property var arr2: ["a", "b", "c"]          // [QString, QString, QString] → QVariantList
property var arr3: [1, "text", true, {x:1}] // 混合类型 → QVariantList
property var arr4: []                       // 空数组 → 空 QVariantList
```

## 4. **Qt 对象类型转换**
import QtQuick 2.15

property var rect: Qt.rect(10, 20, 100, 50)     // QRectF → QVariant(QRectF)
property var point: Qt.point(5, 10)             // QPointF → QVariant(QPointF)
property var size: Qt.size(200, 300)            // QSizeF → QVariant(QSizeF)
property var vector: Qt.vector3d(1, 2, 3)       // QVector3D → QVariant(QVector3D)
property var color: Qt.rgba(0.5, 0.5, 0.5, 1)   // QColor → QVariant(QColor)
property var date: new Date(2024, 0, 1)         // JavaScript Date → QVariant(QDateTime)
```

 5. **函数类型转换**
// 函数会被转换为 QVariant(QJSValue)
property var func: function() { 
    return "hello"; 
}
// 注意:这种转换可能不完全,通常需要特殊处理
 

 

8. **注意事项**

1. **性能考虑**:
   - 大量数据转换可能影响性能
   - 复杂嵌套结构的转换比简单类型慢

2. **内存管理**:
   - QVariant 会创建数据的副本
   - 大对象转换时注意内存使用

3. **类型安全**:
   - 始终检查 QVariant 是否包含预期类型
   - 使用 `canConvert<T>()` 进行类型检查

4. **线程安全**:
   - QVariant 可以在线程间传递
   - 但内部的复杂类型可能不是线程安全的

不需要总是返回 QVariant。QML 可以直接支持:

  • 基本类型(int, double, bool, QString)

  • Qt 核心类型(QColor, QRect, QPoint, QUrl 等)

  • 容器类型(QList, QVariantList, QVariantMap)

  • QObject 派生类指针

需要使用 QVariant 的情况

  1. 返回不确定类型的动态数据

  2. 返回非 QObject 的自定义类型

  3. 泛型编程或模板函数

  4. 需要向后兼容旧代码

最佳实践:尽量使用具体的类型,这样代码更清晰、类型更安全、性能更好。只有在必要时才使用 QVariant。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐