如何在 python 和 qml 中自动插入/编辑 QAbstractListModel 更新?

2022-01-12 00:00:00 python pyqt pyqt5 qml qabstractlistmodel

问题描述

我正在尝试插入/编辑从 pyqt5 中的 QAbstractListModel 子类化的 python 列表.这个 python 列表是在 qml 中 ListView 元素的 model 属性中读取的.我在 qml 中显示数据没有问题.当我尝试将新数据附加到 python 列表中时出现问题.

i am trying to insert/edit a python list that is subclassed from QAbstractListModel in pyqt5. this python list is read in the model property of ListView element in qml. i have no issues displaying the data in qml. problem arises when i try to append new data into the python list.

以下是我目前所做的:

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

class PersonModel(QAbstractListModel):

    Name = Qt.UserRole + 1
    Age = Qt.UserRole + 2

    personChanged = pyqtSignal()

    def __init__(self, parent=None):
        super().__init__(parent)
        self.persons = [
            {'name': 'jon', 'age': 20},
            {'name': 'jane', 'age': 25}
        ]

    def data(self, QModelIndex, role):
        row = QModelIndex.row()
        if role == self.Name:
            return self.persons[row]["name"]
        if role == self.Age:
            return self.persons[row]["age"]

    def rowCount(self, parent=None):
        return len(self.persons)

    def roleNames(self):
        return {
            Qt.UserRole + 1: b'name',
            Qt.UserRole + 2: b'age'
        }

    @pyqtSlot()
    def addData(self):
        self.beginResetModel()
        self.persons = self.persons.append({'name': 'peter', 'age': 22})
        self.endResetModel()
        print(self.persons)

    @pyqtSlot()
    def editData(self):
        print(self.model.persons)

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: Text {
            text: name + " " + age
        }
    }

    Button {
        width: 50
        height: 25
        anchors.bottom: parent.bottom
        text: "add"
        onClicked: {
            console.log("qml adding")
            PersonModel.addData()
        }
    }

    .
    .
    .
}

当我单击调用 model2.py 中的 addData 方法的添加按钮时发生错误.错误在于 rowCount 并且错误消息说 TypeError: object of type 'NoneType' has no len().我是否必须发出更改或传递一些索引和角色值,以便 qml 知道什么是新/旧并仅相应地反映更改?

error occurs when i click the add button which calls the addData method in model2.py. error lies in the rowCount and error message says TypeError: object of type 'NoneType' has no len(). do i have to emit the changes or pass in some index and role value so qml knows what is new/old and only reflect the changes accordingly?

非常感谢任何形式的指导!

any form of guidance is greatly appreciated!


解决方案

你得到的错误是由下面这行代码引起的:

The error you get is caused by the following line of code:

self.persons = self.persons.append({'name': 'peter', 'age': 22})

这是因为 append 函数没有返回任何东西,所以它是为了将 None 分配给 self.persons

It is caused because the append function does not return anything, so it was meant to assign None to self.persons

要插入新数据,您必须调用 beginInsertRows()endInsertRows() 来通知视图更改.

To insert new data you must call beginInsertRows() and endInsertRows() to notify the view of the change.

数据方法必须与文档中显示的相同,即必须具有以下格式:

the data method must be identical to that shown in the documentation, ie it must have the following format:

def data(self, index, role=Qt.DisplayRole):

与rowCount方法相同:

The same with the rowCount method:

def rowCount(self, parent=QModelIndex()):

我已经实现了 addPerson、editPerson 和 deletePerson 方法,它们分别添加、编辑和删除列表中的数据.我还在 .qml 中添加了必要的项目以便能够对其进行测试.

I have implemented the methods addPerson, editPerson and deletePerson that adds, edits and deletes a data from the list respectively. Also I added the necessary items to the .qml to be able to test it.

model2.py

from PyQt5.QtCore import QAbstractListModel, Qt, pyqtSignal, pyqtSlot, QModelIndex    

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}
        ]

    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()

    @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)
        }
    }
}

.py

@pyqtSlot(int, str, int)
def insertPerson(self, row, name, age):
    self.beginInsertRows(QModelIndex(), row, row)
    self.persons.insert(row, {'name': name, 'age': age})
    self.endInsertRows()

.qml

 PersonModel.insertPerson(2, "luis", 1111)

相关文章