How to connect Python and QML with PySide2?
我想在Ubuntu上编写一个简单的桌面应用程序,我认为一种简单的方法是使用Qt和QML作为GUI和Python作为逻辑语言,因为我对Python有点熟悉。
现在,我花了几个小时尝试以某种方式连接GUI和逻辑,但是它不起作用。
我管理连接QML-> Python,但没有其他方法。我有代表我的数据模型的Python类,并添加了JSON编码和解码功能。因此,目前尚不涉及SQL数据库。但是,也许QML视图和某些数据库之间的直接连接会使事情变得更容易?
所以现在有一些代码。
QML-> Python
QML文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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!") } } } } |
然后我有我的slot.py:
1 2 3 4 5 6 7 8 | from PySide2.QtCore import Slot def connect_slots(win): win.tmsPrint.connect(say_hello) @Slot(str) def say_hello(text): print(text) |
最后是我的main.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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!"。但这是最好的方法吗?还是创建一个带有插槽的类并使用
Python-> QML
我做不到。我尝试了不同的方法,但是没有一种有效,而且我也不知道哪种方法最适合使用。我想做的是例如显示对象列表并提供在应用程序中操纵数据的方法等。
我添加了一个带有功能的附加文件
然后我尝试使用QMetaObject和invokeMethod,但是参数错误等导致错误。
这种方法有意义吗?实际上我不知道任何JavaScript,因此,如果没有必要,我宁愿不使用它。
ViewModel方法
我创建了一个文件viewmodel.py
1 2 3 4 5 6 7 | from PySide2.QtCore import QStringListModel class ListModel(QStringListModel): def __init__(self): self.textlines = ['hi', 'ho'] super().__init__() |
在main.py中,我添加了:
1 2 | model = ListModel() engine.rootContext().setContextProperty('myModel', model) |
和ListView看起来像这样:
1 2 3 4 5 6 7 8 | ListView { width: 180; height: 200 model: myModel delegate: Text { text: model.textlines } } |
我收到一个错误" myModel未定义",但是我猜想它仍然无法工作,因为委托仅接受一个元素而不是一个列表。
这是一个好方法吗?如果是的话,我该如何运作?
我感谢您的帮助!
我知道Qt文档,但对此不满意。所以也许我缺少了一些东西。但是PyQt似乎比PySide2更受欢迎(至少Google搜索似乎表明了这一点),而且PySide引用经常使用PySide1或不使用QML QtQuick的做事方式...
您的问题涉及很多方面,因此我将尝试在其答案中进行详细说明,并且此答案将不断更新,因为此类问题经常被问到,但它们是针对特定情况的解决方案,因此,我将自由给出一种通用方法,并在可能的情况下具体说明。
QML到Python:
您的方法之所以有效,是因为python中的类型转换是动态的,而在C ++中则不会发生。它适用于小型任务,但不可维护,逻辑必须与视图分离,因此不应依赖于逻辑。具体来说,假设打印的文本将由逻辑来执行一些处理,然后如果您修改信号的名称,或者如果数据不依赖于
所建议的建议是创建一个类,该类负责映射所需逻辑的数据并将其嵌入到
例:
main.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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_()) |
main.qml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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 } } } |
现在,如果您希望文本由另一个元素提供,则只需更改以下行:
从Python到QML:
我无法告诉您此方法的错误之处,因为您没有显示任何代码,但我确实指出了缺点。主要缺点是要使用此方法,您必须有权使用该方法,并且存在两种可能性,第一种是它是您的第一个示例中所示的rootObjects或在objectName中进行搜索,但是这种情况会发生最初是寻找对象的,然后获取它,并将其从QML中删除,例如,每次更改页面时都会创建并删除StackView的页面,因此此方法将不正确。
对我来说第二种方法是正确的方法,但是您没有正确使用它,这与QtWidgets专注于使用角色的QML中的行和列不同。首先,让我们正确地实现您的代码。
无法从
1 2 3 | model = QStringListModel() model.setStringList(["hi","ho"]) print(model.roleNames()) |
输出:
1 2 3 4 5 6 7 8 | { 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') } |
如果要获取文本,则必须使用角色
1 | Qt::DisplayRole 0 The key data to be rendered in the form of text. (QString) |
因此在
main.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 } } } |
如果要使其可编辑,则必须使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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 ++进行交互,但是最好的方法包括通过
正如您所指出的,PySide2的文档并不多,它正在实现中,您可以通过以下链接查看它。目前最多的是PyQt5的许多示例,所以我建议您了解两者之间的等效之处并进行翻译,因为它们是很小的更改,所以翻译并不难。