I’m having an issue for a while with a QFileSystemModel /QTreeView implementation. What I’m trying to achieve form the application user prospective. I have a QTreeView displaying the file system it only shows the directory name and file names. Each item in the view is checkable. That part is easy to design but what comes next is much harder. I’d like the application automatically expend and check all the items in all sub-directories if the user checks a directory. And I’d like it to automatically uncheck all the items in all sub-directories if the user unchecks a directory too.
I have been trying to implement the second part for a while. I searched the forum and the internet for hints and I found that other people had the same problem and could not find solid answers.Some people tried to iterate recursively through the indexes in the model starting from the parent index associated with the checked directory in the view. (If the children in level#2 are parents then we iterate through them, and so on in all the sub level, until there is no more parents to iterate through.) And most people have issues with:
Int rowCount(const QModelIndex & parent = QModelIndex()) const
In my case, it returns inconsistent results (often 1 or 0) even if there are many more child elements under the parent element.
Here is one of the unsuccessful attempts I made to check all the elements under the user selected directory in my QFileSystemModel subclass:
{
if (role == Qt::CheckStateRole)
{
//if the current item is in the checklist then remove it.
//if the current item is not in the checklist then add it.
if(value == Qt::Checked) m_checkTable.insert(index); else m_checkTable.remove(index);
emit dataChanged(index, index);
std::vector<QModelIndex*> parentTable(1,&const_cast<QModelIndex&>(index));
//Check all the sub-items.
while (!parentTable.empty())
{
currentIndex = parentTable[parentTable.size()-1];
parentTable.pop_back();
if(value == Qt::Checked) m_checkTable.insert(*currentIndex); else m_checkTable.remove(*currentIndex);
emit dataChanged(*currentIndex, *currentIndex);
if (hasChildren(*currentIndex))
{
for (int i(0); i < rowCount(*currentIndex); i++)
{
currentChildIndex = &FileSysSelectModel::index(i,0,*currentIndex);
parentTable.push_back(currentChildIndex);
}
}
}
return true;
}
// regular QFileSystemModel case.
return QFileSystemModel::setData(index, value, role)
bool FileSysSelectModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (role == Qt::CheckStateRole)
{
//if the current item is in the checklist then remove it.
//if the current item is not in the checklist then add it.
if(value == Qt::Checked) m_checkTable.insert(index); else m_checkTable.remove(index);
emit dataChanged(index, index);
QModelIndex *currentIndex;
QModelIndex *currentChildIndex;
std::vector<QModelIndex*> parentTable(1,&const_cast<QModelIndex&>(index));
//Check all the sub-items.
while (!parentTable.empty())
{
currentIndex = parentTable[parentTable.size()-1];
parentTable.pop_back();
if(value == Qt::Checked) m_checkTable.insert(*currentIndex); else m_checkTable.remove(*currentIndex);
emit dataChanged(*currentIndex, *currentIndex);
if (hasChildren(*currentIndex))
{
for (int i(0); i < rowCount(*currentIndex); i++)
{
currentChildIndex = &FileSysSelectModel::index(i,0,*currentIndex);
parentTable.push_back(currentChildIndex);
}
}
}
return true;
}
// regular QFileSystemModel case.
return QFileSystemModel::setData(index, value, role)
To copy to clipboard, switch view to plain text mode
Question1: Does the data in the tree model depend on the exposed items in the tree view?
For example, does the data corresponding to a collapsed branch exist in the model? I believe it does not and I think that the model retrieves the data corresponding to a directory only when the user asks for expending that same directory in the tree view. I’d like to confirm that because the QFileSystemModel doc says very little about it and it not clear to me at all.
Question2: If my assumption in question 1 is correct, how do we access the data of the collapsed branches in order to iterate through them?
A few people mentioned the use of the two following methodes to resolve the issue in one or two threads I’ve seen:
- Virtual Bool canFetchMore(const QModelIndex & parent) const
- Virtual void fetchMore(const QModelIndex & parent)
Here again, the documentation does not say much about it.
Question 3: From my understanding fetchMore populates the data under the parent QModelIndex and adds it to the model. Is that correct?
Here is another attempt. I tried to rework the same piece of code but it does not do better:
//Check all the sub-items.
while (!parentTable.empty())
{
currentIndex = parentTable[parentTable.size()-1];
parentTable.pop_back();
if(value == Qt::Checked) m_checkTable.insert(*currentIndex); else m_checkTable.remove(*currentIndex);
emit dataChanged(*currentIndex, *currentIndex);
if (hasChildren(*currentIndex))
{
while (canFetchMore(*currentIndex))
{
fetchMore(*currentIndex);
}
for (int i(0); i < rowCount(*currentIndex); i++)
{
currentChildIndex = &FileSysSelectModel::index(i,0,*currentIndex);
parentTable.push_back(currentChildIndex);
}
}
}
//Check all the sub-items.
while (!parentTable.empty())
{
currentIndex = parentTable[parentTable.size()-1];
parentTable.pop_back();
if(value == Qt::Checked) m_checkTable.insert(*currentIndex); else m_checkTable.remove(*currentIndex);
emit dataChanged(*currentIndex, *currentIndex);
if (hasChildren(*currentIndex))
{
while (canFetchMore(*currentIndex))
{
fetchMore(*currentIndex);
}
for (int i(0); i < rowCount(*currentIndex); i++)
{
currentChildIndex = &FileSysSelectModel::index(i,0,*currentIndex);
parentTable.push_back(currentChildIndex);
}
}
}
To copy to clipboard, switch view to plain text mode
Some fetchMore users say that the reason why that kind of implementation does not work is because the FileSystemModel updated its data with an independent thread, so when the main thread executes rowCount, fetchMore has not finished updating the data.
I tried to use the directoryLoaded(const QString & path) signal by implementing the SLOT below in my FileSystemModel sub class to make sure fetchMore more had finish executing when calling rowCount.
void FileSysSelectModel
::dataReady(const QString ¤tPath
) {
m_currentPath = currentPath;
}
void FileSysSelectModel::dataReady(const QString ¤tPath)
{
m_currentPath = currentPath;
}
To copy to clipboard, switch view to plain text mode
//Check all the sub-items.
while (!parentTable.empty())
{
currentIndex = parentTable[parentTable.size()-1];
parentTable.pop_back();
if(value == Qt::Checked) m_checkTable.insert(*currentIndex); else m_checkTable.remove(*currentIndex);
emit dataChanged(*currentIndex, *currentIndex);
if (hasChildren(*currentIndex))
{
QString test1
= filePath
(*currentIndex
);
//debug while ((canFetchMore(*currentIndex)) || (m_currentPath != filePath(*currentIndex)))
{
fetchMore(*currentIndex);
}
for (int i(0); i < rowCount(*currentIndex); i++)
{
currentChildIndex = &FileSysSelectModel::index(i,0,*currentIndex);
parentTable.push_back(currentChildIndex);
}
}
}
return true;
//Check all the sub-items.
while (!parentTable.empty())
{
currentIndex = parentTable[parentTable.size()-1];
parentTable.pop_back();
if(value == Qt::Checked) m_checkTable.insert(*currentIndex); else m_checkTable.remove(*currentIndex);
emit dataChanged(*currentIndex, *currentIndex);
if (hasChildren(*currentIndex))
{
QString test1 = filePath(*currentIndex); //debug
while ((canFetchMore(*currentIndex)) || (m_currentPath != filePath(*currentIndex)))
{
fetchMore(*currentIndex);
}
for (int i(0); i < rowCount(*currentIndex); i++)
{
currentChildIndex = &FileSysSelectModel::index(i,0,*currentIndex);
parentTable.push_back(currentChildIndex);
}
}
}
return true;
To copy to clipboard, switch view to plain text mode
That does not work either.
Any idea?
Thanks
Bookmarks