Important: Please read the Qt Code of Conduct -

Updating list model (QAbstractListModel) and emitting only the proper signals (add/remove rows, dataChanged)

  • Hi All

    Context: chat contact list
    Within our app we've got sort of a specialized chat contact manager and therefore there is a list of contacts with properties such as displayName, availability, avatarImage, etc.
    This data is stored in a sorted QList<OurContactClass*> and is exposed to QML-based UI via QAbstractListModel subclass that own a given list.

    Situation: contact list updates
    Once in a while contact list is updated (somebody became available or maybe new contact was added). Currently we just refresh the whole model, beginResetModel()/endResetModel(). It works, but causes unnecessary UI flickering (whole ListView is updated) and is very inefficient as in 95% of cases it is only one property that is to be changed. In 5% of cases though a contact is moved up/down or maybe added/removed to the list.

    Question: Is there a standard way for updating the model?
    The proper way for updating the contact list is to carefully compare old and new lists, perform rows addition/deletion for added/removed contacts and signal dataChanged for the contacts with the changed data. That's not exactly a rocket science, yet I guess it is quite a standard situation for the models exposing any sort of changeable lists.

    Is there some sort of standard/usual way or component to perform such a QList (or QAbstractListModel) comparison, update it intelligently and emit just the right signals?
    How would you do it?


  • What I would do, is to re-think your use of a QList<OurContactClass*> as your central storage API. Instead, design a store class that exposes the kinds of manipulations that you need (like addContact() and removeContact()), and use those explicit methods to inform your model about the changes. The Qt container classes are not suited for direct use in conjunction with the model view framework, precisely because there is no signalling from them on changes in their items.

    For changes within the items (availability, for instance), I would make sure that the OurContactClass inherits QObject, and it signals those changes. You can then listen for these signals and update the model accordingly.

  • My case: dataChanged when nothing added/removed, otherwise - resetModel
    By now I implemented a good enough simplified version of what I wanted. I figured that since 5% of changes max are going to be addition/removal (maybe even less than 1% will be seen by user). So I just compare sorted source and target lists, if the contact do differ, model is reset, otherwise only dataChanged is signal.

    In general: still intelligent list/listmodel comparison should be useful
    Andre, your proposal seems to rely on the assumption that external source of changes (messenger transport API) is reliable and that my code also doesn't go to unexpected states (imagine double addition if by mistake same signal is connected twice). In my case I don't trust external API much (I prefer refetching everything unless it effects performance) and wouldn't mind extra protection against my own mistakes as well.

    Your proposal probably makes a lot of sense when performance is critical (dozens on thousands of elements), yet for a contact list with a few items I'd really prefer less intelligence and a simple utility: pass it old and new list and it will figure the diff. Sort of like "git add/commit" calculate the diff for the whole file(s) instead of changing/committing each line separately.

    Anyway that is probably too much of a theory for now. I just thought that such a list comparison is quite a common task and I was wondering if there's already a "diff calculator" that can do it.

Log in to reply