按QML中的角色对QAbstractListModel派生模型进行排序ListView

问题描述:

我已经基于基础QHash创建了QAbstractListModel派生模型。由于我需要在QML中使用模型,因此我无法利用Qt窗口小部件和视图集成的排序功能。按QML中的角色对QAbstractListModel派生模型进行排序ListView

我试过使用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); 

唉,代理工作作为一种模式,但它不排序条目。

首先,不需要qobject_cast<QAbstractItemModel *>向下转换 - NewModelQAbstractItemModel的派生类,多态原则表示可以在任何可以使用父类的地方使用子类。

二,您的prepend方法不使用beginInsertRowsendInsertRows。这违反了MVC API。如果以这种方式使用,您将在附加的视图和代理模型中获得数据损坏。

第三,你还没有提到你是否真的使用你的代理模型作为附加视图的模型:)。

最后,您使用QHash作为数据的后备存储,用于插入QHash::iterator。这是一个非常有用的解决方案,但是却无法正常工作 - 插入或删除操作会导致哈希表的增大/缩小,这意味着需要通过模型索引更改所有发布的数据。这不会起作用。当您需要稳定的订单时,请勿使用QHashdelta方法的复杂性应该被解释为警告;这是一个错误的方法。

+0

'prepend()'用于在不使用模型时填充模型,明智地使用它没有问题。我需要使用QHash进行查找,我已经通过使用散列进行存储并将数据传输到另一个模型来完成此操作,但我正在寻找从哈希中重用原始数据的方法。模型,因为它似乎工作正常,我的问题只是与排序。 – dtech 2013-04-25 20:27:51

+1

试着在你的代码上运行ModelTest,你可能会感到很惊讶。 – 2013-04-26 12:38:25

+0

它被用在一个严格规定的上下文中,认为它作为整个API的一部分是完美的,但它只是针对我特别需要的功能。也许这就是为什么它不适用于排序代理。做我自己的排序代理包装比让股票和模型一起工作容易,同时保持底层容器为最快的查找而优化,这是N1的要求。 – dtech 2013-04-26 13:56:17

如果启用QSortFilterProxyModel :: setDynamicSortFilter(true),则需要调用QSortFilterProxyModel :: sort(...)函数一次,以让代理知道哪种方式进行排序。因此,无论何时更新模型,代理服务器都会自动对所有内容进行排序。

proxy.setDynamicSortFilter(true); 
proxy.sort(0); 
+0

这个作品,谢谢! – Sharm 2018-01-20 23:12:25