Доступ к данным из динамически создаваемого списка в QML с помощью Python - PullRequest
0 голосов
/ 29 марта 2019

У меня есть список в QML и отображение его в объекте listView.Мне нужно получить доступ к этим данным из Python при нажатии кнопки.В Python я создаю объект QStringListModel и привязываю его к моей listModel в QML, используя setContextProperty.Я вижу, как список создается и отображается в 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 есть данные (динамически создаваемые в пользовательском интерфейсе), список питонов должен автоматически заполняться этими данными?Чего мне не хватает?

1 Ответ

1 голос
/ 02 апреля 2019

Вы предполагаете, что поскольку вы передаете модель через setContextProperty () с тем же именем, что и ListModel, то же самое, нет, и наоборот, это приводит к нестабильной работе вашей программы, поскольку QML перекрывает оба имени. Вместо этого вам нужно создать модель в Python и экспортировать ее, но так как вы также хотите взаимодействовать с QML, лучше экспортировать QObject, который имеет модель как qproperty. Не используйте findChild, findChildren для получения элементов QML в python, вы должны сделать противоположное экспорту элементов из python в QML.

Учитывая вышеизложенное, я реализовал класс Manager, имеющий модель qproperty, в дополнение к слоту, который можно вызывать из QML. Чтобы не усложнять код, я использовал класс QStandardItemModel в качестве основы модели, QStringListModel - модель только для чтения, поэтому она не работает в этом случае.

main.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_())

main.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")
    }
}

Я рекомендую вам прочитать мой другой ответ для более подробного объяснения.

...