在 Pyside2 中将 QAbstractListModel 声明为 属性
Declaring a QAbstractListModel as a property in Pyside2
我将 Pyside2 与 QML 一起使用,并尽量保持代码的良好组织。
我想将 Python 的 QAbstractListModel
的子 class MyModel
暴露给 QML,以便在 ListView
中使用。如果我直接在引擎内声明 MyModel
实例,代码将完美运行:
...
engine = QQmlApplicationEngine()
myModel = MyModel(some_dict)
engine.rootContext().setContextProperty("myModel ", myModel)
...
我可以这样使用:
ListView {
model: myModel
delegate: Row {
Text { text: name }
Text { text: type }
}
}
但是,当我尝试将此元素定义为 class 的 Property
时,为了保持整洁而不是到处注册模型,我似乎做不到工作。我无法从 qml 中恢复良好的调试信息,这也无济于事。
我试图声明以下内容
class ModelProvider(QObject):
modelChanged = Signal()
_entries: List[Dict[str, Any]]
def __init__(self, entries, parent=None):
QObject.__init__(self, parent)
self._entries = entries
def _model(self):
return MyModel(self._entries)
myModel = Property(list, _model, notify=modelChanged)
myQVariantModel = Property('QVariantList', _model, notify=modelChanged)
...
modelProvider = ModelProvider(some_dict)
engine.rootContext().setContextProperty("modelProvider", modelProvider )
然后在qml中这样使用
ListView {
model: modelProvider.myModel
// or model: modelProvider.myQVariantModel
delegate: Row {
Text { text: name }
Text { text: type }
}
}
结果是黑屏。
我发现 there 一个潜在的原因可能是 QAbstractListModel
是一个 QObject
,这将使其不可复制,并且在 c++ 中他们建议将指针传递给它代替。但我认为 Python.
中会自动出现这种情况
在这种情况下我做错了什么?如果可能的话,我怎么能找出为什么 ListView
没有渲染任何东西(也许是调试输出)?这样组织我的代码根本不可能吗?
对于上下文,我尝试遵循 Bloc 模式,我很喜欢与 dart
和 flutter
一起使用,其中有一个(或多个)中央Bloc
class 为视图公开模型和作用于此模型的方法。
您必须指出 属性 是一个 QObject,而不是 QVariantList 或列表。另一方面,我不认为你改变了模型,所以你应该使用常量 属性 并且没有信号。此外,您不相信模型的功能,因为每次调用 _model 都会创建一个不同的对象。
main.py
import os
import sys
from functools import partial
from PySide2 import QtCore, QtGui, QtQml
class MyModel(QtCore.QAbstractListModel):
NameRole = QtCore.Qt.UserRole + 1000
TypeRole = QtCore.Qt.UserRole + 1001
def __init__(self, entries, parent=None):
super(MyModel, self).__init__(parent)
self._entries = entries
def rowCount(self, parent=QtCore.QModelIndex()):
if parent.isValid(): return 0
return len(self._entries)
def data(self, index, role=QtCore.Qt.DisplayRole):
if 0 <= index.row() < self.rowCount() and index.isValid():
item = self._entries[index.row()]
if role == MyModel.NameRole:
return item["name"]
elif role == MyModel.TypeRole:
return item["type"]
def roleNames(self):
roles = dict()
roles[MyModel.NameRole] = b"name"
roles[MyModel.TypeRole] = b"type"
return roles
def appendRow(self, n, t):
self.beginInsertRows(QtCore.QModelIndex(), self.rowCount(), self.rowCount())
self._entries.append(dict(name=n, type=t))
self.endInsertRows()
class ModelProvider(QtCore.QObject):
def __init__(self, entries, parent=None):
super(ModelProvider, self).__init__(parent)
self._model = MyModel(entries)
@QtCore.Property(QtCore.QObject, constant=True)
def model(self):
return self._model
def test(model):
n = "name{}".format(model.rowCount())
t = "type{}".format(model.rowCount())
model.appendRow(n, t)
def main():
app = QtGui.QGuiApplication(sys.argv)
entries = [
{"name": "name0", "type": "type0"},
{"name": "name1", "type": "type1"},
{"name": "name2", "type": "type2"},
{"name": "name3", "type": "type3"},
{"name": "name4", "type": "type4"},
]
provider = ModelProvider(entries)
engine = QtQml.QQmlApplicationEngine()
engine.rootContext().setContextProperty("provider", provider)
directory = os.path.dirname(os.path.abspath(__file__))
engine.load(QtCore.QUrl.fromLocalFile(os.path.join(directory, 'main.qml')))
if not engine.rootObjects():
return -1
timer = QtCore.QTimer(interval=500)
timer.timeout.connect(partial(test, provider.model))
timer.start()
return app.exec_()
if __name__ == '__main__':
sys.exit(main())
main.qml
import QtQuick 2.11
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
ApplicationWindow {
visible: true
width: 640
height: 480
ListView {
model: provider.model
anchors.fill: parent
delegate: Row {
Text { text: name }
Text { text: type }
}
}
}
我将 Pyside2 与 QML 一起使用,并尽量保持代码的良好组织。
我想将 Python 的 QAbstractListModel
的子 class MyModel
暴露给 QML,以便在 ListView
中使用。如果我直接在引擎内声明 MyModel
实例,代码将完美运行:
...
engine = QQmlApplicationEngine()
myModel = MyModel(some_dict)
engine.rootContext().setContextProperty("myModel ", myModel)
...
我可以这样使用:
ListView {
model: myModel
delegate: Row {
Text { text: name }
Text { text: type }
}
}
但是,当我尝试将此元素定义为 class 的 Property
时,为了保持整洁而不是到处注册模型,我似乎做不到工作。我无法从 qml 中恢复良好的调试信息,这也无济于事。
我试图声明以下内容
class ModelProvider(QObject):
modelChanged = Signal()
_entries: List[Dict[str, Any]]
def __init__(self, entries, parent=None):
QObject.__init__(self, parent)
self._entries = entries
def _model(self):
return MyModel(self._entries)
myModel = Property(list, _model, notify=modelChanged)
myQVariantModel = Property('QVariantList', _model, notify=modelChanged)
...
modelProvider = ModelProvider(some_dict)
engine.rootContext().setContextProperty("modelProvider", modelProvider )
然后在qml中这样使用
ListView {
model: modelProvider.myModel
// or model: modelProvider.myQVariantModel
delegate: Row {
Text { text: name }
Text { text: type }
}
}
结果是黑屏。
我发现 there 一个潜在的原因可能是 QAbstractListModel
是一个 QObject
,这将使其不可复制,并且在 c++ 中他们建议将指针传递给它代替。但我认为 Python.
在这种情况下我做错了什么?如果可能的话,我怎么能找出为什么 ListView
没有渲染任何东西(也许是调试输出)?这样组织我的代码根本不可能吗?
对于上下文,我尝试遵循 Bloc 模式,我很喜欢与 dart
和 flutter
一起使用,其中有一个(或多个)中央Bloc
class 为视图公开模型和作用于此模型的方法。
您必须指出 属性 是一个 QObject,而不是 QVariantList 或列表。另一方面,我不认为你改变了模型,所以你应该使用常量 属性 并且没有信号。此外,您不相信模型的功能,因为每次调用 _model 都会创建一个不同的对象。
main.py
import os
import sys
from functools import partial
from PySide2 import QtCore, QtGui, QtQml
class MyModel(QtCore.QAbstractListModel):
NameRole = QtCore.Qt.UserRole + 1000
TypeRole = QtCore.Qt.UserRole + 1001
def __init__(self, entries, parent=None):
super(MyModel, self).__init__(parent)
self._entries = entries
def rowCount(self, parent=QtCore.QModelIndex()):
if parent.isValid(): return 0
return len(self._entries)
def data(self, index, role=QtCore.Qt.DisplayRole):
if 0 <= index.row() < self.rowCount() and index.isValid():
item = self._entries[index.row()]
if role == MyModel.NameRole:
return item["name"]
elif role == MyModel.TypeRole:
return item["type"]
def roleNames(self):
roles = dict()
roles[MyModel.NameRole] = b"name"
roles[MyModel.TypeRole] = b"type"
return roles
def appendRow(self, n, t):
self.beginInsertRows(QtCore.QModelIndex(), self.rowCount(), self.rowCount())
self._entries.append(dict(name=n, type=t))
self.endInsertRows()
class ModelProvider(QtCore.QObject):
def __init__(self, entries, parent=None):
super(ModelProvider, self).__init__(parent)
self._model = MyModel(entries)
@QtCore.Property(QtCore.QObject, constant=True)
def model(self):
return self._model
def test(model):
n = "name{}".format(model.rowCount())
t = "type{}".format(model.rowCount())
model.appendRow(n, t)
def main():
app = QtGui.QGuiApplication(sys.argv)
entries = [
{"name": "name0", "type": "type0"},
{"name": "name1", "type": "type1"},
{"name": "name2", "type": "type2"},
{"name": "name3", "type": "type3"},
{"name": "name4", "type": "type4"},
]
provider = ModelProvider(entries)
engine = QtQml.QQmlApplicationEngine()
engine.rootContext().setContextProperty("provider", provider)
directory = os.path.dirname(os.path.abspath(__file__))
engine.load(QtCore.QUrl.fromLocalFile(os.path.join(directory, 'main.qml')))
if not engine.rootObjects():
return -1
timer = QtCore.QTimer(interval=500)
timer.timeout.connect(partial(test, provider.model))
timer.start()
return app.exec_()
if __name__ == '__main__':
sys.exit(main())
main.qml
import QtQuick 2.11
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
ApplicationWindow {
visible: true
width: 640
height: 480
ListView {
model: provider.model
anchors.fill: parent
delegate: Row {
Text { text: name }
Text { text: type }
}
}
}