QFileSystemModel - iterating through current index children
-
I actually find it an interesting question, and may try my hand on it too. That's how/why I made CheckableProxyModel itself too: an interesting question :)
@andre Hi Andre,
I'm stuck with your class CheckableProxyModel. Could you help to modify it a little bit? :)
Currently your code works in such a way that when you expand a node, say R, with some files and some directories, and then check the R node, the child files and directories become checked too. But if you then expand any child directory C, all the child files of C become checked but they are not accounted as checked. Only selected directory C appears in the list.
I want to change this behavior in the following way.
(1) All directories (i.e. nodes with hasChildren==true) which haven't been expanded yet (files were not enumerated by QFileSystemModel, and rowCount==0) have no checkbox at all. The checkbox of directories appears only after expansion. I implemented this by easy modification in flags function:
if ((hasChildren && rowCount>0) || (!hasChildren && rowCount==0)) {flags |= Qt::ItemIsUserCheckable;}
(2) If the user checks expanded directory R, all the child files and all enumerated directories with checkboxes become checked too. This point doesn't need to be implemented. It is implemented in your code.
(3) After the sequence of dir R expansion -> dir R checking -> dir C expansion I would like to see 1) directory C unchecked with all of its child files and 2) directory R partially checked. I was unable to make this point work.
It seems that I need to uncheck the directory C in some place. But I don't know where and when.Can you help me how to do this please?
-
I am not sure what you want to achieve really.
The whole idea of the class is to keep as little state around as possible. That is why if a whole directory is checked, this is all the state that is kept around. What you seem to want to do is the opposite.
What is your ultimate goal? Do you just want to get easier access to all selected files and directories (with implicitly selected items included), or do you have some other goal?
-
I am not sure what you want to achieve really.
The whole idea of the class is to keep as little state around as possible. That is why if a whole directory is checked, this is all the state that is kept around. What you seem to want to do is the opposite.
What is your ultimate goal? Do you just want to get easier access to all selected files and directories (with implicitly selected items included), or do you have some other goal?
@andre Thanks for your reply, Andre!
The ultimate goal is to track all checked files. And I also want to have a lightweight storage of states. The main problem (but also advantage) is that QFileSystemModel acts minimally and lazily. It means that when I try to check non-enumerated directory with some hypothetical files inside, the directory receives checked state, but the files are not added to the list. The mismatch appears if we expand this directory: the files are all checked, but they are absent in the list. Since the files are unknown up to the moment of expansion, three possible solutions are:
- to enumerate all files just before expansion for including them in the list of checked files;
- to make all those child files unchecked just after expansion, update parent nodes making them unchecked or partially checked;
- to separate states of parent and child directories making the state of directory dependent almost on child files only, i.e. in such a way, that checking of parent directory doesn't check the child directory but, maybe, making child directory checked leads to partially checking of parent directory (if it was not fully checked before).
The first solution is hard, I think. We need to react to nodes expansion and wait for enumeration completion.
The second solution would be the best, but requires more complicated "state machine". Thus, I was asking for your help in this point.
The third solution is a compromise. Here we are forced to disable checking of directories which are not enumerated (expanded one or more times). This concept also requires changes in the management of states. I have implemented this behavior almost completely, but I observe some strange things (havn't tested enough). I tried to modify the interaction between nodes with the same set of 4 states (making child directories unchecked or determined by children after checking of the parent direcory). And one of the arguments in favor of this third solution is that we can have multiple directories with thousands of files in one main root directory and there is no danger to check millions of files by one click.
And the last, a little off-topic wish is an accurate or understandable reaction to changes in filesystem model. Currently, unpleasant situation may arise if I rename/move/delete a file or directory. How can I react if the files or directories were checked/unchecked? The most common situation is if the files appear (are generated) in a checked directory. In this case I'd like to see the transition of this directory from checked to partially checked state.
What do you think about the ideas, Andre?
-
It was designed the way it is now on purpose. The proxy model works on any tree-like data model, no matter if it is lazily populated like QFileSystemModel or not. I actually think it is quite elegant that it regards branches and leafs separately in this respect. You should be able to easily iterate over the nodes branches and leafs under a checked branch yourself, getting all the files under there. However, for many purposes this is not needed or even wanted. For instance, if you want to select which parts of your file system to include in a backup, you don't want to store individual files in your selection. You really want to have the directories, and also include any new files appearing in those directories. If you'd base your selection on files, you'd miss such new files.
So, the current design is not broken I think. I think it should work just fine for your needs of getting access to the individual files as well, if you'd just iterate over the directories (branches). That really isn't hard to do, try QDirIterator for that. No changes needed to the proxy model, just to how you use it. Note that if you check a directory high up in the hierarchy of the file system, it may be very, very slow to iterate over all files in there. I'd recommend doing that in a working thread if you really must.
To answer your last question: this is not how it currently works. Changes to the underlying model (again: it is not bound to using a QFileSystem model, that was just a convenient example as it provides a nice and big populated model without any work on my side writing the example) are dealt with in the logical way: if you have checked a branch, then any file or branch under that branch is selected. That includes new files. If a directory already has a mixed state, then new files will not be automatically selected. If you don't want that, you'll have to do a bit of work to monitor changes in the underlying model. Note that in case of the QFSM this is problematic, as it doesn't monitor branches that have not been expanded.
-
@andre Thanks, Andre, for detailed explanation. I understand that your design is solid and well thought. I just wanted to adapt one moment for my needs - disallow checking directory if the child files are not populated yet. And now I finished with code modification as I wrote above. I also mentioned strange behavior in some cases. This little problem still persists. Moreover, I discovered that this behavior takes place in initial CheckableProxyModel class also. The problem arises when directory contains only one file. Then checking of the directory adds the file to the list as expected, but the check mark doesn't appear near the file, it appears only if I move the mouse cursor over that file. If the directory contains 2 files or more, the problem disappears. Could you please check the code? It's enough to create any directory with one file and check the directory