3

I'm developing a Qt C++ application in Unix and I've been trying to do something similar to what this image shows:

enter image description here

As you can see, there is a list of files and folders and a user can select multiple of them (if a folder is selected, all childs also get selected). I don't really care if the folder/file icons are shown.

I was able to create a list of QDir which stores all the files and folders paths given a root path. The problem is that I don't really know which widgets to use to design the selection panel.

By the way, the lis of QDir is a vector, but it can be easily modified to anything else.

Thanks!

lpares12
  • 3,504
  • 4
  • 24
  • 45
  • Have you seen this : [qfiledialog-allows-to-select-multiple-dirs-but-dont-return-them](http://stackoverflow.com/questions/8397970/qfiledialog-allows-to-select-multiple-dirs-but-dont-return-them) or this [select-multiple-files-and-dirs-using-the-same-qfiledialog](https://forum.qt.io/topic/10043/to-select-multiple-files-and-dirs-using-the-same-qfiledialog/6) Maybe can help with somethig. :D – Mara Black Mar 13 '17 at 21:01
  • 1
    http://doc.qt.io/qt-5/qtwidgets-itemviews-simpletreemodel-example.html – Steve Lorimer Mar 13 '17 at 21:19

3 Answers3

2

You can try to make proxy model for QFileSystemModel, override flags() with Qt::ItemIsUserCheckable, override setData() and apply the model to QTreeView. Full example can be found at https://github.com/em2er/filesysmodel. This code is just a concept, i have not tested it thoroughly, but you can take some ideas from it. It will look smth like on the screenshot: It will look smth like on the screenshot.

Also you can combine it with Merged Proxy Model to display multiple starting paths at one view.

em2er
  • 811
  • 5
  • 15
  • 1
    The last link about Merged Proxy Model is dead (available in [WebArchive](http://web.archive.org/web/20160820173155/http://blog.kadu.im/2012/08/merged-proxy-model.html)). Basically it mentions sources file from [the Kadu project](https://gitlab.com/kadu/kadu/wikis/home), specifically [kadu-merged-proxy-model.h](https://gitlab.com/kadu/kadu/blob/master/kadu-core/model/kadu-merged-proxy-model.h) – AntonK Apr 21 '19 at 16:57
  • 1
    @AntonK, just have updated with your link, thank you! – em2er Apr 21 '19 at 18:32
1

You might want to consider the QTreeWidget, or it's a tad more advanced version - QTreeView and an appropriate data model.

Piotr Trochim
  • 693
  • 5
  • 15
1

As some users suggested, I ended up using QFileSystemModel. I'm gonna give a full description of how I implemented it, in case someone else comes up with this problem and needs a clear response.

First of all, a QFileSystemModel is a file tree without checkboxes, to add them, a new class which extends QFileSystemModel and at least 3 methods must be overriden.

class FileSelector : public QFileSystemModel
{
public:
    FileSelector(const char *rootPath, QObject *parent = nullptr);
    ~FileSelector();

    bool setData(const QModelIndex& index, const QVariant& value, int role);
    Qt::ItemFlags flags(const QModelIndex& index) const;
    QVariant data(const QModelIndex& index, int role) const;

private:
    QObject *parent_;
    /* checklist_ stores all the elements which have been marked as checked */
    QSet<QPersistentModelIndex> checklist_;
};

When creating the model a flag, to indicate that it should have a checkable box, must be set. This is why we will use the flags function:

Qt::ItemFlags FileSelector::flags(const QModelIndex& index) const
{
    return QFileSystemModel::flags(index) | Qt::ItemIsUserCheckable;
}

When a click is made in the checkbox, the method setData will be called, with the index of the element that was clicked (not the checkbox itself, but the :

bool FileSelector::setData(const QModelIndex& index, const QVariant& value, int role)
{
    if (role == Qt::CheckStateRole && index.column() == 0) {
        QModelIndexList list;
        getAllChildren(index, list); // this function gets all children 
        // given the index and saves them into list (also saves index in the head)
        if(value == Qt::Checked)
        {
            for(int i = 0; i < list.size(); i++)
            {
               checklist_.insert(list[i]);
               // signals that a change has been made
               emit dataChanged(list[i], list[i]);
            }
        }
        else if(value == Qt::Unchecked)
        {
            for(int i = 0; i < list.size(); i++)
            {
                checklist_.remove(list[i]);
                emit dataChanged(list[i], list[i]);
            }
        }
        return true;
    }
    return QFileSystemModel::setData(index, value, role);
}

When dataChanged is signaled or you open a new path of the tree, the data function will be called. Here you have to make sure to only display the checkbox at the first column (next to the filename), and to retrieve the state of the checkbox, to mark it as checked/unchecked.

QVariant FileSelector::data(const QModelIndex& index, int role) const
{
    if (role == Qt::CheckStateRole && index.column() == 0) {
        if(checklist_.contains(index)) return Qt::Checked;
        else return Qt::Unchecked;
    }
    return QFileSystemModel::data(index, role);
}

The only thing I was not able to accomplish was getting all childs, since the folders must be open to retrieve the childs. So a closed folder won't have any child until you open it.

Hope this can help someone who has the same problem as I did!

lpares12
  • 3,504
  • 4
  • 24
  • 45
  • @em2er I havent but I will tomorrow. Do you think it will fix it? To give you an example, in the OP image if I selected the `kankan` folder (which is folded) would get no childs, while selecting `MyPicture` folder (which is open) would get 8 childs. – lpares12 Mar 14 '17 at 20:17
  • Not sure, but it can. QFileSysModel is optimized to not dispatch invisible and nested elements because of slow disk operations. Silly method to solve it is expand the tree view item on check and do the stuff, but it is not an option, I think. – em2er Mar 14 '17 at 20:23
  • Mmh, I see... In my case, having selected the parent folder is enough for the application, so it's just a visual thing, since I can't check the child files if the folder is not unfolded, and when I unfold it childs appear unchecked. Will give it a try tomorrow though! – lpares12 Mar 14 '17 at 20:28