Сортировка модели, производной от QAbstractListModel, по роли в QML ListView

Я создал модель, производную от QAbstractListModel, на основе базового QHash. Поскольку мне нужно использовать модель в QML, я не могу использовать функции сортировки, интегрированные в виджеты и представления Qt.

Я пробовал использовать QSortFilterProxyModel, но, похоже, он не работает с моей моделью. Заставить модель правильно работать в QML было недостаточно утомительно, и теперь я застрял на сортировке.

Любые предложения приветствуются.

Вот источник модели:

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;
};

Вот "пытаюсь" это разобрать:

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

Увы, прокси работает как модель, но не сортирует записи.


person dtech    schedule 25.04.2013    source источник


Ответы (3)


Если вы включили QSortFilterProxyModel :: setDynamicSortFilter (true), вам нужно вызвать функцию QSortFilterProxyModel :: sort (...) один раз, чтобы позволить прокси-серверу знать, какой путь сортировать.

При этом каждый раз, когда модель обновляется, прокси будет снова сортировать все автоматически.

proxy.setDynamicSortFilter(true);
proxy.sort(0);
person Raoul Hecky    schedule 21.08.2013

Во-первых, нет необходимости qobject_cast<QAbstractItemModel *> понижать значение - NewModel является производным классом от QAbstractItemModel, а принцип полиморфизма говорит, что вы можете использовать подкласс везде, где применим родительский класс.

Во-вторых, ваш prepend метод не использует beginInsertRows и endInsertRows. Это нарушение MVC API. Если вы будете использовать это таким образом, вы получите повреждение данных в прикрепленных представлениях и моделях прокси.

В-третьих, вы не упомянули, действительно ли вы используете свою прокси-модель в качестве модели для прикрепленного представления :).

Наконец, вы используете QHash в качестве резервного хранилища ваших данных с QHash::iterator для вставки. Это интересное решение, но то, что просто не может работать - вставка или удаление может привести к увеличению / уменьшению хэш-таблицы, что означает изменение всех данных, которые вы публикуете через свои модельные индексы. Это просто не сработает. Не используйте QHash, когда вам нужен стабильный заказ. O(n) сложность вашего delta метода следует интерпретировать как предупреждение; это неправильный подход.

person Jan Kundrát    schedule 25.04.2013
comment
prepend() используется для заполнения модели, когда она не используется, нет проблем с ее разумным использованием. Мне нужно использовать QHash для поиска, я уже сделал это, используя хэш для хранения, а затем передав данные в другую модель, но я ищу способы повторно использовать исходные данные из хеша. Модель, как она выглядит, работает нормально, моя проблема только в сортировке. - person dtech; 26.04.2013
comment
Попробуйте запустить ModelTest поверх своего кода, вы можете быть весьма удивлены. - person Jan Kundrát; 26.04.2013
comment
Он используется в строго определенном контексте, мысль о том, что он идеален как часть всего API, далеко удалена, я стремлюсь только к функциональности, которая мне особенно нужна. Может поэтому он не работает с прокси сортировки. Будет проще создать собственную оболочку прокси для сортировки, чем заставить стандартный контейнер работать с моделью, сохраняя при этом базовый контейнер, оптимизированный для максимально быстрого поиска, что является требованием N1. - person dtech; 26.04.2013

Взгляните на https://github.com/oKcerG/SortFilterProxyModel. Этот проект прекрасно раскрывает функциональность QSortFilterProxyModel для QML. Я использовал его в разных проектах, и он сработал. Однако, если вы хотите реализовать собственное решение, это поможет вам реализовать свои идеи.

person Florian Schmidt    schedule 05.06.2019