2
votes

I have got the following scrollview with listview inside:

ScrollView{
    anchors.fill: parent
    ListView{
        id: lvCommitsBranch
        model: git.getCommitsBranch();
        clip: true
        delegate: Rectangle {
            height: 100
            width: parent.width
            Text {
                anchors.left: parent.left
                font.bold: true
                text:model.author
                id:txtName
            }
            Text{
                anchors.left: parent.left
                anchors.top:txtName.bottom
                font.pixelSize: 10
                text:model.email
                id: txtEmail
            }
            Text {
                anchors.left: parent.left
                anchors.top:txtEmail.bottom
                text: model.message + ' ' + model.hash
                id: txtMsg
            }
            MouseArea{
                anchors.fill: parent
                onClicked: {
                    lvCommitsBranch.currentIndex = index;
                    console.log('Msg: ' + model.message);
                    console.log('Hash: ' + model.hash);
                }
                acceptedButtons: Qt.LeftButton | Qt.RightButton
            }
        }
    }
}

The issue is that when I scroll some items disappear (each time randomly and sometimes I have to scroll fast but not always).

enter image description here

When I click on the items that have not disappeared, I get undefined on all the model's properties. When Mousearea's onclick is triggered it prints the following:

qml: Msg: undefined

qml: Hash: undefined

I get the model info from a method (QAbstractListModel) that is returned from my git custom component.

This is my QAbstractListModel:

header:

class CommitsBranch : public QAbstractListModel
{
    Q_OBJECT
public:
    enum Roles {
        AuthorRole,
        EMailRole,
        MsgRole,
        DateRole,
        HashRole
    };
    explicit CommitsBranch(QObject *parent = 0);
    CommitsBranch(Repository *repo);
public:
    virtual int rowCount(const QModelIndex &parent) const override;
    virtual QVariant data(const QModelIndex &index, int role) const override;
protected:
    // return the roles mapping to be used by QML
    virtual QHash<int, QByteArray> roleNames() const override;
private:
    QList<Commit> m_data;
    QHash<int, QByteArray> m_roleNames;

};

Cpp:

CommitsBranch::CommitsBranch(QObject *parent)
    : QAbstractListModel(parent)
{
}

CommitsBranch::CommitsBranch(Repository *repo)
{
    m_roleNames[AuthorRole] = "author";
    m_roleNames[EMailRole] = "email";
    m_roleNames[MsgRole] = "message";
    m_roleNames[DateRole] = "date";
    m_roleNames[HashRole] = "hash";

    /*
    here we append the m_data (QList) Items using libgit2 methods
    */

}

int CommitsBranch::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return m_data.count();
}

QVariant CommitsBranch::data(const QModelIndex &index, int role) const
{    
    // this function returns the required data
}

QHash<int, QByteArray> CommitsBranch::roleNames() const
{
    return m_roleNames;
}

And git is just a class that inherits from QObject and it has the following method:

Q_INVOKABLE QObject* getCommitsBranch();
QObject *Git::getCommitsBranch()
{
    CommitsBranch* files = new CommitsBranch(repo.data());
    return files;
}

I get the same behavior without the scrollview.

EDIT: If I take a repository with a lot of commits (more lines to the listview), even increasing the cacheBuffer won't help, if I scroll a bit fast all the items will disappear.

1
The issue probably stems from the automatic creation and destruction of delegates by the view as you scroll. A dirty quick solution would be to increase the cacheBuffer of the view - that is the amount of pixels to preload. QML is known to sometimes be losing track of its sheep so to speak, check whether scrolling doesn't accidentally delete the actual model items. - dtech
I've tried increasing the cacheBuffer to 100 and the problem still persists. I've changed it again to 1000 and now items do not disappear but sometimes I still get undefined in item's properties (also some weird visual bugs like text moved o.o) - Abdelilah El Aissaoui
Try to wrap an Item around the Rectangle in your delegate. Like delegate: Item { Rectangle { ... } } - DuKes0mE
@DuKes0mE the result is exactly the same, nothing changes :s - Abdelilah El Aissaoui
Hmm, it should avoid your rectangles being deleted. - DuKes0mE

1 Answers

3
votes

The problem here is that, by default, if you return a QObject* it will transfer the ownership to QML.

http://doc.qt.io/qt-5/qtqml-cppintegration-data.html#data-ownership

The exception to this rule is when a QObject is returned from an explicit C++ method call: in this case, the QML engine assumes ownership of the object, unless the ownership of the object has explicitly been set to remain with C++ by invoking QQmlEngine::setObjectOwnership() with QQmlEngine::CppOwnership specified.

You have to set the returned QObject* ownership manually, so it doesn't get destroyed by the QML engine :

QObject *Git::getCommitsBranch()
{
    CommitsBranch* files = new CommitsBranch(repo.data());

    QQmlEngine::setObjectOwnership(files, QQmlEngine::CppOwnership)

    return files;
}

Note that you will have a memory leak as your CommitsBranch object will never be deleted. But at least your QML items should not disappear anymore !

EDIT: As suggested you can do something like this to avoid the memory leak :

// CommitsBranch Constructor
CommitsBranch::CommitsBranch(Repository *repo, QObject *parent) :
    QAbstractListModel(parent) { /*stuff*/ }

QObject *Git::getCommitsBranch()
{
    // Setting ownership is not necessary if you pass the parent to the QAbstractListModel
    CommitsBranch* commits = new CommitsBranch(repo.data(), this);

    return files;
}