Reputation: 584
I want to create a simple list of GUI elements arranged in a list using PyQt + QML approach. Elements represent projects with two main properties: name
and state
. Initially the list is filling with some in-code objects, for example from the QT settings storage. The thing is, I want to wrap every entry retrieved from the storage in my class (dedicated module). In pseudo-code:
from my_module import Project
initial_list = [{
name: 'Spaceship',
state: 'In progress'
}, {
name: 'Aircraft',
state: 'Done'
}, {
name: 'Truck',
state: 'Broken'
}]
projects = [Project(p) for p in initial_list]
User can click on the item and perform some tasks with it (thats why I want the element to be a Project
instance).
I understand that I should have a model representing the data but I struggle with the scattered examples I found. Should I have a model of the single element as well as the whole list model? How to encapsulate a Project
instance in a QObject
subclass? My last tries were QAbstractListModel
to use as a model and ListView
to use as a visual representation. Can you provide me a minimal complete example?
Upvotes: 2
Views: 1906
Reputation: 584
Guess I've figured it out by myself. For all who somehow will want to repeat the task: you indeed need to subclass QAbstractItemModel
(QAbstractListModel
) to provide a model for your QML (you don't need to subclass a single item, though I found approaches when it is a valid case).
Python:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtCore import QCoreApplication, QUrl, QAbstractItemModel, pyqtProperty, QAbstractListModel, QModelIndex, \
QObject, QVariant, Qt
from PyQt5.QtGui import QGuiApplication
from PyQt5.QtQuick import QQuickView
import mylib
class ProjectListItem(QObject):
def __init__(self, project: mylib.Project, parent=None):
super().__init__(parent)
self.project = project
@pyqtProperty('QString')
def name(self):
return self.project.path.name
@pyqtProperty('QString')
def state(self):
return str(self.project.state)
class ProjectsList(QAbstractListModel):
def __init__(self, projects: list, parent=None):
super().__init__(parent)
self.projects = projects
def rowCount(self, parent=None, *args, **kwargs):
return len(self.projects)
def data(self, index: QModelIndex, role=None):
# print(index, role)
if role == Qt.DisplayRole:
return self.projects[index.row()]
def addProject(self, project):
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
self.projects.append(project)
self.endInsertRows()
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
projects = ProjectsList([])
projects.addProject(ProjectListItem(mylib.Project('Abc')))
projects.addProject(ProjectListItem(mylib.Project('Def')))
projects.addProject(ProjectListItem(mylib.Project('Ghi')))
view = QQuickView()
view.setResizeMode(QQuickView.SizeRootObjectToView)
view.rootContext().setContextProperty('projectsModel', projects)
view.setSource(QUrl('main.qml'))
sys.exit(app.exec_())
QML:
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("PyQt5 love QML")
color: "whitesmoke"
GridLayout {
columns: 2
rows: 1
ListView {
width: 200; height: 250
model: projectsModel
delegate: Item {
id: projectListItem
width: ListView.view.width
height: 40
Column {
Text { text: '<b>Name:</b> ' + display.name }
Text { text: '<b>State:</b> ' + display.state }
}
MouseArea {
anchors.fill: parent
onClicked: { projectListItem.ListView.view.currentIndex = index; }
}
}
highlight: Rectangle { color: "lightsteelblue"; radius: 5 }
focus: true
}
}
}
So then you'll get something like this:
Upvotes: 3