c++ - 模拟在 QML 中实例化的 C++ 类

标签 c++ qt mocking qml

我正在尝试编写一个插件,其中包含一些 QML 文件和一些提供较低级别功能并与另一个应用程序通信的 C++ 类。它们由 QML 组件使用。 我希望能够从 QML 管理这些 C++ 对象的生命周期(即它们应该在加载 QML 文件时创建并在 QML 销毁时销毁),同时仍然能够模拟 C++ 对象。

到目前为止,我尝试了几种不同的方法。理想情况下,结果是我可以在要编辑的 QML 文件上使用 qmlscene,并在该文件旁边有一个 dummydata 文件夹,其中包含实例化 C++ 的模拟类(class)。 如果我尝试在继承自 QQmlExtensionPlugin 的插件类中使用 qmlRegisterType(类似于 https://qmlbook.github.io/ch17-extensions/extensions.html 中的示例),并将生成的库传递给 qmlscene,QML 文件不会使用 mock,而是实例化一个 C++ 对象。这意味着有时,我需要启动一些逻辑来将一些模拟数据放入我的 QML 文件中。

“QML Book”中的示例似乎建议在将任何 C++ 引入 QML 之前,先使用 mock 完全设计 QML 组件。有没有办法做到这一点更可持续?我想,我可以通过注释掉相应的行来避免将 qmlRegisterType 用于我想模拟一段时间的 C++ 类,但我不想这样做。

我尝试的另一种方法是使用中央 C++ Controller 类中的 QQMLContext::setContextProperty。这使我能够将 C++ 对象从 C++ 传递到 QML 并使用虚拟数据,但是对象的生命周期不会由 QML 组件管理,而是由 C++ 管理。此外,每个类都应该被多次实例化,正确连接信号很容易出错。这是我到目前为止发现的:

auto proxy = std::make_shared<Proxy>();
//make `proxy` object known in QML realm
_qmlEngine.rootContext()->setContextProperty("proxy", proxy.get());

connect(&_qmlEngine, &QQmlApplicationEngine::objectCreated,
        [&proxy](QObject *object, const QUrl &url) {
            if (url == QUrl("qrc:/imports/Common/TestWindow.qml")) {

                // make sure the proxy is not destroyed when leaving scope of this function
                connect(qobject_cast<QQuickWindow *>(object),
                        &QWindow::visibilityChanged, // as a dirty workaround for missing QWindow::closing signal
                        [proxy]() mutable { proxy.reset(); }); // delete proxy when closing TestWindow
            }
        });

_qmlEngine.load(QUrl("qrc:/imports/Common/TestWindow.qml"));

是否有一种“舒适”的方式来模拟在 QML 中实例化且最初来自 C++ 的数据,或者至少有一种好方法可以将此类 C++ 对象的生命周期附加到 QML 对象的生命周期?

最佳答案

我解决这个问题的方法如下:

实际生产应用程序将使用 C++ 插件,仅包含 C++ 文件,不包含 QML。

对于模拟,有一个与 C++ 插件同名的 QML 模块,其中包含提供与等效 C++ 类相同接口(interface)的 QML 文件。除了一般的 QML 包含之外,这个模块被传递给 qmlscene

如果 C++ 类头如下所示:

class Proxy : public QObject
{
  Q_OBJECT

  public:
    Q_PROPERTY(int foo)
    Q_INVOKABLE void start();

  signals:
    void started();
}

并且此类可用于 QML,如下所示:

qmlRegisterType<Proxy>("Logic", 1, 0, "Proxy");

QML 模拟(在文件 Proxy.qml 中)看起来像这样:

import QtQml 2.12

QtObject {
  signal started()
  property var foo: 42
  function start() { console.log("start") }
}

并且可以使用如下所示的 qmldir 文件在 QML 中导入:

module Logic

Proxy 1.0 Proxy.qml

qmlscene 的最终调用将是

qmlscene [path/to/prototype/qml] -I [path/to/folder/containing/proxy/mock/]

关于c++ - 模拟在 QML 中实例化的 C++ 类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58660958/

相关文章:

c++ - Hippo Mocks 中具有不同返回值的多个预期调用是否可以重复使用模拟?

unit-testing - 使用 Spring Boot 进行身份验证过滤器的集成测试

c++ - 使用共享库的后果是打开另一个具有不同构建类型的共享库

QTextEdit.insertHtml() 很慢

c++ - 没有匹配函数调用 ‘std::less<int>::less(const int&, const int&)’

c++ - Qt如何申请管理员权限?

c++ - QT如何检查按键是否被按下?

c# - NHibernate 单元测试模拟/内存数据库

C++ - 从整数数组中插入和提取字符

c++ - 重载 >> 使用 istream