为什么在 QAbstractListModel 的子类中的 beginInsertRows() 和 endInsertRows() 之后不发出 dataChanged?
Why isn't dataChanged emitted after beginInsertRows() and endInsertRows() in a subclass of QAbstractListModel?
我一直在搞 QAbstractListModel 的子类化,我想我不明白如何正确地将数据添加到模型中。这是我的脚本和 QML:
import QtQuick
import QtQuick.Controls
ApplicationWindow
{
id: mainWindow
visible: true
title: qsTr("Sample Qt Quick application")
width: 400
height: 400
color: "whitesmoke"
Component.onCompleted: console.log("Component completed")
Connections
{
target: main.custom_model
function onDataChanged(topLeft, bottomRight, roles)
{
console.log("Custom model data changed")
}
}
} // ApplicationWindow
import sys
from random import randint
from pathlib import Path
from PySide6.QtCore import Qt, QObject, QTimer, Property, QAbstractListModel, QModelIndex
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
class CustomModel(QAbstractListModel):
def __init__(self, parent=None):
super().__init__(parent)
self._my_items = []
self._role1 = Qt.UserRole + 1
self._role2 = Qt.UserRole + 2
self._roles = {
self._role1: b"role1",
self._role2: b"role2"
}
def append_item(self, arg1, arg2):
row_count = self.rowCount()
self.beginInsertRows(QModelIndex(), row_count, row_count)
self._my_items.append({
self._roles[self._role1]: arg1,
self._roles[self._role2]: arg2
})
self.endInsertRows()
def rowCount(self, parent=QModelIndex()):
"""
Required for subclasses of QAbstractListModels
"""
return len(self._my_items)
def data(self, index, role=Qt.DisplayRole):
"""
Required for subclasses of QAbstractListModels
"""
try:
the_item = self._my_items[index.row()]
except IndexError:
return QVariant()
if role in self._roles:
key = self._roles[role]
return the_item[key]
return QVariant()
def roleNames(self):
return self._roles
class MainContextClass(QObject):
def __init__(self, parent=None):
super().__init__(parent)
self._model = CustomModel()
def add_an_item(self):
thing1 = randint(0, 9)
thing2 = randint(0, 9)
print("Adding {0} and {1}".format(thing1, thing2))
self._model.append_item(thing1, thing2)
@Property(QObject, constant=True)
def custom_model(self):
return self._model
def main():
app = QGuiApplication(sys.argv)
qml_app_engine = QQmlApplicationEngine()
qml_context = qml_app_engine.rootContext()
main_context = MainContextClass(parent=app)
qml_context.setContextProperty("main", main_context)
this_file_path = Path(__file__)
main_qml_path = this_file_path.parent / 'example.qml'
qml_app_engine.load(str(main_qml_path))
timer = QTimer()
timer.setInterval(1000)
timer.setSingleShot(False)
timer.timeout.connect(main_context.add_an_item)
timer.start()
sys.exit(app.exec())
if __name__ == '__main__':
main()
我的期望是,每次计时器超时时,都会将新行添加到列表模型中,因此应该发出列表模型的 dataChanged
信号。 Connections
对象的信号处理程序然后应该打印一条消息。但它似乎永远不会执行:
$ python example.py
qml: Component completed
Adding 7 and 0
Adding 8 and 5
Adding 4 and 0
...
如果我在 endInsertRows()
之后明确添加 self.dataChanged.emit()
,那么显然信号处理程序会执行。但是在我看到的所有文档和示例代码中,都没有这样做。那么,正确的做法是什么?
dataChanged
is emitted (explicitly) when the information of an item changes, when inserting rows then you should listen for the rowsAboutToBeInserted
or rowsInserted
信号:
Connections {
target: main.custom_model
function onRowsAboutToBeInserted(parent, first, last) {
console.log("before", parent, first, last);
}
function onRowsInserted(parent, first, last) {
console.log("after", parent, first, last);
/* print data
let index = main.custom_model.index(first, 0, parent);
let data1 = main.custom_model.data(index, Qt.UserRole + 1);
let data2 = main.custom_model.data(index, Qt.UserRole + 2);
console.log(data1, data2);*/
}
}
另一方面,在 PySide 中没有 QVariant
,相反你必须 return python 个对象,在空 QVariant 的情况下你必须 return None
(或没有相同的东西):
def data(self, index, role=Qt.DisplayRole):
"""
Required for subclasses of QAbstractListModels
"""
try:
the_item = self._my_items[index.row()]
except IndexError:
return
if role in self._roles:
key = self._roles[role]
return the_item[key]
我一直在搞 QAbstractListModel 的子类化,我想我不明白如何正确地将数据添加到模型中。这是我的脚本和 QML:
import QtQuick
import QtQuick.Controls
ApplicationWindow
{
id: mainWindow
visible: true
title: qsTr("Sample Qt Quick application")
width: 400
height: 400
color: "whitesmoke"
Component.onCompleted: console.log("Component completed")
Connections
{
target: main.custom_model
function onDataChanged(topLeft, bottomRight, roles)
{
console.log("Custom model data changed")
}
}
} // ApplicationWindow
import sys
from random import randint
from pathlib import Path
from PySide6.QtCore import Qt, QObject, QTimer, Property, QAbstractListModel, QModelIndex
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
class CustomModel(QAbstractListModel):
def __init__(self, parent=None):
super().__init__(parent)
self._my_items = []
self._role1 = Qt.UserRole + 1
self._role2 = Qt.UserRole + 2
self._roles = {
self._role1: b"role1",
self._role2: b"role2"
}
def append_item(self, arg1, arg2):
row_count = self.rowCount()
self.beginInsertRows(QModelIndex(), row_count, row_count)
self._my_items.append({
self._roles[self._role1]: arg1,
self._roles[self._role2]: arg2
})
self.endInsertRows()
def rowCount(self, parent=QModelIndex()):
"""
Required for subclasses of QAbstractListModels
"""
return len(self._my_items)
def data(self, index, role=Qt.DisplayRole):
"""
Required for subclasses of QAbstractListModels
"""
try:
the_item = self._my_items[index.row()]
except IndexError:
return QVariant()
if role in self._roles:
key = self._roles[role]
return the_item[key]
return QVariant()
def roleNames(self):
return self._roles
class MainContextClass(QObject):
def __init__(self, parent=None):
super().__init__(parent)
self._model = CustomModel()
def add_an_item(self):
thing1 = randint(0, 9)
thing2 = randint(0, 9)
print("Adding {0} and {1}".format(thing1, thing2))
self._model.append_item(thing1, thing2)
@Property(QObject, constant=True)
def custom_model(self):
return self._model
def main():
app = QGuiApplication(sys.argv)
qml_app_engine = QQmlApplicationEngine()
qml_context = qml_app_engine.rootContext()
main_context = MainContextClass(parent=app)
qml_context.setContextProperty("main", main_context)
this_file_path = Path(__file__)
main_qml_path = this_file_path.parent / 'example.qml'
qml_app_engine.load(str(main_qml_path))
timer = QTimer()
timer.setInterval(1000)
timer.setSingleShot(False)
timer.timeout.connect(main_context.add_an_item)
timer.start()
sys.exit(app.exec())
if __name__ == '__main__':
main()
我的期望是,每次计时器超时时,都会将新行添加到列表模型中,因此应该发出列表模型的 dataChanged
信号。 Connections
对象的信号处理程序然后应该打印一条消息。但它似乎永远不会执行:
$ python example.py
qml: Component completed
Adding 7 and 0
Adding 8 and 5
Adding 4 and 0
...
如果我在 endInsertRows()
之后明确添加 self.dataChanged.emit()
,那么显然信号处理程序会执行。但是在我看到的所有文档和示例代码中,都没有这样做。那么,正确的做法是什么?
dataChanged
is emitted (explicitly) when the information of an item changes, when inserting rows then you should listen for the rowsAboutToBeInserted
or rowsInserted
信号:
Connections {
target: main.custom_model
function onRowsAboutToBeInserted(parent, first, last) {
console.log("before", parent, first, last);
}
function onRowsInserted(parent, first, last) {
console.log("after", parent, first, last);
/* print data
let index = main.custom_model.index(first, 0, parent);
let data1 = main.custom_model.data(index, Qt.UserRole + 1);
let data2 = main.custom_model.data(index, Qt.UserRole + 2);
console.log(data1, data2);*/
}
}
另一方面,在 PySide 中没有 QVariant
,相反你必须 return python 个对象,在空 QVariant 的情况下你必须 return None
(或没有相同的东西):
def data(self, index, role=Qt.DisplayRole):
"""
Required for subclasses of QAbstractListModels
"""
try:
the_item = self._my_items[index.row()]
except IndexError:
return
if role in self._roles:
key = self._roles[role]
return the_item[key]