问题
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.
the following is what i have done so far:
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()
}
}
.
.
.
}
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!
回答1:
The error you get is caused by the following line of code:
self.persons = self.persons.append({'name': 'peter', 'age': 22})
It is caused because the append function does not return anything, so it was meant to assign None to self.persons
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):
The same with the rowCount method:
def rowCount(self, parent=QModelIndex()):
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)
}
}
}
Edit:
.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)
来源:https://stackoverflow.com/questions/46814961/how-to-insert-edit-qabstractlistmodel-in-python-and-qml-updates-automatically