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

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

本文关键字:QQuickWindow QQuickWidget 加载 动态 Qt C++ 单元测试      更新时间:2023-10-16

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

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 组件:

my.qml:

Rectangle {
...
}

但是当我将组件包装到Dialog

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

它停止工作:

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

这是意料之中的,因为Dialog是一个QQuickWindow.但是,尝试通过这样的QQuickView加载QQuickItemhttps://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(并将其根QQuickItemC++进行测试?有什么想法吗?谢谢!

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

主.qml

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 {
}
}