pythonqmlpyside6

Using ListModel (Python/Pyside6) in QML


I have a ListView in QML and want to populate it with data from a AbstractListModel that I created in Python.

AbtractListModel.py (I removed methods like rowCount() to keep the example lucid)

class StudentModel(QAbstractListModel):
    def __init__(self)
        super().__init()
        self.studentList = []
        self.studentList.append(Student("Peter", 22)

    def data(self, index: QtCore.QModelIndex, role: int = ...) -> typing.Any:
        if role == QtCore.Qt.DisplayRole:
            return self.studentList[index]
        return None

Student.py

class Student(object):
    name = ""
    age = 0

    def __init__(self, name, age):
        self.name = name
        self.age = age

ListView.qml

ListView {
   model: studentModel
    
   delegate: Rectangle {
       Text{ text: #name }
       Text{ text: #age }
   }
}

How can I access name and age of a student in the delegate to show them where I used "#name" and "#age"?


Solution

  • At a minimum you must implement the rowCount, data and roleNames methods of the QAbstractListModel:

    from __future__ import annotations
    
    import os
    import sys
    import typing
    from dataclasses import dataclass, fields
    from pathlib import Path
    
    from PySide6.QtCore import (
        QAbstractListModel,
        QByteArray,
        QCoreApplication,
        QModelIndex,
        QObject,
        Qt,
        QUrl,
    )
    from PySide6.QtGui import QGuiApplication
    from PySide6.QtQml import QQmlApplicationEngine
    
    
    CURRENT_DIRECTORY = Path(__file__).resolve().parent
    
    
    @dataclass
    class Student:
        name: str = ""
        age: int = 0
    
    
    class StudentModel(QAbstractListModel):
        def __init__(self, parent: QObject | None) -> None:
            super().__init__()
            self._studend_list = []
            self._studend_list.append(Student("Peter", 22))
    
        def data(self, index: QModelIndex, role: int = Qt.DisplayRole) -> typing.Any:
            if 0 <= index.row() < self.rowCount():
                student = self._studend_list[index.row()]
                name = self.roleNames().get(role)
                if name:
                    return getattr(student, name.decode())
    
        def roleNames(self) -> dict[int, QByteArray]:
            d = {}
            for i, field in enumerate(fields(Student)):
                d[Qt.DisplayRole + i] = field.name.encode()
            return d
    
        def rowCount(self, index: QModelIndex = QModelIndex()) -> int:
            return len(self._studend_list)
    
        def add_student(self, student: Student) -> None:
            self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
            self._studend_list.append(student)
            self.endInsertRows()
    
    
    def main() -> None:
        app = QGuiApplication(sys.argv)
    
        engine = QQmlApplicationEngine()
    
        student_model = StudentModel()
        engine.rootContext().setContextProperty("studentModel", student_model)
    
        filename = os.fspath(CURRENT_DIRECTORY / "main.qml")
        url = QUrl.fromLocalFile(filename)
    
        def handle_object_created(obj: QObject | None, obj_url: QUrl) -> None:
            if obj is None and url == obj_url:
                QCoreApplication.exit(-1)
    
        engine.objectCreated.connect(handle_object_created, Qt.QueuedConnection)
        engine.load(url)
    
        student_model.add_student(Student("wileni", 23))
    
        sys.exit(app.exec())
    
    
    if __name__ == "__main__":
        main()
    
    import QtQuick
    import QtQuick.Controls
    
    ApplicationWindow {
        id: root
    
        width: 640
        height: 480
        visible: true
    
        ListView {
            model: studentModel
            anchors.fill: parent
    
            delegate: Row {
                Text {
                    text: model.name
                }
    
                Text {
                    text: model.age
                }
    
            }
    
        }
    
    }