Search code examples
pythonqmlpyside2

several questions about QML and PySide2


I have the following cases, I want to use several Qml: "welcome.qml", "create.qml", "dashboard.qml"

in which cases to use QQuickview or QqmlApplicationEngine.?

I am using "QQmlAplicatiobEngine" and search in the object with findChild to get the signal, and handle the logic, If the signal completes a condition, I use the engine.load to load another QML.

python:

class guiBackend(QObject):

    def __init__(self):
        self.engine = QQmlApplicationEngine()
        self.context = self.engine.rootContext()
        self.context.setContextProperty("main", self.engine)
        self.welcome()

    def welcome(self):
        self.engine.load("welcome.qml")
        self.engine.rootObjects()[0].show()
        ob = self.engine.rootObjects()[0]
        next = ob.findChild(QObject, "timer")
        print(dir(next))
        if  path.exists('data.some'):
            next.change.connect(self.open_account)
        else:
            next.change.connect(self.create_account)
    def create(self):
        self.engine.rootObjects()[0].close()
        self.engine.load("create.qml")
        self.engine.rootObjects()[1].show()
        add = ob.findChild(QObject, "addWallet")
        recovery = ob.findChild(QObject, "recovery")
        add.change.connect(self.add_account)
        recovery.change.connect(self.recovery)
        #self.open_account load dashboard.qml and self.account load form.qml

if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    gui = guiBackend()
    sys.exit(app.exec_())

Qml:

ApplicationWindow {
    id: welcome
    width: 650; height: 390
    opacity: 1
    visible: true
    minimumHeight: 232
    minimumWidth:226
    title: "open account"
    flags: Qt.SplashScreen
    Item {

        id: element
        x: 9
        y: 88
        width: 560
        height: 300
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.verticalCenter: parent.verticalCenter
        Timer {
        signal change
        objectName: "timer"
        interval: 5000; running: true; repeat: false
        onTriggered: change()
        }
        Image {
            id: image
            x: 130
            y: 60
            width: 342
            height: 188
            anchors.verticalCenterOffset: -56
            anchors.horizontalCenterOffset: 0
            anchors.verticalCenter: parent.verticalCenter
            anchors.horizontalCenter: parent.horizontalCenter
            source: "neirons_logo.png"
            fillMode: Image.PreserveAspectFit
        }

        AnimatedImage {
            id: animatedImage
            x: 236
            y: 200
            width: 100
            height: 100
            source: "loading.gif"
        }
    }
}

create.qml:

ApplicationWindow {
    id: create
    width: 210; height: 210

    Rectangle {
        id: rectangle
        x: 70
        y: 132
        width: 200
        height: 200
        color: "#72ded8"
        anchors.verticalCenterOffset: 0
        anchors.horizontalCenterOffset: 0
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.verticalCenter: parent.verticalCenter

        ColumnLayout {
            x: 60
            y: 73
            width: 109
            height: 128
            anchors.verticalCenter: parent.verticalCenter
            anchors.horizontalCenter: parent.horizontalCenter

            TextInput {
                id: nameAccount
                objectName: textAccount
                text: qsTr("nameAccount")
                Layout.fillWidth: true
                Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
                Layout.preferredHeight: 20
                Layout.preferredWidth: 80
                verticalAlignment: Text.AlignVCenter
                font.pixelSize: 12
            }

            TextInput {
                id: nameAccount
                objectName: textAccount
                text: qsTr("Name Account")
                Layout.fillWidth: true
                Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
                Layout.preferredHeight: 20
                Layout.preferredWidth: 80
                font.pixelSize: 12
            }
        }

        Button {
            signal addinfo
            id: submitNameAccount
            objectName: submit
            x: 50
            y: 134
            width: 81
            height: 23
            text: qsTr("Add")
            font.bold: true
            font.pointSize: 12
            anchors.horizontalCenter: parent.horizontalCenter
            onClicked: addinfo()
    }


}

It's better with QQuickview or QQmlAplicationEngine.


Solution

  • From what the OP provides, I will point out the following aspects:

    • When should QQmlAplicationEngine or QQuickview be used? is better QQmlAplicationEngine or QQuickview ?

      The use of one or the other depends on the root element of the QML, if the root is Window or ApplicationWindow then you must use QQmlAplicationEngine, if instead it is an Item or its derivatives you can use QQuickView. So for the above one is not better than another. What happens if I load a QML with root Window or ApplicationWindow with QQuickView? Then it will show 2 windows: One will be from the QQuickView and the other from the Window or ApplicationWindow. What if I load a QML with Item with QQmlApplicationEngine? Well you will need to place it inside a Window as indicated by the docs:

      Unlike QQuickView, QQmlApplicationEngine does not automatically create a root window. If you are using visual items from Qt Quick, you will need to place them inside of a Window.

    • Do not access QML elements from python/C++

      QML has its own handling of the variables so you could delete it or create it at any time, there is nothing determined, so accessing these objects can be dangerous since they may not have allocated memory. The right thing is to do the opposite, export a QObject to QML and make connections in QML.


    We will apply the above concepts to the code provided by the OP (there are types that I have corrected).

    • First of all, since the roots are ApplicationWindow, then QQmlApplicationEngine should be used.

    • Instead of creating the "change" signal in each element, you can export the backend to QML using setContextProperty and then call the slots directly. To obtain the object using rootObjects() is dangerous since there are qmls that are loaded asynchronously, instead use the objectCreated signal.

    main.py

    import os
    
    from PyQt5 import QtCore, QtGui, QtQml
    
    CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
    
    
    class Backend(QtCore.QObject):
        def __init__(self, parent=None):
            super().__init__(parent)
    
            self._engine = QtQml.QQmlApplicationEngine()
            self._welcome = None
            self._wallet = None
            self.engine.objectCreated.connect(self.on_object_created)
            self.engine.rootContext().setContextProperty("backend", self)
    
            self.load_welcome()
    
        @property
        def engine(self):
            return self._engine
    
        @property
        def welcome(self):
            return self._welcome
    
        @property
        def wallet(self):
            return self._wallet
    
        @staticmethod
        def create_url(qml):
            return QtCore.QUrl.fromLocalFile(os.path.join(CURRENT_DIR, qml))
    
        def load_welcome(self):
            self.engine.load(self.create_url("welcome.qml"))
    
        @QtCore.pyqtSlot(QtCore.QObject, QtCore.QUrl)
        def on_object_created(self, obj, url):
            if url == self.create_url("welcome.qml"):
                self._welcome = obj
            elif url == self.create_url("wallet.qml"):
                self._wallet = obj
    
        @QtCore.pyqtSlot()
        def create_wallet(self):
            self.welcome.close()
            self.engine.load(self.create_url("wallet.qml"))
    
        @QtCore.pyqtSlot()
        def add_info(self):
            print("add_info")
    
    
    if __name__ == "__main__":
        import sys
    
        app = QtGui.QGuiApplication(sys.argv)
        backend = Backend()
    
        sys.exit(app.exec_())
    

    welcome.qml

    import QtQuick 2.14
    import QtQuick.Controls 2.14
    
    ApplicationWindow {
        id: root
        width: 650; height: 390
        opacity: 1
        visible: true
        minimumHeight: 232
        minimumWidth:226
        title: "open account"
        flags: Qt.SplashScreen
        Item {
            id: element
            x: 9
            y: 88
            width: 560
            height: 300
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.verticalCenter: parent.verticalCenter
            Timer {
                interval: 5000; running: true; repeat: false
                onTriggered: backend.create_wallet()
            }
            Image {
                id: image
                x: 130
                y: 60
                width: 342
                height: 188
                anchors.verticalCenterOffset: -56
                anchors.horizontalCenterOffset: 0
                anchors.verticalCenter: parent.verticalCenter
                anchors.horizontalCenter: parent.horizontalCenter
                source: "neirons_logo.png"
                fillMode: Image.PreserveAspectFit
            }
    
            AnimatedImage {
                id: animatedImage
                x: 236
                y: 200
                width: 100
                height: 100
                source: "loading.gif"
            }
        }
    }
    

    wallet.qml

    import QtQuick 2.14
    import QtQuick.Controls 2.14
    import QtQuick.Layouts 1.14
    
    
    ApplicationWindow {
        id: root
        visible: true
        width: 210; height: 210
    
        Rectangle {
            id: rectangle
            x: 70
            y: 132
            width: 200
            height: 200
            color: "#72ded8"
            anchors.verticalCenterOffset: 0
            anchors.horizontalCenterOffset: 0
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.verticalCenter: parent.verticalCenter
    
            ColumnLayout {
                x: 60
                y: 73
                width: 109
                height: 128
                anchors.verticalCenter: parent.verticalCenter
                anchors.horizontalCenter: parent.horizontalCenter
    
                TextInput {
                    id: nameAccount1
                    text: qsTr("nameAccount")
                    Layout.fillWidth: true
                    Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
                    Layout.preferredHeight: 20
                    Layout.preferredWidth: 80
                    verticalAlignment: Text.AlignVCenter
                    font.pixelSize: 12
                }
    
                TextInput {
                    id: nameAccount2
                    text: qsTr("Name Account")
                    Layout.fillWidth: true
                    Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
                    Layout.preferredHeight: 20
                    Layout.preferredWidth: 80
                    font.pixelSize: 12
                }
            }
    
            Button {
                id: submitNameAccount
                x: 50
                y: 134
                width: 81
                height: 23
                text: qsTr("Add")
                font.bold: true
                font.pointSize: 12
                anchors.horizontalCenter: parent.horizontalCenter
                onClicked: backend.add_info()
            }
        }
    }