Динамически загружать QQuickWindow вместо QQuickWidget в модульном тесте Qt C++
В нашем проекте у нас есть модульные тесты 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
. Однако пытаясь загрузитьQQuickItem
через QQuickView
вот так https://doc.qt.io/qt-5/qquickview.html:
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
как здесь /questions/46949239/v-chem-raznitsa-mezhdu-qquickview-i-qquickwindow/46949260#46949260:
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:
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
) и получить рут QQuickItem
в C++ для тестирования? Есть идеи? Спасибо!
1 ответ
Может ваш вопрос о QQuickWindow
/QQuickView
? КакQQuickWidget
связано с проблемой? Это просто обертка дляQQuickWindow
. В любом случае, я бы вместо этого использовал некоторую оболочку динамической загрузки QML. Идея состоит в том, чтобы загружать каждый компонент последовательно. Пример ниже просто иллюстрирует идею. Если вам не нужно функциональное тестирование, просто удалите таймеры и подключения и поиграйте сLoader.Ready
вместо.
main.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();
}
}
Item1.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);
}
Item2.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);
}
}
Item3.qml
import QtQuick 2.5
Item {
Itemssss {
}
}