Showing specific directories in QFileTreeView
-
Re: How to filter folder in treeview
Hi,
I was earlier reading this thread on filtering specific directories from QTreeView and one of the solution was to suclass QSortProxyModel's "filterAcceptsRow()" method. My situation is I'm showing on QTreeView my directory using QFileSystemModel which will contain only directories and no files as I'm checking for that. I was looking for a way to filter out certain directories with their name "csvs" and "logs" to not show up on QTreeView. For example here is what my structure looks like
Now I looked at sub-classing filterAcceptsRow() method which takes in "int source_row, const QModelIndex &source_parent" as args. I would like to know the logic flow here as I was thinking how I would get the index and row# of that particular directory (csvs/logs) in my model. One way was to get through their filePath using "QModelIndex QFileSystemModel::index(const QString &path, int column = 0) const" but then I will have to detect first whether the "csvs" and "logs"directories are there during input. Just wanted to make sure if this is correct way to go for. Thanks
@Chris-Kawa This is what you mean't when answering that post as that is an old post and I was wondering if there is any new modification for this feature -
@sogo
Not sure I understand what you are asking/suggesting here. You will not try to go from directory name to index, which I believe is what you are thinking. Rather, infilterAcceptsRow()
you will go from index to directory name, and then return false if that matches one you want to hide. -
So I tried from what I think I understand, Here is my code. I have TreeView class which has QTreeView widget and I have subclassed a QFileSystemModel class (FileSystem) to highlight the row bolds for row double clicked. This is what my TreeView.cpp looks like for this function. The openFile function is called from my MainWindow.cpp class which sends it the directory path I choose from QFileDialog:
TreeView.cppvoid TreeView::openFile(const QString& folderPath) { mySortFilterModel->setSourceModel(myModel); myModel->setFilter(QDir::NoDotAndDotDot | QDir::AllDirs); myModel->setRootPath(folderPath); QModelIndex index = myModel->index(folderPath); QDir path(folderPath); QString absPathLogs; QModelIndex indLogs; int rowLog; QString absPathCsvs; QModelIndex indCsvs; int rowCsvs; QStringList list = path.entryList(QDir::AllEntries | QDir::NoDotAndDotDot); if (list.contains("csvs")) { absPathCsvs = path.absoluteFilePath("csvs"); indCsvs = myModel->index(absPathCsvs); rowCsvs = indCsvs.row(); mySortFilterModel->catchIndex(indCsvs); } if (list.contains("logs")) { absPathLogs = path.absoluteFilePath("logs"); indLogs = myModel->index(absPathLogs); rowLog = indLogs.row(); mySortFilterModel->catchIndex(indLogs); } ui->treeView->setModel(mySortFilterModel); ui->treeView->setRootIndex(mySortFilterModel->mapFromSource(index)); ui->treeView -> expand(index); }
So the index is sent of the directory which is "csvs" or "logs" and to QSortFilterProxyModel subclass mySortFilterModel. which stores it using catchIndex function
mySortFilterModel.cppbool SortFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); if (index.data() == _index.data()) { //Check if index.data() is equal to _index.data(). If equal, don't accept that row return false; } else{ return QSortFilterProxyModel::filterAcceptsRow(sourceRow,sourceParent); } } void SortFilterModel::catchIndex (const QModelIndex& index) { _index = index; //Save index coming from TreeView class to QPersistentModelIndex variable }
Right now this works but it only change model for one Directory, which ever is called last. In this case "logs" index is called last so it does not show logs directory in treeView. I'm not sure if this is correct way, I'm looking into it now but any suggestions will be helpful.
@JonB you are right, this is what I'm doing currently
-
@sogo You are doing the filtering on the wrong level. Get rid of the
catchIndex
method and all that logic on top level and do the filtering in filterAcceptRow with something likeQModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); QString dir_name= index.data().toString(); if ( /* dir_name.contains(...) or whatever your rules are */ ) return false;
-
@sogo
As per @Chris-Kawa's post. the whole idea of going from name to index is quite unnecessary and extremely slow. No need toentryList()
to visit the files that are there. As per his, just insidefilterAcceptRow()
all you have to do is see the name of the file forindex
, and reject if not wanted. 4 lines of code, one lookup, small & quick :) -
Hi @Chris-Kawa and @JonB , yea now it works perfectly fine. Got rid of catchIndex() and just checked for data contains in filterAcceptsRow function. I have one issue, I'm unable to get row count from my filtered model. It always returns 0. Here is my code snippet for checking row count in TreeView class:
TreeView.cppvoid TreeView::openFile(const QString& folderPath) { mySortFilterModel->setSourceModel(myModel); myModel->setFilter(QDir::NoDotAndDotDot | QDir::AllDirs); myModel->setRootPath(folderPath); QModelIndex index = myModel->index(folderPath); ui->treeView->setModel(mySortFilterModel); ui->treeView->setRootIndex(mySortFilterModel->mapFromSource(index)); ui->treeView -> expand(index); int rows = myModel->rowCount(index); qDebug() << rows; }
As per documentation, it says it will return 0 if
Calls to rowCount() will return 0 until the model populates a directory.
Shouldn't model would have populated the directory when I call it?Update: I see what it means by not completing the populating the directory. I have to use signal directoryLoaded() and slot to wait when the directory is completed loaded. It works now. I have to try few things and will close this thread.
-
@sogo said in Showing specific directories in QFileTreeView:
Update: I see what it means by not completing the populating the directory. I have to use signal directoryLoaded() and slot to wait when the directory is completed loaded.
Yes. In order to be "fast", in keeping with the way Qt tries to offer an asynchronous framework,
QFileSystemModel
does its directory reading population in the background, I believe, in its own thread. So the population is not complete until it sends you thatdirectoryLoaded
signal.