【问题标题】:QML view wont get update when adding a new row to a QAbstractListModel subclass向 QAbstractListModel 子类添加新行时,QML 视图不会更新
【发布时间】:2021-09-06 06:12:00
【问题描述】:

我在后端有一个列表模型,起初列表是空的,当从设备接收到新数据时,新数据将附加到列表中,我希望视图也会更新,但它没有。

所以我从某个地方获取代码,为这个问题举一个简单的例子,并且有一个添加按钮可以将新行添加到模型中,它工作正常。

我添加了一个 5 秒后触发的计时器,它也会添加一个 now 行,但视图不会更新此行。谁能指出发生了什么?

ma​​in.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)
        }
    }
}

【问题讨论】:

    标签: python pyqt5 qml


    【解决方案1】:

    应用程序运行时,控制台出现如下错误信息:

    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)
    

    【讨论】:

    • 非常感谢,它有效。顺便说一句,我使用 PyCharm,这个 IDE 不会给我任何错误。我想知道你用的是什么IDE?
    猜你喜欢
    • 1970-01-01
    • 2017-02-01
    • 1970-01-01
    • 2014-09-04
    • 1970-01-01
    • 1970-01-01
    • 2016-10-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多