当 main.qml 通过 HTTP 导入时,main.cpp 中的“engine.rootObjects().first()”崩溃

Mar*_*ark 3 c++ qt qml

我们有一行代码,我一直(或更长时间)使用它来获取指向根窗口的指针。我们最近开始使用 HTTP 语法从服务器导入组件。当我们把它们放在一起时,程序崩溃,engine.rootObjects().first()返回空。这是一个简化的“main.cpp”:

#include <QGuiApplication>
#include <QQmlApplicationEngine>

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;

    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    QObject* rootWindowPointer = (engine.rootObjects().first());        // <-- this is the function that fails

    return app.exec();
}
Run Code Online (Sandbox Code Playgroud)

这是一个简化的“main.qml”,其中包含导致问题的导入:

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15

import "http://eggs:88/empty"       // <-- this is the added line that caused the problem

ApplicationWindow {
    id: window
    visible: true
    width: 200
    height: 100
    title: qsTr("Liblo Server Test")

    Text {
        text: "Made it this far . . ."
        anchors.centerIn: parent
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我们注释掉 main.cpp 中获取根窗口指针的以下行,程序就会运行:

QObject* rootWindowPointer = (engine.rootObjects().first();

如果我们注释掉 main.qml 中“导入”我们的组件的以下行,程序也会运行:

导入“http://eggs:88/empty”

但是,如果您启用了这两行,那么当 main.cpp 中的该行尝试获取指针时,您会遇到以下崩溃:

20:37:03: Starting /xxxx/mark/QtApps/build-libloServer-Desktop_Qt_5_15_0_GCC_64bit-Debug/rootwindow ...
QML debugging is enabled. Only use this in a safe environment.
ASSERT: "!isEmpty()" in file ../../Qt/5.15.0/gcc_64/include/QtCore/qlist.h, line 361
20:37:04: The program has unexpectedly finished.
20:37:04: The process was ended forcefully.
20:37:04: /xxxx/xxxx/QtApps/build-libloServer-Desktop_Qt_5_15_0_GCC_64bit-Debug/rootwindow crashed.
Run Code Online (Sandbox Code Playgroud)

由于不知道为什么这两件事会相关,我们认为这可能是时间问题(不太可能,但有可能)。所以我们在指针获取前面放了一条等待线:

执行 {} while (engine.rootObjects().isEmpty());

但它永远不会变得非空。我们还尝试了真正的 HTTP 库、一个空库和一个伪造的 URL,每次都得到完全相同的结果(因此您可以在没有实际 URL 的情况下尝试它)。

有谁知道为什么会发生这种情况?

如果这是一个不起眼的错误,有人能想到解决方法吗?

是否有另一种方法来获取根窗口的指针以供 C++ 使用?

eyl*_*esc 5

没有什么隐晦的,但这是预期的行为。当对象完成构建时,rootObjects 被分配给 C++,并且由于您有一个异步加载 url(http),因此构建是异步的。因此,如果你想获得 root 权限,那么你必须使用 objectCreated 信号:

QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                 &app, [url, &engine](QObject *obj, const QUrl &objUrl) {
    if (!obj && url == objUrl)
        QCoreApplication::exit(-1);
    else{
        if(engine.rootObjects().isEmpty())
            return;
        QObject* rootWindowPointer = engine.rootObjects().first();
        qDebug() << rootWindowPointer;
    }
}, Qt::QueuedConnection);
Run Code Online (Sandbox Code Playgroud)