Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/qt/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何将Python和QML与PySide2连接起来?_Python_Qt_Qml_Pyside2 - Fatal编程技术网

如何将Python和QML与PySide2连接起来?

如何将Python和QML与PySide2连接起来?,python,qt,qml,pyside2,Python,Qt,Qml,Pyside2,我想在Ubuntu上编写一个简单的桌面应用程序,我认为一个简单的方法是使用Qt,QML作为GUI,Python作为逻辑语言,因为我对Python有点熟悉 现在我花了几个小时试图以某种方式连接GUI和逻辑,但它不起作用。 我管理了QML-->Python的连接,但不是相反。我有代表我的数据模型的Python类,并添加了JSON编码和解码函数。所以目前还没有涉及SQL数据库。但是,QML视图和某些数据库之间的直接连接可能会使事情变得更简单 所以现在有一些代码 QML-->Python QML文件:

我想在Ubuntu上编写一个简单的桌面应用程序,我认为一个简单的方法是使用Qt,QML作为GUI,Python作为逻辑语言,因为我对Python有点熟悉

现在我花了几个小时试图以某种方式连接GUI和逻辑,但它不起作用。 我管理了QML-->Python的连接,但不是相反。我有代表我的数据模型的Python类,并添加了JSON编码和解码函数。所以目前还没有涉及SQL数据库。但是,QML视图和某些数据库之间的直接连接可能会使事情变得更简单

所以现在有一些代码

QML-->Python

QML文件:

ApplicationWindow {

// main window
id: mainWindow
title: qsTr("Test")
width: 640
height: 480

signal tmsPrint(string text)

Page {
    id: mainView

    ColumnLayout {
        id: mainLayout

        Button {
            text: qsTr("Say Hello!")
            onClicked: tmsPrint("Hello!")
        }
    }
}    
}
然后我有我的插槽。py:

from PySide2.QtCore import Slot

def connect_slots(win):
    win.tmsPrint.connect(say_hello)

@Slot(str)
def say_hello(text):
    print(text)
最后是我的main.py:

import sys
from controller.slots import connect_slots

from PySide2.QtWidgets import QApplication
from PySide2.QtQml import QQmlApplicationEngine 

if __name__ == '__main__':
    app = QApplication(sys.argv)

    engine = QQmlApplicationEngine()
    engine.load('view/main.qml')

    win = engine.rootObjects()[0]
    connect_slots(win)

    # show the window
    win.show()
    sys.exit(app.exec_())
这个很好用,我可以打印“你好!”。但是这是最好的方法,还是最好创建一个带有插槽的类,并使用
setContextProperty
直接调用它们而不添加额外的信号

Python-->QML

我做不到。我尝试了不同的方法,但都不管用,我也不知道哪种方法最好。例如,我想做的是显示对象列表,并提供在应用程序中操作数据的方法等

  • 包括Javascript: 我添加了一个额外的文件
    application.js
    ,其中包含一个函数,用于打印某些内容,但它可能用于设置文本字段的上下文等。 然后我尝试使用QMetaObject和invokeMethod,但只得到了错误的参数等错误
  • 这种方法有意义吗?事实上,我不知道任何javascript,所以如果没有必要,我宁愿不使用它

  • 视图模型方法 我创建了一个文件viewmodel.py

    from PySide2.QtCore import QStringListModel
    
    class ListModel(QStringListModel):
    
    def __init__(self):
         self.textlines = ['hi', 'ho']
         super().__init__()
    
  • 在main.py中,我添加了:

    model = ListModel()
    engine.rootContext().setContextProperty('myModel', model)
    
    列表视图如下所示:

    ListView {
                width: 180; height: 200
    
                model: myModel
                delegate: Text {
                    text: model.textlines
                }
            }
    
    我得到一个错误“myModel未定义”,但我想它无论如何都不能工作,因为委托只接受一个元素,而不是一个列表。 这个方法好吗?如果是的话,我如何让它工作

  • 是否有完全不同的方法在QML视图中操作数据 我感谢你的帮助!
    我知道Qt文档,但我对它不满意。也许我错过了什么。但PyQt似乎比PySide2更受欢迎(至少谷歌搜索似乎表明了这一点)PySide参考通常使用PySide1或不使用QML QtQuick做事方式…

    您的问题有很多方面,因此我将尝试在我的答案中详细说明,并且此答案将不断更新,因为这类问题经常被问到,但它们是针对特定情况的解决方案,因此我将冒昧地给出它是一种通用方法,在可能的场景中必须是特定的

    QML到Python:

    您的方法之所以有效,是因为Python中的类型转换是动态的,在C++中它不会发生。它适用于小任务,但不可维护,逻辑必须与视图分离,因此它不应该是依赖的。具体来说,假设打印文本将由逻辑执行一些处理,那么如果您修改信号的名称,或者如果数据不依赖于

    ApplicationWindow
    ,而是依赖于另一个元素等,那么您将不得不更改大量连接代码

    建议您创建一个类,该类负责映射您需要的逻辑数据,并将其嵌入到
    QML
    中,因此,如果您更改视图中的某些内容,只需更改连接即可:

    例如:

    main.py

    import sys
    
    from PySide2.QtCore import QObject, Signal, Property, QUrl
    from PySide2.QtGui import QGuiApplication
    from PySide2.QtQml import QQmlApplicationEngine
    
    class Backend(QObject):
        textChanged = Signal(str)
    
        def __init__(self, parent=None):
            QObject.__init__(self, parent)
            self.m_text = ""
    
        @Property(str, notify=textChanged)
        def text(self):
            return self.m_text
    
        @text.setter
        def setText(self, text):
            if self.m_text == text:
                return
            self.m_text = text
            self.textChanged.emit(self.m_text)   
    
    if __name__ == '__main__':
        app = QGuiApplication(sys.argv)
        backend = Backend()
    
        backend.textChanged.connect(lambda text: print(text))
        engine = QQmlApplicationEngine()
        engine.rootContext().setContextProperty("backend", backend)
        engine.load(QUrl.fromLocalFile('main.qml'))
        if not engine.rootObjects():
            sys.exit(-1)
        sys.exit(app.exec_())
    
    import sys
    
    from PySide2.QtCore import QUrl, QStringListModel
    from PySide2.QtGui import QGuiApplication
    from PySide2.QtQml import QQmlApplicationEngine  
    
    if __name__ == '__main__':
        app = QGuiApplication(sys.argv)
        model = QStringListModel()
        model.setStringList(["hi", "ho"])
    
        engine = QQmlApplicationEngine()
        engine.rootContext().setContextProperty("myModel", model)
        engine.load(QUrl.fromLocalFile('main.qml'))
        if not engine.rootObjects():
            sys.exit(-1)
        sys.exit(app.exec_())
    
    main.qml

    import QtQuick 2.10
    import QtQuick.Controls 2.1
    import QtQuick.Window 2.2
    
    ApplicationWindow {
        title: qsTr("Test")
        width: 640
        height: 480
        visible: true
        Column{
            TextField{
                id: tf
                text: "Hello"
            }
            Button {
                text: qsTr("Click Me")
                onClicked: backend.text = tf.text
            } 
        }
    }
    
    import QtQuick 2.10
    import QtQuick.Controls 2.1
    import QtQuick.Window 2.2
    
    ApplicationWindow {
        title: qsTr("Test")
        width: 640
        height: 480
        visible: true
        ListView{
            model: myModel
            anchors.fill: parent
            delegate: Text { text: model.display }
        }
    }
    
    现在,如果希望文本由另一个元素提供,只需更改行:
    onClicked:backend.text=tf.text


    Python到QML:

  • 我不能告诉你这个方法有什么错误,因为你没有显示任何代码,但我确实指出了缺点。主要缺点是,要使用此方法,您必须有权访问该方法,并且有两种可能性,第一种是它是rootObjects,如第一个示例中所示,或者通过objectName进行搜索,但是您最初查找对象时,得到了它,并且从QML中删除了它,例如,每次更改页面时都会创建和删除StackView的页面,因此此方法不正确

  • 对我来说,第二种方法是正确的,但是您没有正确地使用它,这与qtl中使用角色的行和列的qtwidget不同。首先,让我们正确地实现代码

  • 第一个
    文本行
    无法从
    QML
    访问,因为它不是
    qproperty
    。正如我所说,您必须通过角色访问,要查看模型的角色,您可以打印
    roleNames()
    的结果:

    输出:

    {
        0: PySide2.QtCore.QByteArray('display'),
        1: PySide2.QtCore.QByteArray('decoration'),
        2: PySide2.QtCore.QByteArray('edit'),
        3: PySide2.QtCore.QByteArray('toolTip'),
        4: PySide2.QtCore.QByteArray('statusTip'),
        5: PySide2.QtCore.QByteArray('whatsThis')
    }
    
    如果要获取文本,则必须使用role
    Qt::DisplayRole
    ,其数值符合以下条件:

    因此,在
    QML
    中,您应该使用
    model.display
    (或仅
    display
    )。因此,正确的代码如下所示:

    main.py

    import sys
    
    from PySide2.QtCore import QObject, Signal, Property, QUrl
    from PySide2.QtGui import QGuiApplication
    from PySide2.QtQml import QQmlApplicationEngine
    
    class Backend(QObject):
        textChanged = Signal(str)
    
        def __init__(self, parent=None):
            QObject.__init__(self, parent)
            self.m_text = ""
    
        @Property(str, notify=textChanged)
        def text(self):
            return self.m_text
    
        @text.setter
        def setText(self, text):
            if self.m_text == text:
                return
            self.m_text = text
            self.textChanged.emit(self.m_text)   
    
    if __name__ == '__main__':
        app = QGuiApplication(sys.argv)
        backend = Backend()
    
        backend.textChanged.connect(lambda text: print(text))
        engine = QQmlApplicationEngine()
        engine.rootContext().setContextProperty("backend", backend)
        engine.load(QUrl.fromLocalFile('main.qml'))
        if not engine.rootObjects():
            sys.exit(-1)
        sys.exit(app.exec_())
    
    import sys
    
    from PySide2.QtCore import QUrl, QStringListModel
    from PySide2.QtGui import QGuiApplication
    from PySide2.QtQml import QQmlApplicationEngine  
    
    if __name__ == '__main__':
        app = QGuiApplication(sys.argv)
        model = QStringListModel()
        model.setStringList(["hi", "ho"])
    
        engine = QQmlApplicationEngine()
        engine.rootContext().setContextProperty("myModel", model)
        engine.load(QUrl.fromLocalFile('main.qml'))
        if not engine.rootObjects():
            sys.exit(-1)
        sys.exit(app.exec_())
    
    main.qml

    import QtQuick 2.10
    import QtQuick.Controls 2.1
    import QtQuick.Window 2.2
    
    ApplicationWindow {
        title: qsTr("Test")
        width: 640
        height: 480
        visible: true
        Column{
            TextField{
                id: tf
                text: "Hello"
            }
            Button {
                text: qsTr("Click Me")
                onClicked: backend.text = tf.text
            } 
        }
    }
    
    import QtQuick 2.10
    import QtQuick.Controls 2.1
    import QtQuick.Window 2.2
    
    ApplicationWindow {
        title: qsTr("Test")
        width: 640
        height: 480
        visible: true
        ListView{
            model: myModel
            anchors.fill: parent
            delegate: Text { text: model.display }
        }
    }
    
    如果要使其可编辑,必须使用
    model.display=foo

    import QtQuick 2.10
    import QtQuick.Controls 2.1
    import QtQuick.Window 2.2
    
    ApplicationWindow {
        title: qsTr("Test")
        width: 640
        height: 480
        visible: true
        ListView{
            model: myModel
            anchors.fill: parent
            delegate: 
            Column{
                Text{ 
                    text: model.display 
                }
                TextField{
                    onTextChanged: {
                        model.display = text
                    }
                }
            }
        }
    }
    

    使用QML与Python/C++交互还有许多其他方法,但最好的方法是通过
    setContextProperty
    嵌入在Python/C++中创建的对象


    正如您所指出的,PySide2的文档并不多,它正在实现中,您可以通过以下内容看到它。最常见的是PyQt5的许多示例,因此我建议您了解两者之间的等效性,并进行翻译,因为这是最小的更改,所以翻译并不困难。

    谢谢!这很有帮助。我最好先检查一下PyQt5,看看这些概念是如何转换的