Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I'm trying to create a custom QQuickItem for displaying charts in my application. My C++ version is working, but I cannot get my Python version to work. I believe this is because Q_DECLARE_METATYPE and qRegisterMetaType aren't ported to PySide2. While my example below doesn't demostrate this, my requirements are that the pie chart isn't static (it can change at anytime) and that I can have any number of pie charts that can be added or removed during runtime.

My program will run, but it doesn't display anything and I get the following error on the console:

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*

A stripped down version of my code is as follows:

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>
like image 885
roundtheworld Avatar asked Oct 30 '25 22:10

roundtheworld


1 Answers

You have the following errors:

You have the following errors:

  • In C++ it is not necessary to use Q_DECLARE_METATYPE or qRegisterMetaType so that the models can be accessed from QML, just register it as QObject in Q_Property, the same happens in PySide2.

  • In a model that has 2 columns and n rows, it cannot be a QAbstractListModel, so you must change it to QAbstractTableModel.

  • model must be a constant Property since in your logic you do not change it but only reset its information.

  • Although I do not know if it is an error but if you want to see the data of the model in the ChartView you must associate it with a PieSeries.

Considering the above, the solution is:

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
        }
    }
}
like image 90
eyllanesc Avatar answered Nov 01 '25 13:11

eyllanesc



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!