"TypeError: Cannot read property..." on app quit while using custom QML elements
-
Dear community,
I am using PySide6 6.7.0 and I am creating custom QML items like I learned in the tutorials: https://doc.qt.io/qtforpython-6/examples/example_qml_tutorials_extending-qml-advanced_adding.html
In my main.qml I am using my custom qml items like this:
CustomItemA { id: customItemA width: parent.width / 2 height: parent.height } CustomItemB { id: customItemB width: parent.width / 2 height: parent.height anchors.left: customItemA.right BusyIndicator { running: customItemB.isWorking } }
In most cases when I quit the application I get the following errors:
TypeError: Cannot read property 'right' of null TypeError: Cannot read property 'isWorking' of null
I guess there is a garbage collection issue. Is there something I need to know when it comes to custom qml elements and property bindings?
I read about a similar error when people are creating items and register them in the qml context, but my issue is different. I don't register them in the context, they are created on the QML side.
-
I have encountered this with PyQt. (I was actually hoping that maybe if I were to switch to PySide the problem would go away, but based on this forum thread that would be an unfounded hope.)
It is indeed intermittent, which increases the annoyance factor for me slightly. 😒
I was able to eliminate it for all my QML except for QML that I define with
pragma Singleton
. All of my singleton elements still display this barf-on-quit.UPDATE: There is a superior fix below by @GrecKo in https://forum.qt.io/post/800041
My workaround/hack/trick to eliminate the issue is like so:take the outermost element of the whole QML GUI, and display it from inside aLoader
connect thatLoader
toonAboutToQuit
(ofQt.application
), and "unload" the loaderized GUI at that point.
The outline of the code:
Loader { id: mainLoader anchors.fill: parent sourceComponent: loadableRect Connections { /* We need to explicitly unload our QML at a known good point during shutdown. Otherwise there is some weird race-on-quit where (some of the time) our QML code tries to access destroyed viewmodels, which produces harmless-but-spammy errors like: # OurFile.qml:52: TypeError: Cannot read property 'ourproperty' of null As of Feb 2024, we can still see the spammy stuff but only from QML files that have 'pragma Singleton'. (Unsolved problem: how to unload our Singleton files.) */ target: Qt.application function onAboutToQuit() { console.log('Qml notified of app aboutToQuit') // per docs: // setting sourceComponent to undefined destroys the currently loaded object mainLoader.sourceComponent = undefined } } } Component { id: loadableRect Rectangle { id: ourContent color: root.color // ... all the rest of the GUI here .... } }
-
I've encountered a similar error, though it's intermittent for me. I can't say for sure, but I think it has something to do with the order in which objects are destroyed on application exit. In the OP's example, his CustomItems are being destroyed before the window that's displaying them.
Just an estimated guess, but hopefully someone more knowledgeable can weigh in on this.
-
@afalsa Here is an example. I get an
window.qml:28: TypeError: Cannot read property 'right' of null
on escape. It does not appear every time I quit, you might have to try it several times. I am using PySide6 6.7.0 on MacOS. And here is the Code:from PySide6.QtQml import QmlElement from PySide6.QtQuick import QQuickPaintedItem from PySide6.QtGui import QColor QML_IMPORT_NAME = "myelements" QML_IMPORT_MAJOR_VERSION = 0 QML_IMPORT_MINOR_VERSION = 1 @QmlElement class MyElementA(QQuickPaintedItem): def __init__(self, parent=None): super().__init__(parent) def paint(self, painter): painter.fillRect(0, 0, self.width(), self.height(), QColor("red")) @QmlElement class MyElementB(QQuickPaintedItem): def __init__(self, parent=None): super().__init__(parent) def paint(self, painter): painter.fillRect(0, 0, self.width(), self.height(), QColor("blue"))
window.qml
import QtQuick import QtQuick.Window import myelements Window { id: mainWindow width: 1200 height: 800 visible: true Shortcut { sequence: "Esc" onActivated: Qt.quit() } MyElementA { id: elementA width: 300 height: 300 } MyElementB { width: 300 height: 300 anchors.left: elementA.right } }
And for your convenience the app.y
import sys from PySide6.QtGui import QGuiApplication from PySide6.QtQml import QQmlApplicationEngine from qmlelements import MyElementA # noqa from qmlelements import MyElementB # noqa app = QGuiApplication(sys.argv) engine = QQmlApplicationEngine() engine.quit.connect(app.quit) engine.load('window.qml') sys.exit(app.exec())
-
I have encountered this with PyQt. (I was actually hoping that maybe if I were to switch to PySide the problem would go away, but based on this forum thread that would be an unfounded hope.)
It is indeed intermittent, which increases the annoyance factor for me slightly. 😒
I was able to eliminate it for all my QML except for QML that I define with
pragma Singleton
. All of my singleton elements still display this barf-on-quit.UPDATE: There is a superior fix below by @GrecKo in https://forum.qt.io/post/800041
My workaround/hack/trick to eliminate the issue is like so:take the outermost element of the whole QML GUI, and display it from inside aLoader
connect thatLoader
toonAboutToQuit
(ofQt.application
), and "unload" the loaderized GUI at that point.
The outline of the code:
Loader { id: mainLoader anchors.fill: parent sourceComponent: loadableRect Connections { /* We need to explicitly unload our QML at a known good point during shutdown. Otherwise there is some weird race-on-quit where (some of the time) our QML code tries to access destroyed viewmodels, which produces harmless-but-spammy errors like: # OurFile.qml:52: TypeError: Cannot read property 'ourproperty' of null As of Feb 2024, we can still see the spammy stuff but only from QML files that have 'pragma Singleton'. (Unsolved problem: how to unload our Singleton files.) */ target: Qt.application function onAboutToQuit() { console.log('Qml notified of app aboutToQuit') // per docs: // setting sourceComponent to undefined destroys the currently loaded object mainLoader.sourceComponent = undefined } } } Component { id: loadableRect Rectangle { id: ourContent color: root.color // ... all the rest of the GUI here .... } }
-
-
@KH-219Design Thank you very much for this workaround. Since my content is in a loader anyway, it was easy to apply. However, ultimately, I would like to understand what causes those errors and how to solve them.
-
A simpler alternative was to connect the QGuiApplication aboutToQuit signal to the engine deleteLater slot for me.
one line in
main.py
:app.aboutToQuit.connect(engine.deleteLater)
-
@GrecKo you are a life-saver. Not only is your method simpler, but it works for my cursed Singletons, too!
app.aboutToQuit.connect(engine.deleteLater)
or in my case (because this is a hybrid app that embeds a QML view inside a larger widgets app):
QtWidgets.QApplication.instance().aboutToQuit.connect(qml_view.rootContext().engine().deleteLater)