2 votes

Le ListView de QtQuick ne parvient pas à prendre possession de l'objet QAbstractItemModel

Basé sur Qt la documentation chaque fois que a QObject Le type de pointeur est transmis du code C++ à QML, par l'intermédiaire d'un Q_INVOKABLE il existe un ensemble de règles qui déterminent qui est responsable de la durée de vie de ce pointeur. Si le pointeur QObject est sans parent, le moteur QML est implicitement responsable de la propriété du pointeur.

Dans mon scénario, je veux que mon interface utilisateur frontale représente un modèle de liste qui est généré/fourni par le code C++ du backend. Je suppose que le pointeur restera en vie tant que le code QML y fera référence. Le code ci-dessous montre le cas de test réduit :

Main.cpp

#include <QAbstractItemModel>
#include <QDebug>
#include <QGuiApplication>
#include <QObject>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QStringListModel>

class MyStringListModel : public QStringListModel
{
    Q_OBJECT

public:

    explicit MyStringListModel(const QStringList &strings, QObject* parent=nullptr) : QStringListModel(strings, parent)
    {
        qDebug() << "Creation";
    }

    virtual ~MyStringListModel() override
    {
        qDebug() << "Destruction";
    }
};

class Backend : public QObject
{
    Q_OBJECT

public:

    Backend(QObject* parent=nullptr) : QObject(parent)
    {

    }

    Q_INVOKABLE QAbstractItemModel* createModel() const
    {
        static const QStringList months = {
            tr("January"),
            tr("February"),
            tr("March"),
            tr("April"),
            tr("May"),
            tr("June"),
            tr("July"),
            tr("August"),
            tr("September"),
            tr("October"),
            tr("November"),
            tr("December"),
        };

        return new MyStringListModel(months);
    }
};

int main(int argc, char* argv[])
{
    QGuiApplication application(argc, argv);

    qmlRegisterType<QAbstractItemModel>();

    Backend backend;

    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("backend", &backend);
    engine.load("qrc:///ui/main.qml");

    return application.exec();
}

#include "main.moc"

Main.qml

import QtQuick 2.10
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.1

ApplicationWindow {
    id: window

    width: 200
    height: 250
    visible: true

    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 10

        ListView {

            Layout.fillWidth: true
            Layout.fillHeight: true

            model: backend.createModel()
            delegate: Text {
                anchors.horizontalCenter: parent.horizontalCenter
                text: model.display
            }
        }

        Button {
            Layout.alignment: Qt.AlignCenter
            text: qsTr("Garbage Collect")
            onClicked: gc()
        }
    }
}

Voici une capture d'écran du programme :

Dès que l'utilisateur clique sur le bouton, le ramasse-miettes s'exécute et détruit le ptr du modèle (la destruction est mise en évidence par la sortie "Création" et "Destruction" dans le stdout).

Je suis curieux de savoir pourquoi le pointeur a été détruit. J'ai remarqué qu'il ne mettait pas en place le paramètre ListView comme son parent, ce qui est tout à fait normal, je pensais que le moteur QML aurait utilisé une forme de pointeur de référence pour essayer de garder une trace de qui détient encore une référence à ce sujet. Existe-t-il un document qui donne un meilleur aperçu de la manière dont le ramassage des ordures et la propriété sont mis en œuvre ?

De même, existe-t-il une meilleure façon de structurer ce code tout en respectant les exigences de transmission d'un fichier QObject retour à QML.

-1voto

eyllanesc Points 79506

Il semble que la destruction soit due au fait que l'objet n'est pas référencé dans QML, par exemple s'il est assigné à une propriété, la fonction collecteur d'ordures ne l'affectera pas :

ApplicationWindow {
    id: window
    width: 200
    height: 250
    visible: true
    property var mymodel: backend.createModel()
    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 10
        ListView {
            Layout.fillWidth: true
            Layout.fillHeight: true
            model: mymodel
            delegate: Text {
                anchors.horizontalCenter: parent.horizontalCenter
                text: display
            }
        }
        Button {
            Layout.alignment: Qt.AlignCenter
            text: qsTr("Garbage Collect")
            onClicked: gc()
        }
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X