问题
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.
回答1:
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);
回答2:
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.
回答3:
Have a Look at https://github.com/oKcerG/SortFilterProxyModel. This project exposes the functionality of QSortFilterProxyModel nicely to QML. I used it in different projects and it junst worked. However if you want to implement your own solution it's something to get your ideas.
来源:https://stackoverflow.com/questions/16222797/sort-a-qabstractlistmodel-derived-model-by-role-in-qml-listview