从 Python 后端发出 QStandardItemModel 导致 "undefined" QML 对象
Emitting QStandardItemModel from Python backend results in "undefined" QML object
我正在尝试通过与包含后端的 ContextProperty 的连接将 QStandardItemModel 对象传递给 QML,该后端基于按钮单击生成此模型,但是虽然模型是在 Python 中正确创建的,但似乎它不能被 QML 加载。实际上,我正在处理的应用程序更为复杂,但我包含了一个最小示例,它复制了我得到的错误。基本上,当需要设置通过信号传递给 QML 的模型时,我收到错误消息:Unable to assign [undefined] to QAbstractItemModel*
我知道这种问题以前可能有人问过,我也找到了其他相关问题,但是他们的解决方案与我实现的非常相似,所以我没能确切地看到错误发生的地方。
首先,QML 应用程序是一个简单的 window,带有一个按钮和一个在应用程序启动时以“空”模型开头的 TreeView。单击按钮后,它会在后端触发创建模型的槽(“trigger_signal_send”),并发出包含该模型的信号。 QML 文件还有一个 Connection 对象,它侦听从“后端”发出的信号并更新 TreeView 中的模型。请注意,我只包括相关行(省略了锚点和图形属性等内容)。
main.qml
import QtQuick 2.14
import QtQuick.Controls 1.4
import QtQuick.Window 2.14
Window {
id: mainWindow
width: 640
height: 480
visible: true
title: qsTr("Hello World")
property var myModel: null
function setModel(model) {
mainWindow.myModel = model
}
Rectangle {
anchors.fill: parent
anchors.margins: {
top: 10
left: 10
right: 10
bottom: 60
}
clip: true
TreeView {
id: treeView
model: mainWindow.myModel
anchors.fill: parent
}
}
Button {
id: btn
onClicked: {
backend.trigger_signal_send()
}
}
Connections {
id: conn
target: backend
function onSendModel(model) {
mainWindow.setModel(model)
}
}
}
main.py
这是我定义后端 class 的地方,在单击按钮时使用相关的插槽生成和发出模型,我在这里通过 ContextProperty 将我的后端对象连接到 QML。
# This Python file uses the following encoding: utf-8
import os
from pathlib import Path
import sys
from PySide2.QtCore import QObject, Signal, Slot
from PySide2.QtGui import QGuiApplication, QStandardItemModel, QStandardItem
from PySide2.QtQml import QQmlApplicationEngine
class Backend(QObject):
# Class attributes
model: QStandardItemModel = QStandardItemModel()
# Signals
sendModel: Signal = Signal(QStandardItemModel)
def __init__(self, *args, **kwargs):
super(Backend, self).__init__(*args, **kwargs)
self.sendModel.connect(self.print_model)
@Slot()
def print_model(self, model):
print(model.item(0).text())
return
@Slot(result=None)
def trigger_signal_send(self):
self.model = QStandardItemModel()
self.model.appendRow(QStandardItem('TEST ITEM'))
self.sendModel.emit(self.model)
return
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
backend = Backend()
engine.rootContext().setContextProperty('backend', backend)
engine.load(os.fspath(Path(__file__).resolve().parent / "main.qml"))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
我尝试将 @Slot 装饰器中的“result”关键字设置为 QStandardItemModel 类型,并 return 直接从该槽中输入模型,但我在 QML 级别遇到错误:未知方法return类型:QStandardItemModel*。这是我将 return 值更改为 None 并仅将该模型作为信号发出的理由。我还尝试将 QML 中的“setModel”函数更改为直接引用 treeView.model 属性,但我得到了原始错误。
我知道模型创建正确,类型正确,因为如果我直接在后端创建另一个 Slot class,它将在发出“sendSignal”时打印第一项的文本, 一切正常。
此外,当设置“onSendSignal”的第一行以记录对该函数的输入时,它显示“未定义”,因此问题出在后端调用 sendSignal.emit(model) 之间以及在我的 .qml 文件的连接块中调用 onSendSignal(model) 时。
我花了很多时间尝试不同的解决方案,但我似乎无法弄清楚这两个函数调用之间丢失了什么。任何帮助将不胜感激!
QML 无法识别 QStandardItemModel 类型,导出模型时最好使用 QObject 作为签名:
# Signals
sendModel: Signal = Signal(QObject)
与 QTreeView 不同,在 TreeView 中,您必须设置每列将使用的角色:
TreeView {
id: treeView
model: mainWindow.myModel
anchors.fill: parent
TableViewColumn {
title: "Name"
role: "display"
width: 300
}
}
我正在尝试通过与包含后端的 ContextProperty 的连接将 QStandardItemModel 对象传递给 QML,该后端基于按钮单击生成此模型,但是虽然模型是在 Python 中正确创建的,但似乎它不能被 QML 加载。实际上,我正在处理的应用程序更为复杂,但我包含了一个最小示例,它复制了我得到的错误。基本上,当需要设置通过信号传递给 QML 的模型时,我收到错误消息:Unable to assign [undefined] to QAbstractItemModel*
我知道这种问题以前可能有人问过,我也找到了其他相关问题,但是他们的解决方案与我实现的非常相似,所以我没能确切地看到错误发生的地方。
首先,QML 应用程序是一个简单的 window,带有一个按钮和一个在应用程序启动时以“空”模型开头的 TreeView。单击按钮后,它会在后端触发创建模型的槽(“trigger_signal_send”),并发出包含该模型的信号。 QML 文件还有一个 Connection 对象,它侦听从“后端”发出的信号并更新 TreeView 中的模型。请注意,我只包括相关行(省略了锚点和图形属性等内容)。
main.qml
import QtQuick 2.14
import QtQuick.Controls 1.4
import QtQuick.Window 2.14
Window {
id: mainWindow
width: 640
height: 480
visible: true
title: qsTr("Hello World")
property var myModel: null
function setModel(model) {
mainWindow.myModel = model
}
Rectangle {
anchors.fill: parent
anchors.margins: {
top: 10
left: 10
right: 10
bottom: 60
}
clip: true
TreeView {
id: treeView
model: mainWindow.myModel
anchors.fill: parent
}
}
Button {
id: btn
onClicked: {
backend.trigger_signal_send()
}
}
Connections {
id: conn
target: backend
function onSendModel(model) {
mainWindow.setModel(model)
}
}
}
main.py 这是我定义后端 class 的地方,在单击按钮时使用相关的插槽生成和发出模型,我在这里通过 ContextProperty 将我的后端对象连接到 QML。
# This Python file uses the following encoding: utf-8
import os
from pathlib import Path
import sys
from PySide2.QtCore import QObject, Signal, Slot
from PySide2.QtGui import QGuiApplication, QStandardItemModel, QStandardItem
from PySide2.QtQml import QQmlApplicationEngine
class Backend(QObject):
# Class attributes
model: QStandardItemModel = QStandardItemModel()
# Signals
sendModel: Signal = Signal(QStandardItemModel)
def __init__(self, *args, **kwargs):
super(Backend, self).__init__(*args, **kwargs)
self.sendModel.connect(self.print_model)
@Slot()
def print_model(self, model):
print(model.item(0).text())
return
@Slot(result=None)
def trigger_signal_send(self):
self.model = QStandardItemModel()
self.model.appendRow(QStandardItem('TEST ITEM'))
self.sendModel.emit(self.model)
return
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
backend = Backend()
engine.rootContext().setContextProperty('backend', backend)
engine.load(os.fspath(Path(__file__).resolve().parent / "main.qml"))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
我尝试将 @Slot 装饰器中的“result”关键字设置为 QStandardItemModel 类型,并 return 直接从该槽中输入模型,但我在 QML 级别遇到错误:未知方法return类型:QStandardItemModel*。这是我将 return 值更改为 None 并仅将该模型作为信号发出的理由。我还尝试将 QML 中的“setModel”函数更改为直接引用 treeView.model 属性,但我得到了原始错误。
我知道模型创建正确,类型正确,因为如果我直接在后端创建另一个 Slot class,它将在发出“sendSignal”时打印第一项的文本, 一切正常。
此外,当设置“onSendSignal”的第一行以记录对该函数的输入时,它显示“未定义”,因此问题出在后端调用 sendSignal.emit(model) 之间以及在我的 .qml 文件的连接块中调用 onSendSignal(model) 时。
我花了很多时间尝试不同的解决方案,但我似乎无法弄清楚这两个函数调用之间丢失了什么。任何帮助将不胜感激!
QML 无法识别 QStandardItemModel 类型,导出模型时最好使用 QObject 作为签名:
# Signals
sendModel: Signal = Signal(QObject)
与 QTreeView 不同,在 TreeView 中,您必须设置每列将使用的角色:
TreeView {
id: treeView
model: mainWindow.myModel
anchors.fill: parent
TableViewColumn {
title: "Name"
role: "display"
width: 300
}
}