Choose how to implement rows hiding in QAbstractItemModel based model
-
I want to create data model (based on QAbstractItemModel) with ability to hide certain items (actually to hide whole rows). I found two ways to implement it but can't choose which one is better.
You can find sample code which illustrates this issue on the bottom of this post. You can just copy and paste it into empty widgets project's main.cpp file, run qmake then compile and it should work. You can choose implementation method using macrodefinition "HIDE_IMPLEMENTATION".- 1st method of implementation: use layoutAboutToBeChanged and layoutChanged signals to (not sure about it) repaint whole view
- 2nd method of implementation: use beginInsertRows/endInsertRows to make rows appear and beginRemoveRows/endRemoveRows to hide rows
Both methods seems to work fine but 1st method causes view (QTreeView) to work with bug. After hiding row QTreeView can return wrong current index (index of hidden item). You can use "Check current index" button (see sample code) to see this. 2nd method of implementation doesn't have this bug.
So actually I have three questions:
- Did I implement 1st method right or wrong? I suspect I did it wrong because of error mentioned above.
- Which method is right? First or second? Both of them? Other method?
- Which method is better?
Thanks in advance.
Sample code (tested with Qt 5.6.1):
#include <QApplication> #include <QWidget> #include <QVBoxLayout> #include <QToolBar> #include <QTreeView> #include <QHeaderView> #include <QLineEdit> // -------------------- // // CHOOSE IMPLEMENTATION: 1 or 2 // Implementation 1: emit layoutChanged // Implementation 2: insert/delete row #define HIDE_IMPLEMENTATION 1 // // -------------------- // Model contains only 3 items. 3rd item can be hidden. class SimpleModel: public QAbstractItemModel { Q_OBJECT public: SimpleModel(): QAbstractItemModel(Q_NULLPTR) { hide = false; } void toggleHide() { #if HIDE_IMPLEMENTATION == 1 emit layoutAboutToBeChanged(); hide = !hide; emit layoutChanged(); #elif HIDE_IMPLEMENTATION == 2 hide = !hide; if (!hide) { beginInsertRows(QModelIndex(), 2, 2); endInsertRows(); } else { beginRemoveRows(QModelIndex(), 2, 2); endRemoveRows(); } #endif } protected: Q_INVOKABLE virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const { if (!parent.isValid()) return createIndex(row, column, Q_NULLPTR); else return QModelIndex(); } Q_INVOKABLE virtual QModelIndex parent(const QModelIndex &child) const { Q_UNUSED(child); return QModelIndex(); } Q_INVOKABLE virtual int rowCount(const QModelIndex &parent = QModelIndex()) const { if (!parent.isValid()) return hide ? 2: 3; else return 0; } Q_INVOKABLE virtual int columnCount(const QModelIndex &parent = QModelIndex()) const { Q_UNUSED(parent); return 1; } Q_INVOKABLE virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const { if (!index.isValid() || role != Qt::DisplayRole) return QVariant(); if (!index.parent().isValid()) { switch(index.row()) { case 0: return QString::number(1); case 1: return QString::number(2); case 2: if (!hide) return QString::number(3); } } return QVariant(); } private: bool hide; }; class Widget: public QWidget { Q_OBJECT public: Widget(): QWidget(Q_NULLPTR) { QVBoxLayout* layout = new QVBoxLayout; setLayout(layout); QToolBar* toolbar = new QToolBar; layout->addWidget(toolbar); QAction* actionHide = toolbar->addAction("Toggle hide"); connect(actionHide, &QAction::triggered, this, &Widget::hideAction); QAction* actionCheck = toolbar->addAction("Check current index"); connect(actionCheck, &QAction::triggered, this, &Widget::checkAction); view = new QTreeView; layout->addWidget(view); model = new SimpleModel; view->setModel(model); view->header()->setHidden(true); message = new QLineEdit; layout->addWidget(message); message->setReadOnly(true); } private slots: void hideAction() { model->toggleHide(); } void checkAction() { message->setText( QString("Current index is (row=%1,column=%2)") .arg(view->currentIndex().row()) .arg(view->currentIndex().column()) ); } private: SimpleModel* model; QTreeView* view; QLineEdit* message; }; #include "main.moc" void main(int argc, char** argv) { QApplication a(argc, argv); Widget w; w.show(); a.exec(); }
-
It's MUCH easier and faster to do it via QSortFilterProxyModel. Just reimplement filterAcceptsRow() to return false for the rows you want to hide.
To force a refresh of the rows to hide you can callinvalidateFilter()