首页 > 解决方案 > QMetaProperty::read: 无法处理未注册的数据类型 'QAbstractListModel*'

问题描述

我正在尝试创建一个自定义QQuickItem以在我的应用程序中显示图表。我的 C++ 版本正在运行,但我的 Python 版本无法运行。我相信这是因为Q_DECLARE_METATYPE并且qRegisterMetaType没有移植到 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*

我的代码的精简版本如下:

主文件

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

饼图模型.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()

饼图.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)

饼图视图.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>

标签: pythonqmlpyside2

解决方案


您有以下错误:

您有以下错误:

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

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

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

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

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

饼图模型.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()

饼图.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)

饼图视图.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
        }
    }
}

推荐阅读