gpt4 book ai didi

python - 如何使用 PySide2 连接 Python 和 QML?

转载 作者:行者123 更新时间:2023-12-02 11:17:57 27 4
gpt4 key购买 nike

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

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

现在一些代码。

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!")
}
}
}
}

然后我有我的slots.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_())

这工作正常,我可以打印“Hello!”。但这是最好的方法吗?还是创建一个带有槽的类并使用 setContextProperty 以便能够直接调用它们而不添加额外的信号更好?

Python --> QML

我无法完成这件事。我尝试了不同的方法,但没有一个有效,而且我也不知道哪一种是最好的。例如,我想要做的是显示对象列表并提供操作应用程序中的数据的方法等。

  1. 包括 JavaScript:我添加了一个附加文件 application.js ,其函数只是用于打印某些内容,但它可能用于设置文本字段等的上下文。然后我尝试使用 QMetaObject 和 invokeMethod,但只是出现了参数错误等错误。

这种方法有意义吗?其实我不懂javascript,所以如果没有必要,我宁愿不使用它。

  • ViewModel 方法我创建了一个文件 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 看起来像这样:

    ListView {
    width: 180; height: 200

    model: myModel
    delegate: Text {
    text: model.textlines
    }
    }

    我收到一个错误“myModel 未定义”,但我猜它无论如何都无法工作,因为委托(delegate)只接受一个元素而不是一个列表。这种方法好吗?如果是,我该如何让它发挥作用?

  • 是否有完全不同的方法来操作 QML View 中的数据?
  • 感谢您的帮助!我知道 Qt 文档,但我对它不满意。所以也许我错过了一些东西。但 PyQt 似乎比 PySide2 更受欢迎(至少谷歌搜索似乎表明了这一点),并且 PySide 引用文献经常使用 PySide1 或不使用 QML QtQuick 的做事方式......

    最佳答案

    您的问题涉及很多方面,因此我会尽力在回答中详细说明,并且此答案将不断更新,因为此类问题经常被问到,但它们是针对特定案例的解决方案,因此我将冒昧地回答给出一个通用方法并具体说明可能的场景。

    QML 到 Python:

    你的方法之所以有效,是因为 python 中的类型转换是动态的,而在 C++ 中则不会发生。它适用于小任务,但不可维护,逻辑必须与 View 分离,因此它不应该是依赖的。具体来说,假设打印的文本将由逻辑进行某些处理,那么如果您修改信号的名称,或者数据不依赖于 ApplicationWindow 而是依赖于另一个元素等,那么你将不得不更改很多连接代码。

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

    示例:

    ma​​in.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_())

    ma​​in.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
    }
    }
    }

    现在,如果您希望由另一个元素提供文本,您只需更改该行:onClicked: backend.text = tf.text

    <小时/>

    Python 到 QML:

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

    2. 对我来说第二种方法是正确的,但您没有正确使用它,与 QtWidgets 不同,QtWidgets 专注于 QML 中的行和列,使用角色。首先让我们正确实现您的代码。

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

    model = QStringListModel()
    model.setStringList(["hi", "ho"])
    print(model.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')
    }

    如果您想获取文本,则必须使用角色Qt::DisplayRole,其数值根据 docs是:

    Qt::DisplayRole 0   The key data to be rendered in the form of text. (QString)

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

    ma​​in.py

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

    ma​​in.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
    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的文档不多,它正在实现中,你可以通过下面的link看到它。 。存在的最多的是 PyQt5 的许多示例,因此我建议您了解两者之间的等效项并进行翻译,此翻译并不难,因为它们是最小的更改。

    关于python - 如何使用 PySide2 连接 Python 和 QML?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50609986/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com