使用 Python 从 QML 中动态创建的列表中访问数据

Posted

技术标签:

【中文标题】使用 Python 从 QML 中动态创建的列表中访问数据【英文标题】:Accessing data from a dynamically created list in QML with Python 【发布时间】:2019-03-29 19:53:57 【问题描述】:

我在 QML 中有一个列表并在 listView 对象中显示它。按下按钮时,我需要从 python 访问这些数据。在 Python 中,我创建了一个 QStringListModel 对象,并使用 setContextProperty 将它绑定到我在 QML 中的 listModel。我可以看到在 QML 中按预期创建和显示列表,但是当我想从 python 访问数据时,列表是空的。代码如下:

QML:

import QtQuick 2.0
import QtQuick.Controls 2.3

Rectangle
    id: root
    width:800
    height:600

    ListView 
        id: listView
        x: 476
        y: 64
        width: 110
        height: 160
        model: myModel
        ListModel 
            id: myModel
            ListElement 
                name: "Grey"
                colorCode: "grey"
            

            ListElement 
                name: "Red"
                colorCode: "red"
            

            ListElement 
                name: "Blue"
                colorCode: "blue"
            

            ListElement 
                name: "Green"
                colorCode: "green"
            
        
        delegate: Item 
            x: 5
            width: 80
            height: 40
            Row 
                id: row1
                Rectangle 
                    width: 40
                    height: 40
                    color: colorCode
                

                Text 
                    text: name
                    anchors.verticalCenter: parent.verticalCenter
                    font.bold: true
                
                spacing: 10
            
        
    


Python:

import sys
from PyQt5.QtCore import QUrl, QStringListModel
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQuick import QQuickView


app = QApplication(sys.argv)
view = QQuickView()
view.setSource(QUrl("main.qml"))

pyList = QStringListModel()
view.rootContext().setContextProperty("myModel",pyList)
print(pyList.stringList())
print(pyList.rowCount())
view.show()
print("Done!")
sys.exit(app.exec_())

我的印象是,当我们使用 python 绑定时,在 python 中创建的对象绑定到 QML 对象。因此,如果 QML 列表有数据(在 UI 中动态创建),python 列表应该自动填充该数据吗?我错过了什么?

【问题讨论】:

从我们这里看到的,你的列表是空的,你在哪里添加你的项目?显示sendChar 以及您的delegate 的实现。开发人员喜欢在可以复制粘贴代码并查看其中有什么问题时提供帮助。 嘿,我已将代码更改为一个简短的示例。我在 qml 中有一个列表,但我无法从 python 访问它的数据。我是 Qml 新手,所以我不确定我在这里缺少什么! 【参考方案1】:

您假设因为您通过 setContextProperty() 传递的模型与 ListModel 具有相同的名称,所以它是相同的,不,相反,它会导致您的程序不稳定,因为 QML 与两个名称重叠。相反,您必须在 python 中创建一个模型并将其导出,但由于您还想与 QML 交互,因此最好导出一个具有该模型的 QObject 作为 qproperty。不要使用 findChild、findChildren 在 python 中获取 QML 元素,必须与将元素从 python 导出到 QML 相反。

考虑到上述情况,除了可以从 QML 调用的插槽之外,我还实现了一个具有 qproperty 模型的 Manager 类。为避免代码复杂化,我使用 QStandardItemModel 类作为模型的基础,QStringListModel 是只读模型,因此不适用于这种情况。

ma​​in.py

from enum import Enum
from PyQt5 import QtCore, QtGui, QtQuick

class ElementRoles:
    NameRole = QtCore.Qt.UserRole + 1000
    ColorRole = QtCore.Qt.UserRole + 1001

class ElementModel(QtGui.QStandardItemModel, ElementRoles):
    QtCore.Q_ENUM(ElementRoles)

    def __init__(self, parent=None):
        super(ElementModel, self).__init__(parent)
        roles = 
            ElementModel.NameRole: b'name',
            ElementModel.ColorRole: b'colorCode'
        
        self.setItemRoleNames(roles)

    @QtCore.pyqtSlot(str, QtGui.QColor)
    def addElement(self, name, color):
        item = QtGui.QStandardItem()
        item.setData(name, ElementModel.NameRole)
        item.setData(color, ElementModel.ColorRole)
        self.appendRow(item)

class Manager(QtCore.QObject):
    def __init__(self, parent=None):
        super(Manager, self).__init__(parent)
        self._model = ElementModel()

    @QtCore.pyqtProperty(QtCore.QObject, constant=True)
    def model(self):
        return self._model

    @QtCore.pyqtSlot()
    def on_clicked(self):
        print("count:", self._model.rowCount())
        for row in range(self._model.rowCount()):
            it = self._model.item(row)
            print("row:", row)
            for role, name in self._model.roleNames().items():
                print("role:", name, "data:", it.data(role))

if __name__ == '__main__':
    import os
    import sys
    app = QtGui.QGuiApplication(sys.argv)
    manager = Manager()
    view = QtQuick.QQuickView()
    file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "main.qml")
    view.rootContext().setContextProperty("manager", manager)
    view.setSource(QtCore.QUrl.fromLocalFile(file))
    view.show()
    sys.exit(app.exec_())

ma​​in.qml

import QtQuick 2.0
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3

Rectangle
    id: root
    width:800
    height:600

    ColumnLayout
        anchors.fill: parent
        ListView 
            id: listView
            Layout.alignment: Qt.AlignLeft
            Layout.fillHeight: true
            model: manager.model
            delegate: Item 
                x: 5
                width: 80
                height: 40
                Row 
                    id: row1
                    Rectangle 
                        width: 40
                        height: 40
                        color: colorCode
                    

                    Text 
                        text: name
                        anchors.verticalCenter: parent.verticalCenter
                        font.bold: true
                    
                    spacing: 10
                
            
        

        Button
            Layout.alignment: Qt.AlignCenter
            text: "click me"
            onClicked: manager.on_clicked()
        
    

    Component.onCompleted:
        manager.model.addElement("Gray", "gray")
        manager.model.addElement("Red", "red")
        manager.model.addElement("Blue", "blue")
        manager.model.addElement("Green", "green")
    

我建议您阅读my another answer 以获得更详细的说明。

【讨论】:

以上是关于使用 Python 从 QML 中动态创建的列表中访问数据的主要内容,如果未能解决你的问题,请参考以下文章

使用 QAbstractListModel 从 python 访问 QML 中的列表元素

类型错误:无法在嵌套列表视图中读取 null 的属性“newdaycalendar”,其中 listmodel 动态创建了 qml

如何正确地将 ComboBox 的模型从 python (pyQt5) 传递给 QML?

QML:从动态 MouseArea 中“窃取”事件

将 qml 信号连接到 Qt

使用 PyQt 将项目动态设置为 QML ListModel