4

Here's my situation. I'm trying to combine qml with a mostly widget based UI. To do this, I'm using QQuickView with QWidget::createWindowContainer. I can't use QQuickWidget, because I need to convert the window into a native window, and QQuickWidget doesn't like that. But back to the issue.

My problem is that the first time the view is displayed, it takes like half a second to load causing a very obvious flicker. After that I can hide/show the view all I want, it displays immediately. It's only the first time the qml loads. And I'm fairly certain it's the loading of the qml that causes the issue. Because I have two different QQuickViews that get the same qml as their source. But after any one of them loads once, the other has no issues displaying instantly.

I tried to call show() on view early to get it to load in time. But this causes the qml to appear for a brief moment before any of the widgets get displayed.

Has anyone encountered a similar issue? How can I get the QQuickView to behave.

Edit: I'm using Qt 5.4.2, and I can't update to a newer version due to various reasons.

Adam Rudas
  • 51
  • 6

1 Answers1

3

I was going to say that you can use the same approach as in this answer, but it seems that even that is too early to being loading the QML. It's hacky, but the only other thing I can think of is using a very short Timer:

main.cpp:

#include <QtWidgets>
#include <QtQuick>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0) :
        QMainWindow(parent)
    {
        QQuickView *view = new QQuickView();
        QWidget *container = QWidget::createWindowContainer(view, this);
        container->setFocusPolicy(Qt::TabFocus);
        view->rootContext()->setContextProperty("window", view);
        view->setSource(QUrl("qrc:/main.qml"));
        setCentralWidget(container);
        resize(400, 400);
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

#include "main.moc"

main.qml:

import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 2.0

Item {
    anchors.fill: parent

//    Connections {
//        target: window
//        onAfterSynchronizing: loader.active = true
//    }

    Timer {
        running: true
        repeat: true
        interval: 50
        onTriggered: {
            loader.active = true
        }
    }

    Loader {
        id: loader
        active: false
        sourceComponent: Column {
            anchors.fill: parent

            Repeater {
                model: 30000
                delegate: Button {
                    text: index
                }
            }
        }
    }

    BusyIndicator {
        running: loader.status === Loader.Null
        anchors.centerIn: parent
    }
}

For brevity, I chucked the "heavy" QML into sourceComponent, but you can also use the source property to point to a URL.

BusyIndicator runs its animation on the render thread, so it can continue to spin while the GUI thread is blocked.

Community
  • 1
  • 1
Mitch
  • 23,716
  • 9
  • 83
  • 122
  • I looked into your solution, but then noticed you used qml components that I don't have access to. I apologise, I should have mentioned it in the question. But hopefully this will be useful information to others with similar issues. – Adam Rudas Sep 12 '16 at 13:18
  • You can change `import QtQuick.Controls 2.0` to `import QtQuick.Controls 1.3` (or whatever version of Controls was released with Qt 5.4.2) and it should work just the same. – Mitch Sep 12 '16 at 15:20
  • Sorry, my bad. I must have looked up something wrong. I managed to try out your solution, but sadly it won't solve my issue. This is useful, if you have a complicated qml that takes time to load. My problem is that even a very simple one button qml takes a split second to load for the first time. With your solution, the loader itself takes a split second to appear, and I can just barely make out the loading circle before my qml appears. – Adam Rudas Sep 15 '16 at 09:11