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_())

  • Moderators

    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.cpp

    MainWindow::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++.


  • Moderators

    @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! :)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.