qml程序运行逻辑

我们在书写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_METATYPEqRegisterMetaType

  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)

```

  1. **对象转换规则**

// 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>

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

// 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)

```

  1. **函数类型转换**

// 函数会被转换为 QVariant(QJSValue)

property var func: function() {

return "hello";

}

// 注意:这种转换可能不完全,通常需要特殊处理

  1. **注意事项**

  2. **性能考虑**:

  • 大量数据转换可能影响性能

  • 复杂嵌套结构的转换比简单类型慢

  1. **内存管理**:
  • QVariant 会创建数据的副本

  • 大对象转换时注意内存使用

  1. **类型安全**:
  • 始终检查 QVariant 是否包含预期类型

  • 使用 `canConvert<T>()` 进行类型检查

  1. **线程安全**:
  • QVariant 可以在线程间传递

  • 但内部的复杂类型可能不是线程安全的

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

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

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

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

  • QObject 派生类指针

需要使用 QVariant 的情况

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

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

  3. 泛型编程或模板函数

  4. 需要向后兼容旧代码

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

相关推荐
jiayong232 小时前
JVM垃圾回收机制面试题
java·开发语言·jvm
袁煦丞 cpolar内网穿透实验室2 小时前
mysql_exporter+cpolar远程监控 MySQL 不卡壳!cpolar 内网穿透实验室第 712 个成功挑战
服务器·数据库·mysql·远程工作·内网穿透·cpolar
jushisi2 小时前
下载eclipse MAT(Memory Analyzer Tool)
java·服务器
Mikhail_G2 小时前
Mysql数据库操作指南(零基础篇二)
大数据·数据库·sql·mysql·数据分析
一条大祥脚2 小时前
26.1.24 分块|排序|中位数贪心+线段树二分+聚集贪心
数据库·redis·缓存
源代码•宸2 小时前
GoLang八股(Go并发)
服务器·面试·golang·cap·gmp·三色标记法·混合写屏障
尽兴-2 小时前
JVM垃圾收集器与三色标记算法详解
java·jvm·算法·cms·gc·g1·三色标记算法
Anastasiozzzz2 小时前
Redis脑裂问题--面试坑点【Redis的大脑裂开?】
java·数据库·redis·缓存·面试·职场和发展
木土雨成小小测试员2 小时前
Python测试开发之后端一
开发语言·数据库·人工智能·python·django·sqlite