QMetaProperty :: read:无法处理未注册的数据类型'QAbstractListModel *'

时间:2019-08-16 15:21:34

标签: python qml pyside2

我正在尝试创建一个自定义QQuickItem来在我的应用程序中显示图表。我的C ++版本正在运行,但是我的Python版本无法运行。我相信这是因为Q_DECLARE_METATYPEqRegisterMetaType没有移植到PySide2。尽管下面的示例并未对此进行演示,但我的要求是饼图不是静态的(可以随时更改),并且可以在运行时添加或删除任意数量的饼图。

我的程序将运行,但不显示任何内容,并且在控制台上出现以下错误:

QMetaProperty::read: Unable to handle unregistered datatype 'QAbstractListModel*' for property 'CustomPieChart::model'
QMetaProperty::read: Unable to handle unregistered datatype 'QAbstractListModel*' for property 'CustomPieChart::model'
qrc:/PieChartView.qml:24:17: Unable to assign [undefined] to QAbstractItemModel*

我的代码的精简版如下:

main.py

import sys

from PySide2.QtCore import QUrl
from PySide2.QtGui import QIcon
from PySide2.QtQml import QQmlApplicationEngine, qmlRegisterType
from PySide2.QtWidgets import QApplication

from pie_chart import CustomPieChart

import qml_rc  # noqa: F401

# Need to register PieChartModel in here somehow...

def register_quick_items() -> None:
    qmlRegisterType(CustomPieChart, "Custom.CustomPieChart", 1, 0, "CustomPieChart")


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

    register_quick_items()

    engine = QQmlApplicationEngine()

    engine.load(QUrl("qrc:/main.qml"))

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

    sys.exit(app.exec_())

pie_chart_model.py

from typing import Dict

from PySide2.QtCore import QAbstractListModel, QModelIndex, Qt


class PieChartModel(QAbstractListModel):
    _model_data: Dict[str, int]

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

    def rowCount(self, parent=QModelIndex()) -> int:
        return 2

    def columnCount(self, parent=QModelIndex()):
        return len(self._model_data)

    def data(self, index, role=Qt.DisplayRole):
        # Not relevant

    def headerData(self, section, orientation, role):
        # Not relevant

    def reset_with_data(self, model_data):
        self.beginResetModel()
        self._model_data = model_data
        self.endResetModel()

pie_chart.py

from PySide2.QtCore import Property, Signal, Slot
from PySide2.QtQuick import QQuickItem

from pie_chart_model import PieChartModel


class CustomPieChart(QQuickItem):
    model_changed = Signal(PieChartModel)
    _model: PieChartModel

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

    @Property(PieChartModel, notify=model_changed)
    def model(self):
        return self._model

    @Slot(result=None)
    def reset_model(self):
        pie_slices = {
            "A": 1,
            "B": 2,
            "C": 3
        }

        self._model.reset_with_data(pie_slices)
        self.model_changed.emit(self._model)

PieChartView.qml

import QtCharts 2.13
import QtQuick 2.13

import Custom.CustomPieChart 1.0

CustomPieChart {
    id: customPieChart

    Component.onCompleted: {
        customPieChart.reset_model()
    }

    ChartView {
        id: chartView
        anchors.fill: parent
        antialiasing: true
        animationOptions: ChartView.AllAnimations
        legend.visible: false

        PieSeries {
            id: pieSeries

            HPieModelMapper {
                model: customPieChart.model
                labelsRow: 0
                valuesRow: 1
            }
        }
    }
}

main.qml

import QtQuick 2.13
import QtQuick.Controls 2.13

ApplicationWindow {
    visible: true
    width: 500
    height: 500

    PieChartView {
        anchors.fill: parent
    }
}

qml.qrc

<RCC>
    <qresource prefix="/">
        <file>main.qml</file>
        <file>PieChartView.qml</file>
    </qresource>
</RCC>

1 个答案:

答案 0 :(得分:1)

您遇到以下错误:

您遇到以下错误:

  • 在C ++中,不必使用Q_DECLARE_METATYPE或qRegisterMetaType以便可以从QML访问模型,只需在Q_Property中将其注册为QObject,在PySide2中也是如此。

  • 在具有2列和n行的模型中,它不能是QAbstractListModel,因此必须将其更改为QAbstractTableModel。

  • 模型必须是常量属性,因为在您的逻辑中您不能更改它,而只能重置其信息。

  • 虽然我不知道这是否是错误,但是如果您想在ChartView中查看模型的数据,则必须将其与PieSeries关联。

考虑到上述情况,解决方案是:

pie_chart_model.py

from typing import Dict

from PySide2.QtCore import QAbstractTableModel, QModelIndex, Qt


class PieChartModel(QAbstractTableModel):
    _model_data: Dict[str, int]
    def __init__(self, parent=None):
        super().__init__(parent)
        self._model_data = []

    def rowCount(self, parent=QModelIndex()) -> int:
        return 2

    def columnCount(self, parent=QModelIndex()) -> int:
        return len(self._model_data)

    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid():
            return
        r = index.row()
        c = index.column()
        if 0 <= r < self.rowCount() and 0 <= c < self.columnCount():
            if role == Qt.DisplayRole:
                if r == 0:
                    return list(self._model_data.keys())[c]
                elif r == 1:
                    return list(self._model_data.values())[c]

    def reset_with_data(self, model_data):
        self.beginResetModel()
        self._model_data = model_data
        self.endResetModel()

pie_chart.py

from PySide2.QtCore import Property, Slot, QObject
from PySide2.QtQuick import QQuickItem

from pie_chart_model import PieChartModel


class CustomPieChart(QQuickItem):
    _model: PieChartModel

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

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

    @Slot(result=None)
    def reset_model(self):
        pie_slices = {
            "A": 1,
            "B": 2,
            "C": 3
        }

        self._model.reset_with_data(pie_slices)

PieChartView.qml

import QtCharts 2.13
import QtQuick 2.13

import Custom.CustomPieChart 1.0

CustomPieChart {
    id: customPieChart

    Component.onCompleted: {
        customPieChart.reset_model()
    }

    ChartView {
        id: chartView
        anchors.fill: parent
        antialiasing: true
        animationOptions: ChartView.AllAnimations
        legend.visible: false

        PieSeries{
            id: pie_series
        }

        HPieModelMapper {
            series: pie_series
            model: customPieChart.model
            labelsRow: 0
            valuesRow: 1
        }
    }
}