1
votes

I have built a grid of rectangles in QML which is run from Python. engine.load('main.qml')

Window {
    id: channels
    Grid {
        columns: 2
        spacing: 9
        Rectangle {
            color: "#333"
            width: 75
            height: 75
        }
        Rectangle {
            color: "#333"
            width: 75
            height: 75
        }
    }
}

However, I would like to have over fifty rectangles, so I need to be able to dynamically create and update them from python. How can I do that?

1
Have you considered using a Repeater? They are fairly easy to use, and might fit your needs. - Yoann Quenach de Quivillic

1 Answers

2
votes

To provide information from python (or C++) to qml we can read this link. In it recommends to use QAbstractListModel since this notifies the changes to the qml, in addition to be added dynamically we will use Repeater.

main.qml:

import QtQuick.Window 2.2
import QtQuick 2.0
import QtQuick.Controls 1.4

Window {
    visible: true
    id: channels

    Grid {
        columns: 3
        spacing: 9
        Repeater{
            model: myModel
            delegate: Rectangle{
                height: model.height
                width: model.height
                color: model.color
            }
        }
    }
}

.py:

class Data(object):
    def __init__(self, width=35, height=35, color=QColor("red")):
        self._width = width
        self._height = height
        self._color = color

    def width(self):
        return self._width

    def height(self):
        return self._height

    def color(self):
        return self._color

class Model(QAbstractListModel):

    WidthRole = Qt.UserRole + 1
    HeightRole = Qt.UserRole + 2
    ColorRole = Qt.UserRole + 3

    _roles = {WidthRole: b"width", HeightRole: b"height", ColorRole: b"color"}

    def __init__(self, parent=None):
        QAbstractListModel.__init__(self, parent)

        self._datas = []

    def addData(self, data):
        self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
        self._datas.append(data)
        self.endInsertRows()

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

    def data(self, index, role=Qt.DisplayRole):
        try:
            data = self._datas[index.row()]
        except IndexError:
            return QVariant()

        if role == self.WidthRole:
            return data.width()

        if role == self.HeightRole:
            return data.height()

        if role == self.ColorRole:
            return data.color()

        return QVariant()

    def roleNames(self):
        return self._roles

To make a test we use the following code:

main.py

if __name__ == "__main__":
    import sys
    QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()

    model = Model()
    model.addData(Data(44, 33, QColor("red")))
    model.addData(Data(23, 53, QColor("#333")))

    context = engine.rootContext()
    context.setContextProperty('myModel', model)

    engine.load(QUrl.fromLocalFile("main.qml"))

    if len(engine.rootObjects()) == 0:
        sys.exit(-1)

    qsrand(QTime.currentTime().msec())
    timer = QTimer(engine)
    timer.timeout.connect(lambda: model.addData(Data(20 + qrand() % 40, 
                                                     20 + qrand() % 40, 
                                                     QColor(qrand() % 255, qrand() % 255, qrand() % 255))))
    timer.start(1000)

    engine.quit.connect(app.quit)

    sys.exit(app.exec_())

The complete example you find here.