从 python 管理 QML Repeater

manage QML Repeater from python

我一直在尝试制作 10 个不同的元素(例如按钮),我可以使用转发器做到这一点,但我在为每个新元素设置文本时遇到了问题。

我正在从 Python 中的列表中获取要设置的文本,并通过 QStringListModel 将它们发送到 qml。文本按照我的意愿从列表中转到 qml,但不知何故,转发器将所有元素的文本设置为 Python.

给出的列表中的最后一个字符串

我稍后会提供代码以进行额外解释,但现在我想看看是否有人知道我该怎么做...(问题是代码在另一个设备中,我正在等待它)。基本上我想做的是,例如,我在 python a_list = {buttonName, buttonAge, buttonHeight} 中有一个列表,我想在 qml 中制作多个按钮作为列表的大小(在这种情况下 3) 并将我在转发器中制作的按钮的每个文本更改为列表中的字符串)。

这是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_())

这是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)

这是foo.qml

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

这里是包含中继器的 qml window

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

                        }
               }
            }
    }
}

免责声明:您提供的代码是无用的,因为它没有显示任何解决您问题的尝试,此外还有未定义的元素,我不会将其作为我回答的基础,并且它很遗憾,因为您总是通过纠正错误来学习更多。

如果你想处理 Repeater 使用的来自 Python 的信息,那么你必须使用一个模型。中继器支持3种型号:

  • 一个数,
  • 列表或
  • 继承自 QAbstractItemModel 的对象。

在这种情况下,第一个不提供重要信息,因为它仅指示元素的数量,因此不会显示它,因为它是一个微不足道的示例。

在列表的情况下,逻辑是创建一个类型为“QVariantList”的Q_PROPERTY(在 PyQt5 列表中就足够了)并且它有一个关联的信号,每次列表更改时都会发出该信号,以便视图收到通知并更新绘画。

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
            }
        }
    }
}

在 QAbstractItemModel 的情况下,逻辑是创建 QObject 类型的 QProperty 并使其保持不变,因为模型本身不会改变,但它管理的信息不会改变。在 QML 端,必须使用关联的角色访问 属性,例如在 QStringListModel 的情况下,角色是“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
            }
        }
    }
}

您也可以创建自定义模型,但必须声明与数据关联的角色,一个简单的示例是使用 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)
            }
        }
    }
}

您还可以创建一个 class 继承自 QAbstractListModel 并覆盖数据、setData、roleNames、rowCount 方法。