当向 QAbstractListModel 子类添加新行时,QML 视图不会得到更新
QML view wont get update when adding a new row to a QAbstractListModel subclass
我在后端有一个列表模型,起初列表是空的,当从设备接收到新数据时,新数据将附加到列表中,我希望视图也会更新,但它没有。
所以我从某个地方获取了代码,作为这个问题的一个简单示例,并且有一个添加按钮可以将新行添加到模型中,它工作正常。
我添加了一个在 5 秒后触发的计时器,它也会添加一个 now 行,但视图不会更新此行。谁能指出发生了什么事?
main.py
import sys, model2
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQuick import QQuickView
class MainWindow(QQuickView):
def __init__(self, parent=None):
super().__init__(parent)
self.model = model2.PersonModel()
self.rootContext().setContextProperty('PersonModel', self.model)
self.rootContext().setContextProperty('MainWindow', self)
self.setSource(QUrl('test2.qml'))
myApp = QApplication(sys.argv)
ui = MainWindow()
ui.show()
sys.exit(myApp.exec_())
model2.py
from PyQt5.QtCore import QAbstractListModel, Qt, pyqtSignal, pyqtSlot, QModelIndex
from threading import Timer
class PersonModel(QAbstractListModel):
NameRole = Qt.UserRole + 1
AgeRole = Qt.UserRole + 2
personChanged = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
self.persons = [
{'name': 'jon', 'age': 20},
{'name': 'jane', 'age': 25}
]
self.timer = Timer(5, self.foo)
self.timer.start()
def data(self, index, role=Qt.DisplayRole):
row = index.row()
if role == PersonModel.NameRole:
return self.persons[row]["name"]
if role == PersonModel.AgeRole:
return self.persons[row]["age"]
def rowCount(self, parent=QModelIndex()):
return len(self.persons)
def roleNames(self):
return {
PersonModel.NameRole: b'name',
PersonModel.AgeRole: b'age'
}
@pyqtSlot(str, int)
def addPerson(self, name, age):
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
self.persons.append({'name': name, 'age': age})
self.endInsertRows()
def foo(self):
self.addPerson('oops', 19)
@pyqtSlot(int, str, int)
def editPerson(self, row, name, age):
ix = self.index(row, 0)
self.persons[row] = {'name': name, 'age': age}
self.dataChanged.emit(ix, ix, self.roleNames())
@pyqtSlot(int)
def deletePerson(self, row):
self.beginRemoveColumns(QModelIndex(), row, row)
del self.persons[row]
self.endRemoveRows()
test2.qml
import QtQuick 2.6
import QtQuick.Controls 2.2
Rectangle {
anchors.fill: parent
color: "lightgrey"
ListView {
id: listExample
anchors.fill: parent
model: PersonModel
delegate:
Item {
width: 200
height: 60
Row {
Text {
width: 60
text: name + " " + age
horizontalAlignment: Text.AlignHCenter
anchors.verticalCenter: parent.verticalCenter
}
Button{
width: 20
text: "+"
onClicked: PersonModel.editPerson(index, name, age+1)
}
Button{
width: 20
text: "-"
onClicked: PersonModel.editPerson(index, name, age-1)
}
Button{
width: 20
text: "X"
onClicked: PersonModel.deletePerson(index)
}
}
}
}
Button {
width: 50
height: 25
anchors.bottom: parent.bottom
anchors.right: parent.right
text: "add"
onClicked: {
console.log("qml adding")
PersonModel.addPerson("luis", 22)
}
}
}
当应用程序为运行时,在控制台上得到如下错误信息:
QObject::connect: Cannot queue arguments of type 'QQmlChangeSet'
(Make sure 'QQmlChangeSet' is registered using qRegisterMetaType().)
该错误表明您想从另一个线程修改一个非线程安全的对象(如模型)。模型不能也不应该从另一个线程修改,所以使用 threading.Timer
是错误的,在这种情况下你必须使用 QTimer.singleShot()
:
from PyQt5.QtCore import (
QAbstractListModel,
Qt,
pyqtSignal,
pyqtSlot,
QModelIndex,
QTimer,
)
class PersonModel(QAbstractListModel):
NameRole = Qt.UserRole + 1
AgeRole = Qt.UserRole + 2
personChanged = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
self.persons = [{"name": "jon", "age": 20}, {"name": "jane", "age": 25}]
QTimer.singleShot(5000, self.foo)
我在后端有一个列表模型,起初列表是空的,当从设备接收到新数据时,新数据将附加到列表中,我希望视图也会更新,但它没有。
所以我从某个地方获取了代码,作为这个问题的一个简单示例,并且有一个添加按钮可以将新行添加到模型中,它工作正常。
我添加了一个在 5 秒后触发的计时器,它也会添加一个 now 行,但视图不会更新此行。谁能指出发生了什么事?
main.py
import sys, model2
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQuick import QQuickView
class MainWindow(QQuickView):
def __init__(self, parent=None):
super().__init__(parent)
self.model = model2.PersonModel()
self.rootContext().setContextProperty('PersonModel', self.model)
self.rootContext().setContextProperty('MainWindow', self)
self.setSource(QUrl('test2.qml'))
myApp = QApplication(sys.argv)
ui = MainWindow()
ui.show()
sys.exit(myApp.exec_())
model2.py
from PyQt5.QtCore import QAbstractListModel, Qt, pyqtSignal, pyqtSlot, QModelIndex
from threading import Timer
class PersonModel(QAbstractListModel):
NameRole = Qt.UserRole + 1
AgeRole = Qt.UserRole + 2
personChanged = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
self.persons = [
{'name': 'jon', 'age': 20},
{'name': 'jane', 'age': 25}
]
self.timer = Timer(5, self.foo)
self.timer.start()
def data(self, index, role=Qt.DisplayRole):
row = index.row()
if role == PersonModel.NameRole:
return self.persons[row]["name"]
if role == PersonModel.AgeRole:
return self.persons[row]["age"]
def rowCount(self, parent=QModelIndex()):
return len(self.persons)
def roleNames(self):
return {
PersonModel.NameRole: b'name',
PersonModel.AgeRole: b'age'
}
@pyqtSlot(str, int)
def addPerson(self, name, age):
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
self.persons.append({'name': name, 'age': age})
self.endInsertRows()
def foo(self):
self.addPerson('oops', 19)
@pyqtSlot(int, str, int)
def editPerson(self, row, name, age):
ix = self.index(row, 0)
self.persons[row] = {'name': name, 'age': age}
self.dataChanged.emit(ix, ix, self.roleNames())
@pyqtSlot(int)
def deletePerson(self, row):
self.beginRemoveColumns(QModelIndex(), row, row)
del self.persons[row]
self.endRemoveRows()
test2.qml
import QtQuick 2.6
import QtQuick.Controls 2.2
Rectangle {
anchors.fill: parent
color: "lightgrey"
ListView {
id: listExample
anchors.fill: parent
model: PersonModel
delegate:
Item {
width: 200
height: 60
Row {
Text {
width: 60
text: name + " " + age
horizontalAlignment: Text.AlignHCenter
anchors.verticalCenter: parent.verticalCenter
}
Button{
width: 20
text: "+"
onClicked: PersonModel.editPerson(index, name, age+1)
}
Button{
width: 20
text: "-"
onClicked: PersonModel.editPerson(index, name, age-1)
}
Button{
width: 20
text: "X"
onClicked: PersonModel.deletePerson(index)
}
}
}
}
Button {
width: 50
height: 25
anchors.bottom: parent.bottom
anchors.right: parent.right
text: "add"
onClicked: {
console.log("qml adding")
PersonModel.addPerson("luis", 22)
}
}
}
当应用程序为运行时,在控制台上得到如下错误信息:
QObject::connect: Cannot queue arguments of type 'QQmlChangeSet'
(Make sure 'QQmlChangeSet' is registered using qRegisterMetaType().)
该错误表明您想从另一个线程修改一个非线程安全的对象(如模型)。模型不能也不应该从另一个线程修改,所以使用 threading.Timer
是错误的,在这种情况下你必须使用 QTimer.singleShot()
:
from PyQt5.QtCore import (
QAbstractListModel,
Qt,
pyqtSignal,
pyqtSlot,
QModelIndex,
QTimer,
)
class PersonModel(QAbstractListModel):
NameRole = Qt.UserRole + 1
AgeRole = Qt.UserRole + 2
personChanged = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
self.persons = [{"name": "jon", "age": 20}, {"name": "jane", "age": 25}]
QTimer.singleShot(5000, self.foo)