Unsolved QFileSystemModel with filter QDir::Files shows not only files, but also opened subfolders
-
Hello. I'm new with Qt and I'm following video tutorial about QFileSystemModel. So, I'm making file explorer with two views - one is displaying only folders (with treeView), the other one shows only files from the folder, selected in first view.
Everything works fine, but if I choose folder with subfolder, click on subfolder, and then click on parent folder - it will show previously selected subfolder in the list, that is supposed to show files only. Not sure if it's bug or I did something wrong.
BTW, I am on Mac, but somebody already asked about same problem in comments (without answer), not sure about his OS.
Here is the code:
Dialog.hclass Dialog : public QDialog { Q_OBJECT public: explicit Dialog(QWidget *parent = 0); ~Dialog(); private slots: void on_treeView_clicked(const QModelIndex &index); private: Ui::Dialog *ui; QFileSystemModel *dirmodel; QFileSystemModel *filemodel; };
Dialog.cpp
Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog) { ui->setupUi(this); QString sPath = "/users/Max/"; dirmodel = new QFileSystemModel(this); dirmodel->setFilter(QDir::NoDotAndDotDot | QDir::AllDirs); dirmodel->setRootPath(sPath); ui->treeView->setModel(dirmodel); QModelIndex index = dirmodel->index(sPath, 0); ui->treeView->setRootIndex(index); filemodel = new QFileSystemModel(this); filemodel->setFilter(QDir::NoDotAndDotDot | QDir::Files); filemodel->setRootPath(sPath); ui->listView->setModel(filemodel); index = filemodel->index(sPath, 0); ui->listView->setRootIndex(index); } Dialog::~Dialog() { delete ui; } void Dialog::on_treeView_clicked(const QModelIndex &index) { QString sPath = dirmodel->fileInfo(index).absoluteFilePath(); ui->listView->setRootIndex(filemodel->setRootPath(sPath)); }
-
Hi and welcome to devnet,
What version of Qt are you using ?
On what version of macOS ? -
Hello. I'm using Mac OS 10.13.1 and version of Qt is 5.9.2
I also just tested it on Windows 10 x64 (with Qt 5.9.2) and it has the same issue. -
QString sPath = dirmodel->fileInfo(index).absoluteFilePath(); ui->listView->setRootIndex(filemodel->setRootPath(sPath));
I don't actually see where http://doc.qt.io/qt-5/qfilesystemmodel.html#setRootPath states what the
QModelIndex
it returns is. Did you take this code from somewhere?**** EDIT: Hmm, I see it at http://www.bogotobogo.com/Qt/Qt5_QTreeView_QFileSystemModel_ModelView_MVC.php. And someone said on your youtube that it has same problem. Perhaps an expert can say there is a problem in that code?
-
@Maxxii Can you also provide the .ui file for your example ?
-
Sure. But there is nothing more than 1 treeView and 1 listView.
<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>Dialog</class> <widget class="QDialog" name="Dialog"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>597</width> <height>303</height> </rect> </property> <property name="windowTitle"> <string>Dialog</string> </property> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QTreeView" name="treeView"/> </item> <item> <widget class="QListView" name="listView"/> </item> </layout> </widget> <layoutdefault spacing="6" margin="11"/> <resources/> <connections/> </ui>
-
Even if simple, it avoids people trying to help you to have to rewrite it and guess what's in it and thus lose time to re-create a functional application.
So there might be bug here with QFileSystemModel. I'd recommend taking a look at the bug report system to see if it's something known. If not please consider opening a new report providing the complete code sample.
-
@Maxxii
If this does indeed turn out to be a bug, as @SGaist suggests, and is something to do with changing the root path not quite respecting the filter and showing sub-driectories under some circumstances, to work around for now try something like refreshing or even recreating the filemodel in youron_treeView_clicked()
, till it's fixed. -
@SGaist
Ok. I thought I have to paste minimum amount code on forum, so thank you for clarification.
I wanted to report this as bug, but decided to post it here first, just to be sure it's not about my misunderstanding.@JNBarchan
Ok, I will try this, thank you. -
Don't use 2 separate models, use a single model and a few proxys operating on it to manipulate filtering and setting the roots.
Here is a working example:
CMakeLists.txt
cmake_minimum_required(VERSION 3.14) project(MyApp LANGUAGES CXX) include(FetchContent) FetchContent_Declare(QtModelUtilitiesFC GIT_REPOSITORY https://github.com/VSRonin/QtModelUtilities.git GIT_TAG master ) FetchContent_MakeAvailable(QtModelUtilitiesFC) find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Widgets REQUIRED) add_executable(MyApp main.cpp ) target_include_directories(MyApp PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>) target_link_libraries(MyApp PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets QtModelUtilities::QtModelUtilities ) set_target_properties(MyApp PROPERTIES AUTOMOC ON AUTOUIC ON AUTORCC ON CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON )
main.cpp
#include <QApplication> #include <QSortFilterProxyModel> #include <QListView> #include <QTreeView> #include <QDialog> #include <QHBoxLayout> #include <QFileSystemModel> #include <RootIndexProxyModel> class FileDirFilterProxy : public QSortFilterProxyModel{ public: explicit FileDirFilterProxy(bool filterDirs, QObject* parent=nullptr) :QSortFilterProxyModel(parent) ,m_filterDirs(filterDirs) {} protected: bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override{ const QFileSystemModel* fileModel = qobject_cast<const QFileSystemModel*>(sourceModel()); const QAbstractProxyModel* proxy = qobject_cast<const QAbstractProxyModel*>(sourceModel()); while(!fileModel){ if(!proxy) return QSortFilterProxyModel::filterAcceptsRow(source_row,source_parent); fileModel = qobject_cast<const QFileSystemModel*>(proxy->sourceModel()); proxy = qobject_cast<const QAbstractProxyModel*>(proxy->sourceModel()); } return m_filterDirs == fileModel->isDir(sourceModel()->index(source_row,0,source_parent)); } private: const bool m_filterDirs; }; class Dialog : public QDialog { public: explicit Dialog(QWidget *parent = 0); ~Dialog() = default; private slots: void onTreeViewClicked(const QModelIndex &index); private: QFileSystemModel *dirmodel; FileDirFilterProxy* dirProxy; FileDirFilterProxy* fileProxy; RootIndexProxyModel* fileRootProxy; QListView* listView; QTreeView* treeView; }; Dialog::Dialog(QWidget *parent) : QDialog(parent) { treeView = new QTreeView(this); listView = new QListView(this); auto dialogLay=new QHBoxLayout(this); dialogLay->addWidget(treeView); dialogLay->addWidget(listView); dirmodel = new QFileSystemModel(this); dirmodel->setRootPath("C:/Temp"); dirProxy = new FileDirFilterProxy(true,this); dirProxy->setSourceModel(dirmodel); treeView->setModel(dirProxy); fileRootProxy=new RootIndexProxyModel(this); fileRootProxy->setSourceModel(dirmodel); fileProxy = new FileDirFilterProxy(false,this); fileProxy->setSourceModel(fileRootProxy); listView->setModel(fileProxy); connect(treeView,&QTreeView::clicked,this,&Dialog::onTreeViewClicked); } void Dialog::onTreeViewClicked(const QModelIndex &index) { fileRootProxy->setRootIndex(dirProxy->mapToSource(index)); } int main(int argc, char *argv[]) { QApplication app(argc,argv); Dialog wid; wid.show(); return app.exec(); }
-
What is the content of RootIndexProxyModel?
Can you disclose the RootIndexProxyModel code? -
@IknowQT Sure, as you can see from the CMakeLists.txt it's an open source library: https://github.com/VSRonin/QtModelUtilities
At the time of writing it's:header
#ifndef ROOTINDEXPROXY_H #define ROOTINDEXPROXY_H #include <QIdentityProxyModel> class RootIndexProxyModelPrivate; class RootIndexProxyModel : public QIdentityProxyModel { Q_OBJECT Q_PROPERTY(QModelIndex rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged) Q_DISABLE_COPY(RootIndexProxyModel) Q_DECLARE_PRIVATE_D(m_dptr, RootIndexProxyModel) public: explicit RootIndexProxyModel(QObject *parent = Q_NULLPTR); ~RootIndexProxyModel(); QModelIndex rootIndex() const; void setRootIndex(const QModelIndex &root); QModelIndex mapToSource(const QModelIndex &proxyIndex) const override; QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override; void setSourceModel(QAbstractItemModel *sourceModel) override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; bool insertColumns(int column, int count, const QModelIndex &parent = QModelIndex()) override; bool removeColumns(int column, int count, const QModelIndex &parent = QModelIndex()) override; bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; bool hasChildren(const QModelIndex &parent) const override; bool canFetchMore(const QModelIndex &parent) const override; void fetchMore(const QModelIndex &parent) override; bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destinationParent, int destinationChild) override; bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override; Q_SIGNALS: void rootIndexChanged(); protected: RootIndexProxyModel(RootIndexProxyModelPrivate &dptr, QObject *parent); RootIndexProxyModelPrivate *m_dptr; }; #endif // ROOTINDEXPROXY_H
source
#include "rootindexproxymodel.h" #include <functional> #include <QSize> class RootIndexProxyModelPrivate { Q_DECLARE_PUBLIC(RootIndexProxyModel) Q_DISABLE_COPY(RootIndexProxyModelPrivate) RootIndexProxyModelPrivate(RootIndexProxyModel *q); QVector<QMetaObject::Connection> m_sourceConnections; QPersistentModelIndex m_rootIndex; RootIndexProxyModel *q_ptr; bool m_rootRowDeleted; bool m_rootColumnDeleted; QList<QPersistentModelIndex> layoutChangePersistentIndexes; QModelIndexList proxyIndexes; bool isDescendant(QModelIndex childIdx, const QModelIndex &parentIdx) const; void resetRootOnModelChange(); void checkRootRowRemoved(const QModelIndex &parent, int first, int last); void checkRootColumnsRemoved(const QModelIndex &parent, int first, int last); void onRowsAboutToBeInserted(const QModelIndex &parent, int first, int last); void onRowsInserted(const QModelIndex &parent, int first, int last); void onRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last); void onRowsRemoved(const QModelIndex &parent, int first, int last); void onRowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest); void onRowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest); void onColumnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest); void onColumnsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest); void onColumnsAboutToBeInserted(const QModelIndex &parent, int first, int last); void onColumnsInserted(const QModelIndex &parent, int first, int last); void onColumnsAboutToBeRemoved(const QModelIndex &parent, int first, int last); void onColumnsRemoved(const QModelIndex &parent, int first, int last); void onDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles); void onLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint); void onLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint); }; RootIndexProxyModelPrivate::RootIndexProxyModelPrivate(RootIndexProxyModel *q) : q_ptr(q) , m_rootRowDeleted(false) , m_rootColumnDeleted(false) { Q_ASSERT(q_ptr); QObject::connect(q, &RootIndexProxyModel::sourceModelChanged, std::bind(&RootIndexProxyModelPrivate::resetRootOnModelChange, this)); } bool RootIndexProxyModelPrivate::isDescendant(QModelIndex childIdx, const QModelIndex &parentIdx) const { Q_ASSERT(childIdx.isValid()); if (!parentIdx.isValid()) return true; Q_ASSERT(childIdx.model() == parentIdx.model()); for (childIdx = childIdx.parent(); childIdx.isValid(); childIdx = childIdx.parent()) { if (childIdx == parentIdx) return true; } return false; } void RootIndexProxyModelPrivate::resetRootOnModelChange() { m_rootIndex = QModelIndex(); } void RootIndexProxyModelPrivate::checkRootRowRemoved(const QModelIndex &parent, int first, int last) { Q_Q(RootIndexProxyModel); Q_ASSERT(!parent.isValid() || parent.model() == q->sourceModel()); if (!m_rootIndex.isValid()) return; if (!isDescendant(m_rootIndex, parent)) return; if (m_rootIndex.row() >= first && m_rootIndex.row() <= last) { m_rootRowDeleted = true; q->setRootIndex(QModelIndex()); } } void RootIndexProxyModelPrivate::checkRootColumnsRemoved(const QModelIndex &parent, int first, int last) { Q_Q(RootIndexProxyModel); Q_ASSERT(!parent.isValid() || parent.model() == q->sourceModel()); if (!m_rootIndex.isValid()) return; if (!isDescendant(m_rootIndex, parent)) return; if (m_rootIndex.column() >= first && m_rootIndex.column() <= last) { m_rootColumnDeleted = true; q->setRootIndex(QModelIndex()); } } void RootIndexProxyModelPrivate::onRowsAboutToBeInserted(const QModelIndex &parent, int first, int last) { Q_Q(RootIndexProxyModel); Q_ASSERT(!parent.isValid() || parent.model() == q->sourceModel()); if (m_rootIndex.isValid()) { if (isDescendant(m_rootIndex, parent)) return; } q->beginInsertRows(q->mapFromSource(parent), first, last); } void RootIndexProxyModelPrivate::onRowsInserted(const QModelIndex &parent, int first, int last) { Q_UNUSED(first) Q_UNUSED(last) Q_Q(RootIndexProxyModel); Q_ASSERT(!parent.isValid() || parent.model() == q->sourceModel()); if (m_rootIndex.isValid()) { if (isDescendant(m_rootIndex, parent)) return; } q->endInsertRows(); } void RootIndexProxyModelPrivate::onRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last) { Q_Q(RootIndexProxyModel); Q_ASSERT(!parent.isValid() || parent.model() == q->sourceModel()); if (m_rootIndex.isValid()) { if (isDescendant(m_rootIndex, parent)) return; } q->beginRemoveRows(q->mapFromSource(parent), first, last); } void RootIndexProxyModelPrivate::onRowsRemoved(const QModelIndex &parent, int first, int last) { Q_UNUSED(first) Q_UNUSED(last) Q_Q(RootIndexProxyModel); Q_ASSERT(!parent.isValid() || parent.model() == q->sourceModel()); if (m_rootIndex.isValid()) { if (isDescendant(m_rootIndex, parent)) return; } if (m_rootRowDeleted) m_rootRowDeleted = false; else q->endRemoveRows(); } void RootIndexProxyModelPrivate::onRowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest) { Q_Q(RootIndexProxyModel); Q_ASSERT(!sourceParent.isValid() || sourceParent.model() == q->sourceModel()); Q_ASSERT(!destParent.isValid() || destParent.model() == q->sourceModel()); if (isDescendant(m_rootIndex, destParent) == isDescendant(m_rootIndex, sourceParent)) { if (isDescendant(m_rootIndex, destParent)) return; q->beginMoveRows(q->mapFromSource(sourceParent), sourceStart, sourceEnd, q->mapFromSource(destParent), dest); } else if (isDescendant(m_rootIndex, destParent)) q->beginRemoveRows(q->mapFromSource(sourceParent), sourceStart, sourceEnd); else q->beginInsertRows(q->mapFromSource(destParent), dest, dest + sourceEnd - sourceStart); } void RootIndexProxyModelPrivate::onRowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest) { Q_UNUSED(sourceStart) Q_UNUSED(sourceEnd) Q_UNUSED(dest) Q_Q(RootIndexProxyModel); Q_ASSERT(!sourceParent.isValid() || sourceParent.model() == q->sourceModel()); Q_ASSERT(!destParent.isValid() || destParent.model() == q->sourceModel()); if (isDescendant(m_rootIndex, destParent) == isDescendant(m_rootIndex, sourceParent)) { if (isDescendant(m_rootIndex, destParent)) return; q->endMoveRows(); } else if (isDescendant(m_rootIndex, destParent)) q->endRemoveRows(); else q->endInsertRows(); } void RootIndexProxyModelPrivate::onColumnsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest) { Q_UNUSED(sourceStart) Q_UNUSED(sourceEnd) Q_UNUSED(dest) Q_Q(RootIndexProxyModel); Q_ASSERT(!sourceParent.isValid() || sourceParent.model() == q->sourceModel()); Q_ASSERT(!destParent.isValid() || destParent.model() == q->sourceModel()); if (isDescendant(m_rootIndex, destParent) == isDescendant(m_rootIndex, sourceParent)) { if (isDescendant(m_rootIndex, destParent)) return; q->endMoveColumns(); } else if (isDescendant(m_rootIndex, destParent)) q->endRemoveColumns(); else q->endInsertColumns(); } void RootIndexProxyModelPrivate::onColumnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest) { Q_Q(RootIndexProxyModel); Q_ASSERT(!sourceParent.isValid() || sourceParent.model() == q->sourceModel()); Q_ASSERT(!destParent.isValid() || destParent.model() == q->sourceModel()); if (isDescendant(m_rootIndex, destParent) == isDescendant(m_rootIndex, sourceParent)) { if (isDescendant(m_rootIndex, destParent)) return; q->beginMoveColumns(q->mapFromSource(sourceParent), sourceStart, sourceEnd, q->mapFromSource(destParent), dest); } else if (isDescendant(m_rootIndex, destParent)) q->beginRemoveColumns(q->mapFromSource(sourceParent), sourceStart, sourceEnd); else q->beginInsertColumns(q->mapFromSource(destParent), dest, dest + sourceEnd - sourceStart); } void RootIndexProxyModelPrivate::onColumnsAboutToBeInserted(const QModelIndex &parent, int first, int last) { Q_Q(RootIndexProxyModel); Q_ASSERT(!parent.isValid() || parent.model() == q->sourceModel()); if (m_rootIndex.isValid()) { if (isDescendant(m_rootIndex, parent)) return; } q->beginInsertColumns(q->mapFromSource(parent), first, last); } void RootIndexProxyModelPrivate::onColumnsInserted(const QModelIndex &parent, int first, int last) { Q_UNUSED(first) Q_UNUSED(last) Q_Q(RootIndexProxyModel); Q_ASSERT(!parent.isValid() || parent.model() == q->sourceModel()); if (m_rootIndex.isValid()) { if (isDescendant(m_rootIndex, parent)) return; } q->endInsertColumns(); } void RootIndexProxyModelPrivate::onColumnsAboutToBeRemoved(const QModelIndex &parent, int first, int last) { Q_Q(RootIndexProxyModel); Q_ASSERT(!parent.isValid() || parent.model() == q->sourceModel()); if (m_rootIndex.isValid()) { if (isDescendant(m_rootIndex, parent)) return; } q->beginRemoveColumns(q->mapFromSource(parent), first, last); } void RootIndexProxyModelPrivate::onColumnsRemoved(const QModelIndex &parent, int first, int last) { Q_UNUSED(first) Q_UNUSED(last) Q_Q(RootIndexProxyModel); Q_ASSERT(!parent.isValid() || parent.model() == q->sourceModel()); if (m_rootIndex.isValid()) { if (isDescendant(m_rootIndex, parent)) return; } if (m_rootColumnDeleted) m_rootColumnDeleted = false; else q->endRemoveColumns(); } void RootIndexProxyModelPrivate::onDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) { Q_Q(RootIndexProxyModel); Q_ASSERT(topLeft.isValid()); Q_ASSERT(bottomRight.isValid()); Q_ASSERT(topLeft.model() == q->sourceModel()); Q_ASSERT(bottomRight.model() == q->sourceModel()); Q_ASSERT(bottomRight.parent() == topLeft.parent()); if (m_rootIndex.isValid()) { if (isDescendant(m_rootIndex, topLeft.parent())) return; } q->dataChanged(q->mapFromSource(topLeft), q->mapFromSource(bottomRight), roles); } void RootIndexProxyModelPrivate::onLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint) { Q_Q(RootIndexProxyModel); QList<QPersistentModelIndex> parents; parents.reserve(sourceParents.size()); bool addedInvalidIndex = false; for (const QPersistentModelIndex &parent : sourceParents) { if (!parent.isValid() || isDescendant(m_rootIndex, parent)) { if (!addedInvalidIndex) { addedInvalidIndex = true; parents << QPersistentModelIndex(); } continue; } const QModelIndex mappedParent = q->mapFromSource(parent); Q_ASSERT(mappedParent.isValid()); parents << mappedParent; } q->layoutAboutToBeChanged(parents, hint); const auto proxyPersistentIndexes = q->persistentIndexList(); for (const QModelIndex &proxyPersistentIndex : proxyPersistentIndexes) { proxyIndexes << proxyPersistentIndex; Q_ASSERT(proxyPersistentIndex.isValid()); const QPersistentModelIndex srcPersistentIndex = q->mapToSource(proxyPersistentIndex); Q_ASSERT(srcPersistentIndex.isValid()); layoutChangePersistentIndexes << srcPersistentIndex; } } void RootIndexProxyModelPrivate::onLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint) { Q_Q(RootIndexProxyModel); for (int i = 0; i < proxyIndexes.size(); ++i) q->changePersistentIndex(proxyIndexes.at(i), q->mapFromSource(layoutChangePersistentIndexes.at(i))); layoutChangePersistentIndexes.clear(); proxyIndexes.clear(); QList<QPersistentModelIndex> parents; parents.reserve(sourceParents.size()); bool addedInvalidIndex = false; for (const QPersistentModelIndex &parent : sourceParents) { if (!parent.isValid() || isDescendant(m_rootIndex, parent)) { if (!addedInvalidIndex) { addedInvalidIndex = true; parents << QPersistentModelIndex(); } continue; } const QModelIndex mappedParent = q->mapFromSource(parent); Q_ASSERT(mappedParent.isValid()); parents << mappedParent; } q->layoutChanged(parents, hint); } /*! Constructs a new proxy model with the given \a parent. */ RootIndexProxyModel::RootIndexProxyModel(QObject *parent) : QIdentityProxyModel(parent) , m_dptr(new RootIndexProxyModelPrivate(this)) { } /*! \internal */ RootIndexProxyModel::RootIndexProxyModel(RootIndexProxyModelPrivate &dptr, QObject *parent) : QIdentityProxyModel(parent) , m_dptr(&dptr) { } /*! Destructor */ RootIndexProxyModel::~RootIndexProxyModel() { Q_D(RootIndexProxyModel); for (auto discIter = d->m_sourceConnections.cbegin(); discIter != d->m_sourceConnections.cend(); ++discIter) QObject::disconnect(*discIter); delete m_dptr; } /*! \property RootIndexProxyModel::rootIndex \accessors %rootIndex(), setRootIndex() \notifier rootIndexChanged() \brief This property holds the root index of the model \details The root index is an index of the source model used as root by all elements of the proxy model */ //! Getter for rootIndex property QModelIndex RootIndexProxyModel::rootIndex() const { Q_D(const RootIndexProxyModel); return d->m_rootIndex; } //! Setter for rootIndex property void RootIndexProxyModel::setRootIndex(const QModelIndex &root) { Q_ASSERT_X(!root.isValid() || root.model() == sourceModel(), "RootIndexProxyModel::setRootIndex", "The root index must be an index of the source model"); Q_D(RootIndexProxyModel); if (d->m_rootIndex == root) return; beginResetModel(); d->m_rootIndex = root; endResetModel(); rootIndexChanged(); } /*! \reimp */ void RootIndexProxyModel::setSourceModel(QAbstractItemModel *sourceModel) { Q_D(RootIndexProxyModel); for (auto discIter = d->m_sourceConnections.cbegin(); discIter != d->m_sourceConnections.cend(); ++discIter) QObject::disconnect(*discIter); d->m_sourceConnections.clear(); QIdentityProxyModel::setSourceModel(sourceModel); if (sourceModel) { Q_ASSUME(disconnect(sourceModel, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)), this, SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex, int, int)))); Q_ASSUME(disconnect(sourceModel, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(_q_sourceRowsInserted(QModelIndex, int, int)))); Q_ASSUME(disconnect(sourceModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), this, SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex, int, int)))); Q_ASSUME(disconnect(sourceModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(_q_sourceRowsRemoved(QModelIndex, int, int)))); Q_ASSUME(disconnect(sourceModel, SIGNAL(columnsAboutToBeInserted(QModelIndex, int, int)), this, SLOT(_q_sourceColumnsAboutToBeInserted(QModelIndex, int, int)))); Q_ASSUME( disconnect(sourceModel, SIGNAL(columnsInserted(QModelIndex, int, int)), this, SLOT(_q_sourceColumnsInserted(QModelIndex, int, int)))); Q_ASSUME(disconnect(sourceModel, SIGNAL(columnsAboutToBeRemoved(QModelIndex, int, int)), this, SLOT(_q_sourceColumnsAboutToBeRemoved(QModelIndex, int, int)))); Q_ASSUME(disconnect(sourceModel, SIGNAL(columnsRemoved(QModelIndex, int, int)), this, SLOT(_q_sourceColumnsRemoved(QModelIndex, int, int)))); Q_ASSUME(disconnect(sourceModel, SIGNAL(dataChanged(QModelIndex, QModelIndex, QVector<int>)), this, SLOT(_q_sourceDataChanged(QModelIndex, QModelIndex, QVector<int>)))); Q_ASSUME(disconnect(sourceModel, SIGNAL(columnsAboutToBeMoved(QModelIndex, int, int, QModelIndex, int)), this, SLOT(_q_sourceColumnsAboutToBeMoved(QModelIndex, int, int, QModelIndex, int)))); Q_ASSUME(disconnect(sourceModel, SIGNAL(columnsMoved(QModelIndex, int, int, QModelIndex, int)), this, SLOT(_q_sourceColumnsMoved(QModelIndex, int, int, QModelIndex, int)))); Q_ASSUME(disconnect(sourceModel, SIGNAL(rowsAboutToBeMoved(QModelIndex, int, int, QModelIndex, int)), this, SLOT(_q_sourceRowsAboutToBeMoved(QModelIndex, int, int, QModelIndex, int)))); Q_ASSUME(disconnect(sourceModel, SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)), this, SLOT(_q_sourceRowsMoved(QModelIndex, int, int, QModelIndex, int)))); Q_ASSUME(disconnect(sourceModel, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)), this, SLOT(_q_sourceLayoutAboutToBeChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)))); Q_ASSUME(disconnect(sourceModel, SIGNAL(layoutChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)), this, SLOT(_q_sourceLayoutChanged(QList<QPersistentModelIndex>, QAbstractItemModel::LayoutChangeHint)))); using namespace std::placeholders; d->m_sourceConnections << connect(sourceModel, &QAbstractItemModel::rowsAboutToBeRemoved, std::bind(&RootIndexProxyModelPrivate::onRowsAboutToBeRemoved, d, _1, _2, _3)) << connect(sourceModel, &QAbstractItemModel::columnsAboutToBeRemoved, std::bind(&RootIndexProxyModelPrivate::onColumnsAboutToBeRemoved, d, _1, _2, _3)) << connect(sourceModel, &QAbstractItemModel::rowsRemoved, std::bind(&RootIndexProxyModelPrivate::onRowsRemoved, d, _1, _2, _3)) << connect(sourceModel, &QAbstractItemModel::columnsRemoved, std::bind(&RootIndexProxyModelPrivate::onColumnsRemoved, d, _1, _2, _3)) << connect(sourceModel, &QAbstractItemModel::rowsAboutToBeInserted, std::bind(&RootIndexProxyModelPrivate::onRowsAboutToBeInserted, d, _1, _2, _3)) << connect(sourceModel, &QAbstractItemModel::columnsAboutToBeInserted, std::bind(&RootIndexProxyModelPrivate::onColumnsAboutToBeInserted, d, _1, _2, _3)) << connect(sourceModel, &QAbstractItemModel::rowsInserted, std::bind(&RootIndexProxyModelPrivate::onRowsInserted, d, _1, _2, _3)) << connect(sourceModel, &QAbstractItemModel::columnsInserted, std::bind(&RootIndexProxyModelPrivate::onColumnsInserted, d, _1, _2, _3)) << connect(sourceModel, &QAbstractItemModel::dataChanged, std::bind(&RootIndexProxyModelPrivate::onDataChanged, d, _1, _2, _3)) << connect(sourceModel, &QAbstractItemModel::rowsAboutToBeMoved, std::bind(&RootIndexProxyModelPrivate::onRowsAboutToBeMoved, d, _1, _2, _3, _4, _5)) << connect(sourceModel, &QAbstractItemModel::columnsAboutToBeMoved, std::bind(&RootIndexProxyModelPrivate::onColumnsAboutToBeMoved, d, _1, _2, _3, _4, _5)) << connect(sourceModel, &QAbstractItemModel::rowsMoved, std::bind(&RootIndexProxyModelPrivate::onRowsMoved, d, _1, _2, _3, _4, _5)) << connect(sourceModel, &QAbstractItemModel::columnsMoved, std::bind(&RootIndexProxyModelPrivate::onColumnsMoved, d, _1, _2, _3, _4, _5)) << connect(sourceModel, &QAbstractItemModel::layoutAboutToBeChanged, std::bind(&RootIndexProxyModelPrivate::onLayoutAboutToBeChanged, d, _1, _2)) << connect(sourceModel, &QAbstractItemModel::layoutChanged, std::bind(&RootIndexProxyModelPrivate::onLayoutChanged, d, _1, _2)) << connect(sourceModel, &QAbstractItemModel::rowsAboutToBeRemoved, std::bind(&RootIndexProxyModelPrivate::checkRootRowRemoved, d, _1, _2, _3)) << connect(sourceModel, &QAbstractItemModel::columnsAboutToBeRemoved, std::bind(&RootIndexProxyModelPrivate::checkRootColumnsRemoved, d, _1, _2, _3)); } } /*! \reimp */ QModelIndex RootIndexProxyModel::mapToSource(const QModelIndex &proxyIndex) const { if (!sourceModel()) return QModelIndex(); return QIdentityProxyModel::mapToSource(proxyIndex); } /*! \reimp */ QModelIndex RootIndexProxyModel::mapFromSource(const QModelIndex &sourceIndex) const { if (!sourceModel()) return QModelIndex(); if (!sourceIndex.isValid()) return QModelIndex(); Q_D(const RootIndexProxyModel); if (d->m_rootIndex.isValid()) { if (!d->isDescendant(sourceIndex, d->m_rootIndex)) return QModelIndex(); } return QIdentityProxyModel::mapFromSource(sourceIndex); } /*! \reimp */ QModelIndex RootIndexProxyModel::index(int row, int column, const QModelIndex &parent) const { Q_ASSERT(!parent.isValid() || parent.model() == this); if (!sourceModel()) return QModelIndex(); Q_D(const RootIndexProxyModel); QModelIndex sourceParent; if (parent.isValid()) sourceParent = mapToSource(parent); else sourceParent = d->m_rootIndex; const QModelIndex sourceIndex = sourceModel()->index(row, column, sourceParent); return mapFromSource(sourceIndex); } /*! \reimp */ int RootIndexProxyModel::columnCount(const QModelIndex &parent) const { Q_ASSERT(!parent.isValid() || parent.model() == this); if (!sourceModel()) return 0; Q_D(const RootIndexProxyModel); QModelIndex sourceParent; if (parent.isValid()) sourceParent = mapToSource(parent); else sourceParent = d->m_rootIndex; return sourceModel()->columnCount(sourceParent); } /*! \reimp */ int RootIndexProxyModel::rowCount(const QModelIndex &parent) const { Q_ASSERT(!parent.isValid() || parent.model() == this); if (!sourceModel()) return 0; Q_D(const RootIndexProxyModel); QModelIndex sourceParent; if (parent.isValid()) sourceParent = mapToSource(parent); else sourceParent = d->m_rootIndex; return sourceModel()->rowCount(sourceParent); } /*! \reimp */ bool RootIndexProxyModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { Q_ASSERT(!parent.isValid() || parent.model() == this); if (!sourceModel()) return 0; Q_D(const RootIndexProxyModel); QModelIndex sourceParent; if (parent.isValid()) sourceParent = mapToSource(parent); else sourceParent = d->m_rootIndex; return sourceModel()->dropMimeData(data, action, row, column, sourceParent); } /*! \reimp */ bool RootIndexProxyModel::insertColumns(int column, int count, const QModelIndex &parent) { Q_ASSERT(!parent.isValid() || parent.model() == this); if (!sourceModel()) return 0; Q_D(RootIndexProxyModel); QModelIndex sourceParent; if (parent.isValid()) sourceParent = mapToSource(parent); else sourceParent = d->m_rootIndex; return sourceModel()->insertColumns(column, count, sourceParent); } /*! \reimp */ bool RootIndexProxyModel::insertRows(int row, int count, const QModelIndex &parent) { Q_ASSERT(!parent.isValid() || parent.model() == this); if (!sourceModel()) return 0; Q_D(RootIndexProxyModel); QModelIndex sourceParent; if (parent.isValid()) sourceParent = mapToSource(parent); else sourceParent = d->m_rootIndex; return sourceModel()->insertRows(row, count, sourceParent); } /*! \reimp */ bool RootIndexProxyModel::removeColumns(int column, int count, const QModelIndex &parent) { Q_ASSERT(!parent.isValid() || parent.model() == this); if (!sourceModel()) return false; Q_D(RootIndexProxyModel); QModelIndex sourceParent; if (parent.isValid()) sourceParent = mapToSource(parent); else sourceParent = d->m_rootIndex; return sourceModel()->removeColumns(column, count, sourceParent); } /*! \reimp */ bool RootIndexProxyModel::removeRows(int row, int count, const QModelIndex &parent) { Q_ASSERT(!parent.isValid() || parent.model() == this); if (!sourceModel()) return false; Q_D(RootIndexProxyModel); QModelIndex sourceParent; if (parent.isValid()) sourceParent = mapToSource(parent); else sourceParent = d->m_rootIndex; return sourceModel()->removeRows(row, count, sourceParent); } /*! \reimp */ bool RootIndexProxyModel::hasChildren(const QModelIndex &parent) const { Q_ASSERT(!parent.isValid() || parent.model() == this); if (!sourceModel()) return false; Q_D(const RootIndexProxyModel); QModelIndex sourceParent; if (parent.isValid()) sourceParent = mapToSource(parent); else sourceParent = d->m_rootIndex; return sourceModel()->hasChildren(sourceParent); } /*! \reimp */ bool RootIndexProxyModel::canFetchMore(const QModelIndex &parent) const { Q_ASSERT(!parent.isValid() || parent.model() == this); if (!sourceModel()) return false; Q_D(const RootIndexProxyModel); QModelIndex sourceParent; if (parent.isValid()) sourceParent = mapToSource(parent); else sourceParent = d->m_rootIndex; return sourceModel()->canFetchMore(sourceParent); } /*! \reimp */ void RootIndexProxyModel::fetchMore(const QModelIndex &parent) { Q_ASSERT(!parent.isValid() || parent.model() == this); if (!sourceModel()) return; Q_D(RootIndexProxyModel); QModelIndex sourceParent; if (parent.isValid()) sourceParent = mapToSource(parent); else sourceParent = d->m_rootIndex; sourceModel()->fetchMore(sourceParent); } /*! \reimp */ bool RootIndexProxyModel::moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destinationParent, int destinationChild) { Q_ASSERT(!sourceParent.isValid() || sourceParent.model() == this); Q_ASSERT(!destinationParent.isValid() || destinationParent.model() == this); if (!sourceModel()) return false; Q_D(RootIndexProxyModel); QModelIndex srcParent; if (sourceParent.isValid()) srcParent = mapToSource(sourceParent); else srcParent = d->m_rootIndex; QModelIndex destParent; if (destinationParent.isValid()) destParent = mapToSource(destinationParent); else destParent = d->m_rootIndex; return sourceModel()->moveColumns(srcParent, sourceColumn, count, destParent, destinationChild); } /*! \reimp */ bool RootIndexProxyModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) { Q_ASSERT(!sourceParent.isValid() || sourceParent.model() == this); Q_ASSERT(!destinationParent.isValid() || destinationParent.model() == this); if (!sourceModel()) return false; Q_D(RootIndexProxyModel); QModelIndex srcParent; if (sourceParent.isValid()) srcParent = mapToSource(sourceParent); else srcParent = d->m_rootIndex; QModelIndex destParent; if (destinationParent.isValid()) destParent = mapToSource(destinationParent); else destParent = d->m_rootIndex; return sourceModel()->moveColumns(srcParent, sourceRow, count, destParent, destinationChild); } /*! \class RootIndexProxyModel \brief This proxy model will display only the portion of a tree model with the common ancestor \a rootIndex The functionality is similar to QAbstractItemView::setRootIndex but on the model side */