Skip to content
Advertisement

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”?

Advertisement

Answer

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
            }

        }

    }

}
User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement