2013-04-25 11 views
6

Ho creato un modello derivato QAbstractListModel basato su un QHash sottostante. Poiché ho bisogno di utilizzare il modello in QML, non posso utilizzare la funzionalità di ordinamento che i widget e le viste di Qt hanno integrato.Ordinare un modello derivato QAbstractListModel in base al ruolo in QML ListView

Ho provato a utilizzare un QSortFilterProxyModel ma non sembra funzionare con il mio modello. Fare in modo che il modello funzioni correttamente in QML non è stato abbastanza noioso, e ora sono bloccato sull'ordinamento.

Qualsiasi suggerimento è gradito.

Qui è la fonte del modello:

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

qui è "cercando" di risolverlo:

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

Ahimè, il proxy funziona come un modello, ma non ordinare le voci .

risposta

1

Prima di tutto, non c'è bisogno di qobject_cast<QAbstractItemModel *> downcasting - il NewModel è una classe derivata del QAbstractItemModel e il principio del polimorfismo dice che è possibile utilizzare una sottoclasse in tutto il mondo in cui una classe genitore è applicabile.

In secondo luogo, il metodo di prepend non usa beginInsertRows e endInsertRows. Questa è una violazione dell'API MVC. Otterrai la corruzione dei dati nelle viste e nei modelli proxy collegati se lo utilizzi in questo modo.

In terzo luogo, non hai menzionato se stai effettivamente utilizzando il modello proxy come modello per la vista allegata :).

Infine, si sta utilizzando QHash come backing store dei dati con QHash::iterator per l'inserimento. Questa è una soluzione itneresting, ma qualcosa che semplicemente non può funzionare: un inserimento o una rimozione può far sì che la tabella hash si accresci o si riduca, il che significa cambiare tutti i dati pubblicati tramite gli indici del modello. Questo non funzionerà. Non utilizzare QHash quando è necessario un ordine stabile. La complessità O(n) del tuo metodo delta deve essere interpretata come un avvertimento; questo è un approccio sbagliato

+0

'prepend()' viene utilizzato per popolare il modello quando non viene utilizzato, non ci sono problemi con l'utilizzo corretto. Ho bisogno di usare QHash per la ricerca, l'ho già fatto usando l'hash per lo storage e poi trasferendo i dati su un altro modello, ma sto cercando modi per riutilizzare i dati originali dall'hash. Il modello così com'è sembra funzionare bene, il mio problema riguarda solo l'ordinamento. – dtech

+1

Prova ad eseguire ModelTest sopra al tuo codice, potresti essere piuttosto sorpreso. –

+0

Viene utilizzato in un contesto strettamente specificato, il pensiero che sia perfetto come parte dell'intera API è molto lontano, mirano solo alla funzionalità di cui ho bisogno in particolare. Forse è per questo che non funziona con il proxy di ordinamento. Sarà più facile fare il mio wrapper proxy di ordinamento piuttosto che far funzionare il magazzino con il modello, mantenendo il contenitore sottostante ottimizzato per la ricerca più veloce, che è il requisito N1. – dtech

6

Se si abilita QSortFilterProxyModel :: setDynamicSortFilter (true), è necessario chiamare la funzione QSortFilterProxyModel :: sort (...) una volta per consentire al proxy di sapere in che modo ordinare.

Con ciò, ogni volta che si aggiorna il modello, il proxy ripartirà tutto automaticamente solo automaticamente.

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

Funziona, grazie! – Sharm

Problemi correlati