你的问题有很多方面,所以我会尽力在我的答案中详细说明,而且这个答案也会不断更新,因为这类问题经常被问到,但它们是针对特定案例的解决方案,所以我将冒昧地给出它通用方法并针对可能的情况进行具体说明。
QML 到 Python:
您的方法之所以有效,是因为 python 中的类型转换是动态的,而在 C++ 中则不会发生。它适用于小任务,但不可维护,逻辑必须与视图分离,因此它不应该是依赖的。具体来说,假设打印的文本会被逻辑拿来进行一些处理,那么如果修改信号的名称,或者数据不依赖于ApplicationWindow
但在另一个元素等上,那么您将不得不更改很多连接代码。
正如您所指出的,建议创建一个类,负责映射您需要逻辑的数据并将其嵌入QML
,因此如果您更改视图中的某些内容,只需更改连接即可:
Example:
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_())
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
}
}
}
现在,如果您希望由另一个元素提供文本,您只需更改该行:onClicked: backend.text = tf.text
.
Python 到 QML:
我无法告诉你这种方法做错了什么,因为你没有显示任何代码,但我确实指出了缺点。主要缺点是要使用此方法,您必须有权访问该方法,并且有两种可能性,第一种是它是 rootObjects,如第一个示例中所示或通过 objectName 进行搜索,但它发生了您最初查找该对象,得到它,并将其从 QML 中删除,例如,每次更改页面时都会创建和删除 StackView 的页面,因此此方法不正确。
对我来说第二种方法是正确的,但您没有正确使用它,与 QtWidgets 不同,QtWidgets 专注于 QML 中的行和列,使用角色。首先让我们正确实现您的代码。
First textlines
无法从以下位置访问QML
因为它不是一个qproperty
。正如我所说,您必须通过角色访问,要查看模型的角色,您可以打印结果roleNames()
:
model = QStringListModel()
model.setStringList(["hi", "ho"])
print(model.roleNames())
output:
{
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 http://doc.qt.io/qt-5/qt.html#ItemDataRole-enum is:
Qt::DisplayRole 0 The key data to be rendered in the form of text. (QString)
so in QML
你应该使用model.display
(或仅display
)。所以正确的代码如下:
main.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_())
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
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++ 交互,但最好的方法包括通过以下方式嵌入在 Python/C++ 中创建的对象:setContextProperty
.
正如您所指出的,PySide2的文档不多,它正在实现,您可以通过以下内容看到它link https://doc-snapshots.qt.io/qtforpython/index.html。存在的最多的是 PyQt5 的许多示例,因此我建议您了解两者之间的等效项并进行翻译,此翻译并不难,因为它们是最小的更改。