Solved QML working in QQuickView, but not in custom QQuickWindow
-
My aim is to use QML for GUI on top of some custom rendering that I am doing in OpenGL. Before going into that, I thought it would be good to get a better understanding of QML. Therefore, I thought I might start by implementing my own version of QQuickView on top of QQuickWindow (slowly working my way deeper into the workings of QML). However, while doing so, I quickly ran into a problem with the following QML:
import QtQuick 2.5 import QtQuick.Controls 1.4 Item { anchors.fill: parent ListModel { id: fruitModel ListElement { title: "Apple" page: "Banana" } ListElement { title: "Orange" page: "Banana" } ListElement { title: "Banana" page: "Banana" } } ListView { anchors.fill: parent model: fruitModel delegate: Rectangle{ width: parent.width; height: 80 color: "red" Text { anchors.fill: parent color: "black" text: title } } } }
This is a pretty simple scene that works perfectly well if I display it in a QQuickView. However, if I use my custom window, only the first list item is displayed. Can anybody point me towards what is missing in my QQuickWindow subclass to make this work?
#! /usr/bin/python3 import os import sys from PyQt5 import QtCore, QtGui, QtNetwork, QtQml, QtQuick, QtWidgets def application_path(*paths): return os.path.join(os.path.dirname(os.path.realpath(__file__)), *paths) class MainWindow(QtQuick.QQuickWindow): def __init__(self): super(MainWindow, self).__init__() self._engine = QtQml.QQmlEngine(self) if not self._engine.incubationController(): self._engine.setIncubationController(self.incubationController()) self._engine.quit.connect(QtWidgets.QApplication.instance().quit) def setSource(self, url): context = QtQml.QQmlContext(self._engine) component = QtQml.QQmlComponent(self._engine) component.loadUrl(url) if component.isError(): logger.error(component.errorString()) return obj = component.create(context) if component.isError(): logger.error(component.errorString()) return if isinstance(obj, QtQuick.QQuickItem): obj.setParentItem(self.contentItem()) self._root = obj if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) #view = QtQuick.QQuickView() view = MainWindow() view.setSource(QtCore.QUrl.fromLocalFile(application_path("main.qml"))) view.resize(800, 600) view.show() sys.exit(app.exec_())
-
Hi @kloffy and Welcome,
I'm not aware of PyQt so I'm not sure what is causing that problem in that case. But I replicated the same code using C++ and it worked as expected. All 3 items are shown. Following is what I tried:
MainWindow.cppMainWindow::MainWindow() { QQmlEngine *eng = new QQmlEngine(this); eng->setIncubationController(incubationController()); QQmlContext *con = new QQmlContext(eng); QQmlComponent *comp = new QQmlComponent(eng); comp->loadUrl(QUrl("qrc:/main.qml")); QQuickItem *obj = qobject_cast<QQuickItem*>(comp->create(con)); obj->setParentItem(contentItem()); }
main.cpp
QGuiApplication app(argc, argv); MainWindow win; win.resize(800,600); win.show(); return app.exec();
-
Thank you for testing the code in C++, I should have probably tried that myself. Your response has led me towards a solution. It appears that the problem is indeed specific to PyQt5, and in particular I have a feeling it might be related to object lifetimes. Something might be getting destroyed before it should be, which has me a bit worried about the robustness of PyQt5 in general. I can make the example work by keeping a reference to the context in a member of MainWindow, i.e.:
self._context = QtQml.QQmlContext(self._engine)
I was under the impression that in Qt's ownership model, the QQmlContext would be kept alive by its parent the QQmlEngine. I am really puzzled by what is going on, but I suppose in order to get clarity I will have to consult the PyQt5 guys.
-
Oh, mea culpa to the PyQt5 guys. I just saw that the parent is passed as the second parameter to QQmlContext. If I pass QQmlEngine twice, it also works:
context = QtQml.QQmlContext(self._engine, self._engine)
On the other hand, in that case I am a bit confused as to why this is not an issue in C++.
-
@kloffy I too have no idea about the implementation :) You can try asking it on the mailing lists.
http://lists.qt-project.org/mailman/listinfo/interest
http://lists.qt-project.org/mailman/listinfo
The Qt developers might know the exact reason behind these internals. -
Thanks again, the mailing list will come in handy, no doubt. As far as the original problem goes, I think it can be considered resolved! :)