3

We have a line of code that I've been using forever (or longer) to fetch a pointer to the root window. We've recently started importing our components from a server using the HTTP syntax for import. When we put then together, the program crashes, with engine.rootObjects().first() returning empty. Here is a simplified "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();
}

And here is a simplified "main.qml" with the import that caused the problem:

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

If we comment out the following line in main.cpp that gets the root window pointer, the program runs:

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

If we comment out the following line in main.qml that 'imports' our components, the program also runs:

Import "http://eggs:88/empty"

But if you have both lines enabled, then you get the following crash when that line in main.cpp tries to fetch the pointer:

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.

Having no idea why these two things would be related, we considered that it might be timing (unlikely, but possible). So we put a wait line in front of the pointer fetch:

Do {} while ( engine.rootObjects().isEmpty());

But it never becomes non-empty. We also tried our real HTTP library, an empty library, and a bogus URL, with the exact same results each time (so you can try it without actually having a URL).

Does anyone have any insight on why this is happening?

If this is an obscure bug, can anyone think of a work-around?

Is there another way to fetch a pointer to the root window for C++ use?

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Mark
  • 217
  • 2
  • 9

1 Answers1

5

There is nothing obscure but it is expected behavior. The rootObjects are assigned to C++ when the object is finished building and since you have an asynchronous loading url (the http) then the construction is asynchronous. So if you want to get root then you must use the objectCreated signal:

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);
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • But if I wait for "engine.rootObjects().isEmpty()" to be not empty, shouldn't that also work? Or is the fact that I'm delaying "app.exec()" while I wait preventing the list from being built? – Mark Nov 13 '20 at 08:03
  • @Mark How to implement the wait that the list is not empty? Well, in order not to block the construction then you must use some signal, and that signal is objectCreated. – eyllanesc Nov 13 '20 at 08:06