QML and Python Interoperability

The primary mechanism in which Python and QML communicates is via the model.

References




Model Introduction

Between logic and graphics, there is an intermediate object that "delegate" requests from one to the other. This object is called a model.

app.qml

import QtQuick 2.0

Rectangle {
    width: 300
    height: 300
    color: "brown"

    ListView {
        clip: true

        model: pyModel

        anchors.fill: parent
        anchors.margins: 5

        delegate: Text {
            text: pyLabel
            color: pyColor
        }
    }
}

app.py

import sys

from PyQt5 import QtCore, QtGui, QtQuick


class Model(QtCore.QAbstractListModel):
    def __init__(self, schema, parent=None):
        super(Model, self).__init__(parent)

        # Each item is a dictionary of key/value pairs
        self.items = list()

        # QML requires a model to define upfront
        # exactly which roles it can supply. I refer
        # to this as the models "schema".
        self.schema = schema

    def append(self, item):
        """Append item to end of model"""
        self.beginInsertRows(QtCore.QModelIndex(),
                             self.rowCount(),
                             self.rowCount())

        self.items.append(item)
        self.endInsertRows()

    def data(self, index, role):
        """Return value of item[`index`] of `role`"""
        key = self.schema[role]
        return self.items[index.row()].get(key)

    def setData(self, index, value, role):
        """Set item[`index`] of `role` to `value`"""
        key = self.schema[role]
        self.items[index.row()][key] = value
        self.dataChanged.emit(index, index)

    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.items)

    def roleNames(self):
        """Role names are used by QML to map key to role"""
        return dict(enumerate(self.schema))


app = QtGui.QGuiApplication(sys.argv)

view = QtQuick.QQuickView()

schema = [
    "pyLabel",
    "pyColor",
]

model = Model(schema)

items = [
    {
        "pyLabel": "First Item",
        "pyColor": "white",
    },
    {
        "pyLabel": "Second Item",
        "pyColor": "white",
    }
]

for item in items:
    model.append(item)

engine = view.engine()
context = engine.rootContext()
context.setContextProperty("pyModel", model)

view.setSource(QtCore.QUrl("app.qml"))
view.setResizeMode(view.SizeRootObjectToView)
view.show()

# Appending to the model
QtCore.QTimer.singleShot(2000, lambda: model.append({
    "pyLabel": "Third Item",
    "pyColor": "steelblue"
}))

# Modifying an item in the model
QtCore.QTimer.singleShot(3000, lambda: model.setData(
    model.createIndex(1, 0),  # 1th item, 0th column
    "New pLabel!",
    schema.index("pyLabel"),
))

app.exec_()

results matching ""

    No results matching ""