manage QML Repeater from python

suliman

I've been trying to make for example 10 different elements for example buttons and I could do that using the repeater but I've been having trouble with setting the text for each new element.

I'm getting the texts I want to set from a list in Python and I sent them to qml through QStringListModel. The text got to qml from the list as I wanted, but somehow the repeater set's the text of all the elements as the last string in the list given from Python.

I will provide the code later for extra explanation but for now I want to see if someone have idea how I can do it...(the thing is that the code is in another device and I'm waiting for it). Basically what I'm trying to do is, let's say for example, I have a list in python a_list = {buttonName, buttonAge, buttonHeight} and I want to make multiple buttons in qml as the size of the list (in this case 3) and change each text of the buttons i made in the repeater as the strings in the list).

this is main.py

import sys
from PySide2.QtCore import QUrl
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine

from foo import FooController

if __name__ == '__main__':
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()
    fooController = FooController()
    engine.rootContext().setContextProperty("fooController", fooController)

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

    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec_())

This is foo.py

from PySide2.QtCore import QObject, Property, Slot

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

class FooController(QObject):
    def __init__(self, parent=None):
        QObject.__init__(self, parent)

        self.__text = "Foo"
        self.__name = s1.name
        self.__age = s1.age

    @Property(str)
    def text(self):
        return self.__name

    @Slot()
    def clickListener(self):
        print(self.__name)

This is foo.qml

import QtQuick 2.9
import QtQuick.Controls 2.2
Button {
    text: fooController.text
    onClicked: fooController.clickListener()
}

and here is the qml window that contains the repeater

import QtQuick 2.0
import "../components"
//import QtQuick.Timeline 1.0
import QtQuick.Controls 2.15

import QtQuick 2.15
import QtQuick.Window 2.15
import QtGraphicalEffects 1.15
import QtQuick.Layouts 1.15
import "../main.py"

window{
    width: 1500
    height: 920
    minimumWidth: 1100
    minimumHeight: 650
    visible: true
    color: "#00000000"
    id: mainWindow
    title: qsTr("--")
    Rectangle{
            id: rectangle
            anchors.fill: parent
            anchors.rightMargin: 0
            anchors.bottomMargin: 0
            anchors.leftMargin: 0
            anchors.topMargin: 0
            radius: 10
            color: "#4642b6"
            Flickable {
                id: flickable
                contentHeight: gridLayoutBottom.height
                anchors.left: parent.left
                anchors.right: parent.right
                anchors.top: parent.top
                anchors.bottom: parent.bottom
                anchors.topMargin: 96
                anchors.rightMargin: 8
                anchors.leftMargin: 8
                anchors.bottomMargin: 8
                clip: true
                ListModel {
                            id: imageModel

                            ListElement { _id: "tile0" }


                        }
                Repeater {
                        model: imageModel
                        delegate: CustomMenuType{
                            ListView{
                                model: s

                                delegate: Text {
                                    text: model.display
                                    font.family: "Segoe UI"
                                    color: "#ffffff"
                                }
                            }

                            //text: ListView.delegate.Text.text
                            font.pointSize: 9
                            Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter

                        }
               }
            }
    }
}
eyllanesc

Disclaimer: The code you provide is useless since it does not show any attempt to solve your problem, besides that there are undefined elements that I will not take as a basis for my answer, and it is a shame because you always learn more by correcting errors.

If you want to handle the information that the Repeater uses from Python then you must use a model. The Repeater supports 3 types of models:

  • A number,
  • A list or
  • An object that inherits from QAbstractItemModel.

In this case the first does not provide important information since it only indicates the number of elements so it will not show it since it is a trivial example.

In the case of the list the logic is to create a Q_PROPERTY of type "QVariantList" (in PyQt5 list is enough) and that has an associated signal that is emitted every time the list changes so that the view is notified and updates the painting.

import os.path
import sys

from PySide2.QtCore import Property, QObject, QDateTime, QTimer, QUrl, Signal
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine


CURRENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__))


class Controller(QObject):
    modelChanged = Signal()

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

        self._model = ["Text1", "Text2", "Text3"]

    @Property("QVariantList", notify=modelChanged)
    def model(self):
        return self._model

    def update_model(self, l):
        self._model = l[:]
        self.modelChanged.emit()


if __name__ == "__main__":
    app = QGuiApplication(sys.argv)

    controller = Controller()

    engine = QQmlApplicationEngine()
    engine.rootContext().setContextProperty("controller", controller)
    filename = os.path.join(CURRENT_DIRECTORY, "main.qml")
    engine.load(QUrl.fromLocalFile(filename))

    if not engine.rootObjects():
        sys.exit(-1)

    def on_timeout():
        dt = QDateTime.currentDateTime()
        l = [dt.addSecs(i).toString() for i in range(3)]
        controller.update_model(l)

    timer = QTimer(timeout=on_timeout, interval=1000)
    timer.start()

    sys.exit(app.exec_())
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15

Window{
    width: 640
    height: 480
    visible: true

    Column {
        Repeater {
            model: controller.model
            Button {
                text: model.modelData
            }
        }
    }
}

In the case of QAbstractItemModel the logic is to create a QProperty of type QObject and make it constant since the model itself does not change but the information it manages. And on the QML side, the property must be accessed using the associated role, for example in the case of QStringListModel the role is "display":

import os.path
import sys

from PySide2.QtCore import Property, QDateTime, QObject, QStringListModel, QTimer, QUrl
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine


CURRENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__))


class Controller(QObject):
    def __init__(self, parent=None):
        super().__init__(parent)

        self._model = QStringListModel()

        self.model.setStringList(["Text1", "Text2", "Text3"])

    def get_model(self):
        return self._model

    model = Property(QObject, fget=get_model, constant=True)


if __name__ == "__main__":
    app = QGuiApplication(sys.argv)

    controller = Controller()

    engine = QQmlApplicationEngine()
    engine.rootContext().setContextProperty("controller", controller)
    filename = os.path.join(CURRENT_DIRECTORY, "main.qml")
    engine.load(QUrl.fromLocalFile(filename))

    if not engine.rootObjects():
        sys.exit(-1)

    def on_timeout():
        dt = QDateTime.currentDateTime()
        l = [dt.addSecs(i).toString() for i in range(3)]
        controller.model.setStringList(l)

    timer = QTimer(timeout=on_timeout, interval=1000)
    timer.start()

    sys.exit(app.exec_())
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15

Window{
    width: 640
    height: 480
    visible: true

    Column {
        Repeater {
            model: controller.model
            Button {
                text: model.display
            }
        }
    }
}

You can also create a custom model but you have to declare a role associated with the data, a trivial example is to use the QStandardItemModel class.

import os.path
import sys

from PySide2.QtCore import (
    Property,
    QDateTime,
    QObject,
    Qt,
    QTimer,
    QUrl,
)
from PySide2.QtGui import QGuiApplication, QStandardItem, QStandardItemModel
from PySide2.QtQml import QQmlApplicationEngine


CURRENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__))

TEXT_ROLE = Qt.UserRole + 1000
DATA_ROLE = TEXT_ROLE + 1


class Controller(QObject):
    def __init__(self, parent=None):
        super().__init__(parent)

        self._model = QStandardItemModel()
        self.model.setItemRoleNames({TEXT_ROLE: b"text", DATA_ROLE: b"data"})
        for i in range(3):
            item = QStandardItem()
            item.setData("Text{}".format(i), TEXT_ROLE)
            item.setData(i, DATA_ROLE)
            self.model.appendRow(item)

    def get_model(self):
        return self._model

    model = Property(QObject, fget=get_model, constant=True)


if __name__ == "__main__":
    app = QGuiApplication(sys.argv)

    controller = Controller()

    engine = QQmlApplicationEngine()
    engine.rootContext().setContextProperty("controller", controller)
    filename = os.path.join(CURRENT_DIRECTORY, "main.qml")
    engine.load(QUrl.fromLocalFile(filename))

    if not engine.rootObjects():
        sys.exit(-1)

    def on_timeout():
        dt = QDateTime.currentDateTime()
        for i in range(controller.model.rowCount()):
            item = controller.model.item(i)
            item.setData(dt.addSecs(i).toString(), TEXT_ROLE)
            item.setData(dt.addSecs(i), DATA_ROLE)

    timer = QTimer(timeout=on_timeout, interval=1000)
    timer.start()

    sys.exit(app.exec_())
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15

Window{
    width: 640
    height: 480
    visible: true

    Column {
        Repeater {
            model: controller.model
            Button {
                text: model.text
                onClicked: console.log(model.data)
            }
        }
    }
}

You could also create a class that inherits from QAbstractListModel and overrides the data, setData, roleNames, rowCount methods.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

QML,How to dynamicaly change Item of Repeater from C++

From Dev

Interacting with delegated QML Component in Repeater from C++

From Dev

QML,How to dynamicaly change Item of Repeater from C++

From Dev

Signal between QML with Repeater

From Dev

Invoking QML method from Python

From Java

QML Repeater is not drawn correctly after model is changed

From Dev

How to move/animate components created by repeater in QML?

From Dev

QML Repeater for multiple items without a wrapping Item

From Dev

How to use QML Repeater for page indicator?

From Dev

Asynchronous call from QML to Python with callback

From Dev

How to apply Qt Graphical Effect on Delegate of Repeater in QML

From Dev

How to access dynamically/randomly loaded Repeater items in QML?

From Dev

Getting Text from Repeater in Protractor

From Dev

Break in repeater from SQL database

From Dev

No Results from ACF Repeater Field

From Dev

How to manage and communicate with multiple IPython/Jupyter kernels from a Python script?

From Dev

I can't Manage to close from in Python 3 with tkinter

From Dev

python manage.py collectstatic is not copying static files from folder

From Dev

find parent repeater from child repeater's dropdownlist selectedindexchange

From Dev

Access Python QObject from QML fails to convert on second call

From Java

Can't reference existing QML elements from python

From Dev

How to fire a button event from inside a repeater?

From Dev

protractor return array of values from repeater

From Dev

Databind repeater from database with Where statement

From Dev

Protractor: Return Repeater Count from Prototype

From Dev

show multiple data from database using repeater

From Dev

Delete an item from an asp:Repeater using jQuery

From Dev

How to Fill Repeater ItemTemplate from Code Behind?

From Dev

Create ACF repeater field from front end

Related Related

  1. 1

    QML,How to dynamicaly change Item of Repeater from C++

  2. 2

    Interacting with delegated QML Component in Repeater from C++

  3. 3

    QML,How to dynamicaly change Item of Repeater from C++

  4. 4

    Signal between QML with Repeater

  5. 5

    Invoking QML method from Python

  6. 6

    QML Repeater is not drawn correctly after model is changed

  7. 7

    How to move/animate components created by repeater in QML?

  8. 8

    QML Repeater for multiple items without a wrapping Item

  9. 9

    How to use QML Repeater for page indicator?

  10. 10

    Asynchronous call from QML to Python with callback

  11. 11

    How to apply Qt Graphical Effect on Delegate of Repeater in QML

  12. 12

    How to access dynamically/randomly loaded Repeater items in QML?

  13. 13

    Getting Text from Repeater in Protractor

  14. 14

    Break in repeater from SQL database

  15. 15

    No Results from ACF Repeater Field

  16. 16

    How to manage and communicate with multiple IPython/Jupyter kernels from a Python script?

  17. 17

    I can't Manage to close from in Python 3 with tkinter

  18. 18

    python manage.py collectstatic is not copying static files from folder

  19. 19

    find parent repeater from child repeater's dropdownlist selectedindexchange

  20. 20

    Access Python QObject from QML fails to convert on second call

  21. 21

    Can't reference existing QML elements from python

  22. 22

    How to fire a button event from inside a repeater?

  23. 23

    protractor return array of values from repeater

  24. 24

    Databind repeater from database with Where statement

  25. 25

    Protractor: Return Repeater Count from Prototype

  26. 26

    show multiple data from database using repeater

  27. 27

    Delete an item from an asp:Repeater using jQuery

  28. 28

    How to Fill Repeater ItemTemplate from Code Behind?

  29. 29

    Create ACF repeater field from front end

HotTag

Archive