¿Por qué no dataChanged que se emite después de beginInsertRows() y endInsertRows() en una subclase de QAbstractListModel?

0

Pregunta

Yo jugueteaba con la creación de subclases QAbstractListModel, y no creo que entender la forma correcta de agregar datos al modelo. Aquí está mi script y el QML:

import QtQuick
import QtQuick.Controls

ApplicationWindow
{
    id: mainWindow
    visible: true
    title: qsTr("Sample Qt Quick application")
    width: 400
    height: 400
    color: "whitesmoke"

    Component.onCompleted: console.log("Component completed")

    Connections
    {
        target: main.custom_model
        function onDataChanged(topLeft, bottomRight, roles)
        {
            console.log("Custom model data changed")
        }
    }
    
}  // ApplicationWindow

import sys

from random import randint
from pathlib import Path
from PySide6.QtCore import Qt, QObject, QTimer, Property, QAbstractListModel, QModelIndex
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine


class CustomModel(QAbstractListModel):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._my_items = []
        
        self._role1 = Qt.UserRole + 1
        self._role2 = Qt.UserRole + 2
        
        self._roles = {
            self._role1: b"role1",
            self._role2: b"role2"
        }
        
    def append_item(self, arg1, arg2):
        row_count = self.rowCount()
        self.beginInsertRows(QModelIndex(), row_count, row_count)

        self._my_items.append({
            self._roles[self._role1]: arg1,
            self._roles[self._role2]: arg2
        })

        self.endInsertRows()

    def rowCount(self, parent=QModelIndex()):
        """
        Required for subclasses of QAbstractListModels
        """
        return len(self._my_items)

    def data(self, index, role=Qt.DisplayRole):
        """
        Required for subclasses of QAbstractListModels
        """
        try:
            the_item = self._my_items[index.row()]
        except IndexError:
            return QVariant()

        if role in self._roles:
            key = self._roles[role]
            return the_item[key]

        return QVariant()

    def roleNames(self):
        return self._roles


class MainContextClass(QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._model = CustomModel()

    def add_an_item(self):
        thing1 = randint(0, 9)
        thing2 = randint(0, 9)
        print("Adding {0} and {1}".format(thing1, thing2))
        self._model.append_item(thing1, thing2)

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


def main():
    app = QGuiApplication(sys.argv)
    qml_app_engine = QQmlApplicationEngine()
    qml_context = qml_app_engine.rootContext()

    main_context = MainContextClass(parent=app)
    qml_context.setContextProperty("main", main_context)
    
    this_file_path = Path(__file__)
    main_qml_path = this_file_path.parent / 'example.qml'
    qml_app_engine.load(str(main_qml_path))

    timer = QTimer()
    timer.setInterval(1000)
    timer.setSingleShot(False)
    timer.timeout.connect(main_context.add_an_item)
    timer.start()

    sys.exit(app.exec())

    
if __name__ == '__main__':
    main()

Mi expectativa es que, cada vez que el temporizador de tiempo de espera, una nueva fila se añade a la listmodel, y por lo tanto, la listmodel del dataChanged la señal debe conseguir que se emite. El Connections objeto de la señal del controlador debe imprimir un mensaje. Pero parece que nunca se ejecuta:

$ python example.py
qml: Component completed
Adding 7 and 0
Adding 8 and 5
Adding 4 and 0
...

Si puedo agregar explícitamente self.dataChanged.emit() después de endInsertRows(), entonces, obviamente, el manejador se ejecuta. Pero en toda la documentación y código de ejemplo que veo, no lo hace. Así que, ¿cuál es el enfoque adecuado?

pyside6 python python-3.x qml
2021-11-15 04:28:26
1

Mejor respuesta

2

dataChanged se emite (explícitamente) cuando la información de un elemento cambia, cuando la inserción de filas, a continuación, usted debe escuchar para el rowsAboutToBeInserted o rowsInserted señales:

Connections {
    target: main.custom_model

    function onRowsAboutToBeInserted(parent, first, last) {
        console.log("before", parent, first, last);
    }

    function onRowsInserted(parent, first, last) {
        console.log("after", parent, first, last);
        /* print data
        let index = main.custom_model.index(first, 0, parent);
        let data1 = main.custom_model.data(index, Qt.UserRole + 1);
        let data2 = main.custom_model.data(index, Qt.UserRole + 2);
        console.log(data1, data2);*/
    }
}

Por otro lado, en PySide no hay QVarianten lugar usted debe devolver los objetos de python, en el caso de null QVariant debe devolver None (o nada que es el mismo):

def data(self, index, role=Qt.DisplayRole):
    """
    Required for subclasses of QAbstractListModels
    """
    try:
        the_item = self._my_items[index.row()]
    except IndexError:
        return

    if role in self._roles:
        key = self._roles[role]
        return the_item[key]
2021-11-15 04:57:20

En otros idiomas

Esta página está en otros idiomas

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Slovenský
..................................................................................................................