Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QSortFilterProxyModel, QFileSystemModel and QTreeView browse USB



  • Re: QTreeview filtering search

    All,
    We are on CentOS 7.6 and using Qt 5.12.0. We are using a QTreeView to browse local filesystems. The model is a QFileSystemModel. In the original code we were using the directoryLoaded and rootPathChanged signals in the QFileSystemModel. In the treeView, we were using setModel to aggregate the QFileSystemModel object(model) into the QTreeView object(tree).

    Now what happens is the user inserts a USB stick and informs our application of it's presence. The USB stick is auto-mounted by an external process and our application knows how to look for USB mounts. We can select from a list of USB sticks found and browse them.

    If the user pulls the USB stick out while it is being browsed, the root desktop is exposed. We don't want that.

    So after reading the Qt docs and posts in here, I got the idea to subclass the QSortFilterProxyModel class and reimplement filterAcceptsRow(). It is pretty simple. I just ask it to filter out anything that is not under the USB mount point. The filter works beautiful.

    Now we are currently using the QFileSystemModel::directoryLoaded() and QFileSystemModel::rootPathChanged() to signal a change.

    My question is do I need to reimplement those signals in my QSortFilterProxyModel subclass to run the directory through the filter and react accordingly? I am setting the sourceModel in my QSortFilterProxyModel object to the QFileSystemModel object so could access the signals through sourceModel()->directoryLoaded(). But that isn't what I should do, correct?

    The other piece is that were extracting a QModelIndexList by calling ui.tree->selectionModel()->selectedRows(). From reading the docs, I believe the selection model is the same as the source model unless we explicitly set the selection model. Is that correct? If that is the case, do I need to reimplement selectedRows() in my proxy class to filter the rows based on my filter?

    Regards,
    Sandra Carney



  • @Sandra_Carney
    I'm tired, but I don't think there's anything for you to implement, These are methods for you to call. The selection model is not the same thing as the source model. It is a model for the selections the user has made. These are against the proxy model. QSortFilterProxyModel::mapSelectionToSource() maps from a proxy model selection through to the corresponding source model selection. Which resolves the crash you reported.



  • I think I figured out how to hook the signals and slots up correctly. Now when I generate QModelIndexList(list) by calling ui.tree->selectionModel()->selectedRows() and try to access the list, my application crashes. I think I have to get the rows from the QSortFilterProxyModel object. That is where I am stuck.

    I also called QSortFilterProxyModel::rowCount() via my object. It always returns 0 which is surprising.



  • @Sandra_Carney

    ui.tree->selectionModel()->selectedRows() and try to access the list, my application crashes
    I think I have to get the rows from the QSortFilterProxyModel object

    See https://doc.qt.io/qt-5/qsortfilterproxymodel.html#mapSelectionToSource (& https://doc.qt.io/qt-5/qsortfilterproxymodel.html#mapSelectionFromSource).

    I don't know why your QSortFilterProxyModel::rowCount() is 0.



  • @JonB My first question is which class implements selectedRows()? I don't see it in QFileSystemModel() and besides the code is calling ui.tree->selectionModel()->selectedRows(). What is the selection model? Is it the same as the sourceModel() if it is not explictly set or is it some other default?



  • OK, I poked through the include files and found where selectedRows() is implemented. I plan to implement selectedRows() in my QSortFilterProxyModel subclass based on your suggestion of mapSelectionFromSource() right?



  • @Sandra_Carney
    I'm tired, but I don't think there's anything for you to implement, These are methods for you to call. The selection model is not the same thing as the source model. It is a model for the selections the user has made. These are against the proxy model. QSortFilterProxyModel::mapSelectionToSource() maps from a proxy model selection through to the corresponding source model selection. Which resolves the crash you reported.



  • @JonB Thankyou, very, very much. You gave me enough to figure it out. For folks' benefit:

        MyProxy* pm= qobject_cast<MyProx*>(ui.tree->model());
        QFileSystemModel* fm = (QFileSystemModel*)pm->sourceModel();
        QItemSelectionModel *select = ui.tree->selectionModel();
        QModelIndexList l = select->selectedRows();
    
        if( list.count() )
        {
            path = fm->filePath(pm->mapToSource(l[0]));
            emit pathSelectionChanged(path);
        }
    


  • @Sandra_Carney

        QItemSelectionModel *select = ui.tree->selectionModel();
        QModelIndexList l = select->selectedRows();
        ...
            pm->mapToSource(l[...])
    

    This is fine. You are working through selectionModel()->selectedRows() calling QProxyModel::mapToSource() on an item to get from a proxy QModelIndex to the source QModelIndex.

    I believe QSortFilterProxyModel::mapSelectionToSource() is just a convenience to save the two steps. E.g.

    QItemSelectionModel *select = ui.tree->selectionModel();
    QModelIndexList sourceIndexList = pm->mapSelectionToSource(select->selection())->indexes();
    

    Whether it's just a shortcut convenience or it's more efficient I don't know. The important thing either way is that you have to do this index mapping source model <--> proxy model indexes.


Log in to reply