QFileSystemModel - iterating through current index children

  • Hi Andre,

    your implementation of the Qtree´s checkstate propogation works great!!! Thank you!

    One specific question related to my project is that since I am interested in getting the unchecked files for processing. What would be the quickest method for me to get all of the unchecked filenames under the unchecked directories? Should I be using QDirectoryIterator?

  • Yes. Get the unchecked directories from the proxy model, and iterate over those directories using a QDirectoryIterator.

    I am glad my proxy model works for you!

  • Hi Andre,

    I'm trying to understand your checkableproxymodel code. It's very well written & thanks for the same.
    In mainwindow.cpp, selectedItemsChanged() function, I see the path of checked & unchecked files/directories. Also I want the size of checked files. How to get the size of the file I check ?

    Kindly help me. Thank you.

  • [quote author="rawfool" date="1399037548"]Hi Andre,

    I'm trying to understand your checkableproxymodel code. It's very well written & thanks for the same.
    In mainwindow.cpp, selectedItemsChanged() function, I see the path of checked & unchecked files/directories. Also I want the size of checked files. How to get the size of the file I check ?

    Kindly help me. Thank you.[/quote]

    That's really a bit besides the point of the model, but ok. The model only provides a proxy on top of any other tree-type model you put behind it. If that model is a model describing the file system, then you can use it to access information on files. If it is something else, then, well...

    In the example, the base model is a QFileSystemModel. QFileSystemModel provides roles for the path, the icon and the permissions, but not for the file size (weird omission, I think). The size is available from column 1 in the model though, but as a string, not as a value in bytes. To get that data, you have to use something like this:

    foreach (const QModelIndex index, selectedFiles) {
    list += "<li>" + index.data(QFileSystemModel::FilePathRole).toString() + "</li>";
    QModelIndex sizeIndex = index.model().index(index.row(), 1, index.parent());
    qDebug() << "size of" << index.data(QFileSystemModel::FilePathRole)
    << "is" << index.data();
    (note: completely untested code)

  • Hi Andre, thanks for replying.
    I'm getting an error after adding the above code and it says -

    @error: request for member 'index' in 'index.QModelIndex::model()', which is of non-class type 'const QAbstractItemModel*'@
    for the line -
    @QModelIndex sizeIndex = index.model().index(index.row(), 1, index.parent());@

    I'm novice to model view concepts, so unable to solve this. Can you help me make it right. Thank you.

  • That's why I said: untested code. It is meant as a pointer (pun intended), not as a ready-made solution.

    So, use -> instead of .

  • Hi Andre,

    In selectedItemsChanged() slot, instead of iterating through all the checked/unchecked items, is there a way to get only checked/unchecked/modified item?

    Thank you.

  • So, you want to know what changed exactly? No, there is currently no API for that. I guess it could be added by modifying the setCheckState method.

  • Thank you for responding.

    Yeah, I want to know what has changed in the tree.
    Actually I'm adding the checked items to a QTableView. On every check/uncheck of file or directory, I'm adding the same to QAbstractTableModel based class.

    For every update, I'm clearing the data and adding again in foreach loop.
    So, the problem is if there are 100000 files in a directory, and if 99998 files are checked and for another check, I will have to delete all the data, again iterate through all the 99998+1 items, which is a burden.

  • Wouldn't it be possible to just use the same model for your table? What data is in your table?

  • Actually I'm just starting to learn Model View architecture, and I'm not proficient in those concepts, so I'm struggling to find my feet.

    Things to be shown in my table which contains three columns(Directory/File, Path, Size) -

    • On every check of file, a row has to be added with the corresponding attributes.
    • On uncheck of the file/folder, the row with that record has to be deleted.
    • On check of dir, only dir path needs to be shown. No need to show the files under it.

    It's same as the output data of selectedItemsChanged() slot but without iterations of adding/deletion of items.
    Exactly like in the picture attached below -
    !http://enterpriseblog.net/wp-content/uploads/2008/07/idrive.gif(Table attached to tree)!

    Can you help me how to do this please?

  • What you are for, is a way to flatten the tree-shaped model into a flat list, selecting only the checked items (so, the items appearing in the items you can retreive from the selectedItems method). Interesting challange.

    I think I'd make a QAbstractTableModel derived proxy model, on which you can set a source model and a list of QModelIndex objects to show from that source model. The CheckableProxyModel can serve as the source model, and the lists of selected files and directories can be combined to provide the input. A proxy based on QSFPM won't work, as you also want to change the structure of the data you represent.

    The basic problem you have now: how to efficiently update, will still have to be solved, but it can essentially be solved by first creating a diff off the current list of indices versus the new list. Standard algoritms can be used to do that, and the fact that QModelIndex has operator<() helps a lot there. That results in a list of indices to remove, and a list of indices to add to the model. That can be done in a relatively simple way. Storing QModelIndex instances is not recommended, you'd have to use QPersistentModelIndex. No idea how that will perform if you have a lot of them.

    The number of rows of the proxy model would be the number of items set on it, but the number of columns would be the number of columns from the source model. The data and flags methods would simply use the stored model index to retrieve the corresponding data from the source model.

    All in all: a doable but not entirely trivial task. I can imagine that it sounds tricky to do if you are just learing the model/view framework. It is simpler than the CheckableProxyModel itself though :-)

  • Ok Andre, thank you for your guidance. Will try the way you mentioned. Thank you.

  • 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 :)

  • Cool. :)

  • @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?

  • @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:

    1. to enumerate all files just before expansion for including them in the list of checked files;
    2. to make all those child files unchecked just after expansion, update parent nodes making them unchecked or partially checked;
    3. 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

Log in to reply

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.