关于 QML 和 PySide2 的几个问题

2022-01-19 00:00:00 python pyside2 qml

问题描述

我有以下情况,想用几个Qml:welcome.qml"、create.qml"、dashboard.qml"

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

在哪些情况下使用QQuickview或QqmlApplicationEngine?

in which cases to use QQuickview or QqmlApplicationEngine.?

我正在使用QQmlAplicatiobEngine"并使用 findChild 在对象中搜索以获取信号并处理逻辑,如果信号完成条件,我使用 engine.load 加载另一个 QML.

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.

蟒蛇:

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:

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


}

使用 QQuickview 或 QQmlAplicationEngine 效果更好.

It's better with QQuickview or QQmlAplicationEngine.


解决方案

从OP提供的内容,我会指出以下几个方面:

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

  • 什么时候应该使用QQmlAplicationEngineQQuickview?QQmlAplicationEngine 还是 QQuickview 更好?

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

使用一个或另一个取决于 QML 的根元素,如果根是 Window 或 ApplicationWindow,则必须使用 QQmlAplicationEngine,如果它是 Item 或其派生类,则可以使用 QQuickView.因此,对于上述一个并不比另一个更好.如果我用根窗口加载 QML 或用 QQuickView 加载 ApplicationWindow 会发生什么?然后它将显示 2 个窗口:一个来自 QQuickView,另一个来自 Window 或 ApplicationWindow.如果我用 QQmlApplicationEngine 加载带有 Item 的 QML 怎么办?好吧,您需要将它放在一个 Window 中,如 文档:

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:

与 QQuickView 不同,QQmlApplicationEngine 不会自动创建根窗口.如果您使用 Qt Quick 中的可视项目,则需要将它们放置在 Window 内.

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.

  • 不要从 python/C++ 访问 QML 元素

    QML 对变量有自己的处理方式,因此您可以随时删除或创建它,没有任何确定的内容,因此访问这些对象可能很危险,因为它们可能没有分配内存.正确的做法是反其道而行之,将 QObject 导出到 QML 并在 QML 中建立连接.

    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.

    我们会将上述概念应用到 OP 提供的代码中(有些类型我已经更正了).

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

    • 首先,既然根是ApplicationWindow,那么就应该使用QQmlApplicationEngine.

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

    您可以使用 setContextProperty 将后端导出到 QML,然后直接调用插槽,而不是在每个元素中创建更改"信号.使用 rootObjects() 获取对象是危险的,因为有异步加载的 qmls,而是使用 objectCreated 信号.

    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.

    ma​​in.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()
            }
        }
    }
    
  • 相关文章