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

QMetaProperty::read: Unable to handle unregistered datatype 'QAbstractListModel*'

我正在尝试创建自定义 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>

您有以下错误:

您有以下错误:

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