Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort a QAbstractListModel derived model by role in QML ListView

I've created a QAbstractListModel derived model based on an underlying QHash. Since I need to use the model in QML, I cannot make use of the sorting functionality Qt widgets and views have integrated.

I tried using a QSortFilterProxyModel but it doesn't seem to work with my model. Getting the model to properly work in QML wasn't tedious enough, and now I am stuck on sorting.

Any suggestions are appreciated.

Here is the model source:

typedef QHash<QString, uint> Data;

class NewModel : public QAbstractListModel {
    Q_OBJECT
    Q_PROPERTY(int count READ count NOTIFY countChanged)

public:
    NewModel(QObject * parent = 0) : QAbstractListModel(parent) {}

    enum Roles {WordRole = Qt::UserRole, CountRole};

    QHash<int, QByteArray> roleNames() const {
        QHash<int, QByteArray> roles;
        roles[WordRole] = "word";
        roles[CountRole] = "count";
        return roles;
    }

    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const {
        if (index.row() < 0 || index.row() >= m_data.size()) return QVariant();
        Data::const_iterator iter = m_data.constBegin() + index.row();

        switch (role) {
        case WordRole:
            return iter.key();
        case CountRole:
            return iter.value();
        } return QVariant();
    }

    int rowCount(const QModelIndex &parent) const {
        Q_UNUSED(parent)
        return m_data.size();
    }

    int count() const { return m_data.size(); }

public slots:
    void append(const QString &word) {
        bool alreadyThere = m_data.contains(word);
        if (alreadyThere) m_data[word]++;
        else m_data.insert(word, 1);

        Data::const_iterator iter = m_data.find(word);
        uint position = delta(iter);

        if (alreadyThere) {
            QModelIndex index = createIndex(position, 0);
            emit dataChanged(index, index);
        } else {
            beginInsertRows(QModelIndex(), position, position);
            endInsertRows();
            emit countChanged();
        }
    }

    void prepend(const QString &word) {
        if (m_data.contains(word)) m_data[word]++;
        else m_data.insert(word, 1);
    }

signals:
    void countChanged();

private:
    uint delta(Data::const_iterator i) {
        uint d = 0;
        while (i != m_data.constBegin()) { ++d; --i; }
        return d;
    }

    Data m_data;
};

Here is "trying" to sort it:

NewModel model;
QAbstractItemModel * pm = qobject_cast<QAbstractItemModel *>(&model);
QSortFilterProxyModel proxy;
proxy.setSourceModel(pm);
proxy.setSortRole(NewModel::WordRole);
proxy.setDynamicSortFilter(true);

Alas, the proxy works as a model, but it doesn't sort the entries.

like image 764
dtech Avatar asked Apr 25 '13 19:04

dtech


2 Answers

If you enable QSortFilterProxyModel::setDynamicSortFilter(true), you need to call QSortFilterProxyModel::sort(...) function once to let the proxy know which way to sort.

With that, any time the model is updated the proxy will sort everything again just automatically.

proxy.setDynamicSortFilter(true);
proxy.sort(0);
like image 121
Raoul Hecky Avatar answered Sep 29 '22 13:09

Raoul Hecky


First of all, There's no need for qobject_cast<QAbstractItemModel *> downcasting -- the NewModel is a derived class of the QAbstractItemModel and the polymorphism principle says that you can use a subclass everywhere where a parent class is applicable.

Second, your prepend method does not use beginInsertRows and endInsertRows. That's a violation of the MVC API. You'll get data corruption in the attached views and proxy models if you use it this way.

Third, you haven't mentioned whether you're actually using your proxy model as the model for the attached view :).

Finally, you are using QHash as a backing store of your data with QHash::iterator for insertion. That's an itneresting solution, but something which just cannot work -- an insertion or removal can cause the hash table to grow/shrink, which means changing all data you publish via your model indexes. This is just not going to work. Don't use QHash when you need a stable order. The O(n) complexity of your delta method should be interpreted as a warning; this is a wrong approach.

like image 30
Jan Kundrát Avatar answered Sep 29 '22 13:09

Jan Kundrát