繁体   English   中英

在Qt C++单元测试中动态加载QQuickWindow而不是QQuickWidget

[英]Dynamically load QQuickWindow instead of QQuickWidget in Qt C++ unit test

在我们的项目中,我们对 QML 源进行了 C++ 单元测试。 它使用以下代码动态加载组件以进行进一步处理

class MyTest {
    ...
    QScopedPointer<QQuickWidget> quickWidget;
    QQuickItem* root = nullptr;

    void setQmlSource(const QString& source)
    {
        quickWidget.reset(new QQuickWidget);
        quickWidget->rootContext()->engine()->addImportPath("qrc:/qml");
        quickWidget->setSource(QUrl::fromUserInput(source));
        root = quickWidget->rootObject();
    }
}

对于像这样的 qml 组件工作正常:

我的.qml:

Rectangle {
   ...
}

但是,当我将组件包装到Dialog

Dialog {
    ...
    Rectangle {
       ...
    }
}

它停止工作:

错误: QQuickWidget仅支持加载从QQuickItem派生的根对象。

这是预期的DialogQQuickWindow 然而试图加载QQuickItem通过QQuickView这样https://doc.qt.io/qt-5/qquickview.html#details

void MyTest::setQmlWindow(const QString& source)
{
    QQuickView *view = new QQuickView;
    view->rootContext()->engine()->addImportPath("qrc:/qml");
    view->setSource(QUrl::fromUserInput(source));
    root = view->rootObject();
}

也因上述错误而失败。 并通过QQmlApplicationEngine加载,如这里https://stackoverflow.com/a/23936741/630169

void MyTest::setQmlWindow(const QString& source)
{
    engine = new QQmlApplicationEngine;
    //engine->addImportPath("qrc:/qml");
    engine->load(QUrl::fromUserInput(source));
    QObject *myObject = engine->rootObjects().first();;
    QQuickWindow *window = qobject_cast<QQuickWindow*>(myObject);
    root = window->contentItem();
}

失败并出现另一个错误:

QQmlApplicationEngine加载组件失败
QWARN:未安装MyTest::myMethodTest()模块“ mynamespace.mymodule
QWARN:未安装MyTest::myMethodTest()模块“ mynamespace.mymodule
...

为什么view->setSource()Rectangle项正确加载此模块,而QQmlApplicationEngine无法为相同的项 qml 源执行但包装到Dialog

注意:这些模块是 C++ 并且使用view->setSource()很好地加载。

如果我尝试像文档中提到的那样通过QQmlComponent使用和加载: https://doc.qt.io/qt-5/qqmlcomponent.html#details

void MyTest::setQmlWindow(const QString& source)
{
    engine = new QQmlApplicationEngine;
    //engine->addImportPath("qrc:/qml");
    QQmlComponent *component = new QQmlComponent(engine, QUrl::fromUserInput(source));
    component->loadUrl(QUrl::fromUserInput(source));
    QQuickWindow *window = qobject_cast<QQuickWindow*>(component->create());
    root = window->contentItem();
}
  • 然后有另一个错误:

    QQmlComponent : 组件未准备好

如果engine->addImportPath()没有被调用,并且崩溃

位置:[未知文件(0)]

调用engine->addImportPath()时出错。

如何正确加载Dialog ( QQuickWindow ) 并在 C++ 中获取根QQuickItem以进行测试? 有任何想法吗? 谢谢!

也许您的问题是关于QQuickWindow / QQuickView QQuickWidget与该问题有何关联? 那只是QQuickWindow一个包装器。 无论如何,我会改用一些 QML 动态加载包装器。 这个想法是按顺序加载每个组件。 下面的例子只是说明了这个想法。 如果您不需要功能测试,只需删除计时器和连接并使用Loader.Ready代替。

主文件

import QtQuick 2.5
import QtQuick.Window 2.2

Window {
    id: window
    visible: true
    width: 640
    height: 480
    title: qsTr("Test window")
    property var objects: ["Item1.qml", "Item2.qml", "Item3.qml"]
    property int currentObject: 0
    property var testResults: [0, 0]
    property bool testRunning: false

    onTestRunningChanged: {
        if(window.testRunning)
            console.log("start testing");
        else
            console.log("all the objects has tested. passed: " + window.testResults[0] + ", failed: " + window.testResults[1]);
    }

    function loadObject() {
        try
        {
            loader.source = window.objects[window.currentObject];
        }
        catch(ex) {}
    }

    function checkNext(prevStatus) {
        if(!window.testRunning)
            return;

        window.testResults[prevStatus ? 0 : 1]++;
        timer.running  = false;
        console.log(window.objects[window.currentObject] + ":" + ((prevStatus) ? "passed" : "failed"))
        if(window.currentObject === window.objects.length - 1) {
            window.testRunning = false;
        } else {
            window.currentObject ++;
            loadObject();
            timer.start();
        }
    }

    Loader {
        id: loader
        anchors.centerIn: parent
        onStatusChanged: {
            if (loader.status == Loader.Error) {
                loader.source = "";
                checkNext(false);
            }
        }
    }

    Connections {
        target: loader.item
        onTestPassed: checkNext(true);
    }

    Timer {
        id: timer
        interval: 5000
        repeat: false
        onTriggered: checkNext(false);
    }

    Component.onCompleted: {
        window.testRunning = true;
        loadObject();
    }
}

项目1.qml

import QtQuick 2.0
import QtQuick.Controls 2.5

Dialog {
    id: item
    signal testPassed(bool value);
    title: "Dialog test"
    modal: true
    standardButtons: Dialog.Ok
    visible: true
    onAccepted: item.testPassed(true);
}

项目2.qml

import QtQuick 2.5
import QtQuick.Controls 2.5

Rectangle {
    id: item
    signal testPassed(bool value);

    width: 200
    height: 200
    color: "orange"

    Button {
        anchors.centerIn: parent
        text: "Test"
        onClicked: item.testPassed(true);
    }
}

项目3.qml

import QtQuick 2.5

Item {
    Itemssss {

    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM