Unsolved How do you add a custom column to QFileDialog?
-
I know this has been asked before but the solution has never been given.. at least that I can find. I want to add a column to the QFileDialog details view. I have my own custom proxy model that I have subclassed from QSortFilterProxyModel. I override flags, headerData, data, columnCount as well as filterAcceptsRow and lessThan because I am also doing custom sorting and filtering.
I have added overrides for parent, index, mapFromSource and mapToSource. I'm just not sure how indexes work though.
My custom column header is displayed but the data method is never called with an index that has the custom column and DisplayRole.
Since this has been asked several times, I'm hoping someone could point me to a working solution. I also want to understand how the model indexiing works. My overrides for mapToSource and mapFromSource just call the base class. My overrides for index and parent are below.
QModelIndex MyFilterProxyModel::index( int row, int col, const QModelIndex& index ) const
{
if( col > columnCount() )
return QModelIndex();if( col > 3 && index.isValid() )
{
return createIndex( row, col, index.internalPointer() );
}return QSortFilterProxyModel::index( row, col, index );
}QModelIndex MyFilterProxyModel::parent( const QModelIndex& index ) const
{
if( index.isValid() && index.column() > 3 )
{
return createIndex( index.row(), 0, index.internalPointer() );
}
return QSortFilterProxyModel::parent( index );
} -
So I can get my data to display but I can't select the file. I get this error message in my output window:
Can't select indexes from different model or with different parents
-
I must say I'm a little surprised at the lack of replies. If more info is needed, please let me know. I have provided all of the source below.
A little bit of an update. I can display the custom column with data now. However, I could not select the file until I changed my implementation of parent and index. When I select the file, I get the following assertion but it works otherwise. Of course this means something is wrong.
QSortFilterProxyModel: index from wrong model passed to mapFromSource ASSERT: "!"QSortFilterProxyModel: index from wrong model passed to mapFromSource"" in file itemviews\qsortfilterproxymodel.cpp, line 383 QSortFilterProxyModel: index from wrong model passed to mapFromSource
Here are is code for MyFilterProxyModel.h
#ifndef MYFILTERPROXYMODEL_H #define MYFILTERPROXYMODEL_H #include <QObject> #include <QSortFilterProxyModel> #include <QStringList> #include <QFileDialog> #include <QDir> #include "qitemselectionmodel.h" class MyFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: explicit MyFilterProxyModel(QObject *parent = 0); MyFilterProxyModel( QFileDialog* pFileDialog, QObject *pParent ); // // QSortFilterProxy model overrides. QVariant headerData( int Section, Qt::Orientation Orientation, int Role = Qt::DisplayRole ) const; QVariant data( const QModelIndex& Index, int Role = Qt::DisplayRole ) const; int columnCount( const QModelIndex& parent = QModelIndex() ) const; Qt::ItemFlags flags( const QModelIndex& index ) const; QModelIndex index( int row, int col, const QModelIndex& index = QModelIndex() ) const; QModelIndex mapFromSource( const QModelIndex& sourceIndex ) const; QModelIndex mapToSource( const QModelIndex& proxyIndex ) const; QModelIndex parent( const QModelIndex& index ) const; protected: bool lessThan( const QModelIndex& left, const QModelIndex& right ) const; signals: public slots: private: QFileDialog* m_pFileDialog; }; #endif // MYFILTERPROXYMODEL_H
Here is the implementation:
#include "myfilterproxymodel.h" #include <QFileDialog> #include <QDebug> MyFilterProxyModel::MyFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent) { } MyFilterProxyModel::MyFilterProxyModel( QFileDialog* pFileDialog, QObject *pParent ) : QSortFilterProxyModel( pParent ) { m_pFileDialog = pFileDialog; } int MyFilterProxyModel::columnCount( const QModelIndex& parent ) const { return 5; } QVariant MyFilterProxyModel::headerData( int Section, Qt::Orientation Orientation, int Role ) const { QVariant HeaderData = QSortFilterProxyModel::headerData( Section, Orientation, Role ); if( Role == Qt::DisplayRole ) { if( Section > 3 ) { HeaderData = "custom header"; } } return HeaderData; } QVariant MyFilterProxyModel::data( const QModelIndex& Index, int Role ) const { QVariant Data; if( Role == Qt::DisplayRole ) { if( Index.column() == 0 || Index.column() == 4 ) { // qDebug() << "data " << Data; // qDebug() << "proxy " << Index; // qDebug() << "parent " << Index.parent(); // qDebug() << "source " << mapToSource(Index); // qDebug() << "sibling " << Index.sibling(Index.row(), 0); } if( Index.column() > 3 ) { QModelIndex SIndex = mapToSource( Index ); Data = SIndex.row(); } else { Data = QSortFilterProxyModel::data( Index, Role ); } } else if (Role == Qt::DecorationRole && Index.column() > 3 ) { } else { Data = QSortFilterProxyModel::data( Index, Role ); } return Data; } bool MyFilterProxyModel::lessThan( const QModelIndex& Left, const QModelIndex& Right ) const { if( Left.column() > 3 || Right.column() > 3 ) { return Left.row() < Right.row(); } return QSortFilterProxyModel::lessThan( Left, Right ); } Qt::ItemFlags MyFilterProxyModel::flags( const QModelIndex& index ) const { if( index.column() > 3 ) { return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } return QSortFilterProxyModel::flags( index ); } QModelIndex MyFilterProxyModel::mapFromSource( const QModelIndex& sourceIndex ) const { return QSortFilterProxyModel::mapFromSource( sourceIndex ); } QModelIndex MyFilterProxyModel::mapToSource( const QModelIndex& proxyIndex ) const { if( proxyIndex.isValid() && proxyIndex.column() > 3 ) { return QSortFilterProxyModel::mapToSource( proxyIndex.sibling( proxyIndex.row(), 1) ); } return QSortFilterProxyModel::mapToSource( proxyIndex ); } QModelIndex MyFilterProxyModel::index( int row, int col, const QModelIndex& index ) const { if( ( col > 3 ) && index.isValid() ) { // ??? This does not make sense. Shouldn't we use sibling instead of child here??? // only the first option below allows a file to be selected however we get an assertion in mapFromSource. return createIndex( row, col, index.child(row,1).internalPointer() ); // return createIndex( row, col, index.sibling(row,1).internalPointer() ); // return createIndex( row, col, index.internalPointer() ); } return QSortFilterProxyModel::index( row, col, index ); } QModelIndex MyFilterProxyModel::parent( const QModelIndex& Index ) const { if( Index.isValid() && ( Index.column() > 3 ) ) { // Return the parent of my sibling (same row, column 0). return QSortFilterProxyModel::parent( createIndex(Index.row(), 1, Index.internalPointer() ) ); } return QSortFilterProxyModel::parent( Index ); }
Here is my usage:
#include <QApplication> #include <QFileDialog> #include "myfilterproxymodel.h" #include <QDebug> int main(int argc, char *argv[]) { QApplication a(argc, argv); // Create the file open dialog to use. QFileDialog* pFileDialog = new QFileDialog( 0, "My Title", "C://TEMP" ); // Create our own proxy model to customize the dialog. MyFilterProxyModel *pProxy = new MyFilterProxyModel( pFileDialog, 0 ); // Customize pFileDialog->setFileMode( QFileDialog::ExistingFile ); pFileDialog->setAcceptMode( QFileDialog::AcceptOpen ); pFileDialog->setViewMode( QFileDialog::Detail ); pFileDialog->setFilter( pFileDialog->filter() | QDir::Hidden ); pFileDialog->setOption( QFileDialog::DontUseNativeDialog, true ); pFileDialog->setProxyModel( pProxy ); pProxy->sort( 0 ); // Show the dialog. pFileDialog->exec(); delete pFileDialog; return 0;//a.exec(); }
Another issue I have also noticed is that I can only sort on my custom column once. My lessThan override is not called for my custom column.
Any insights are greatly appreciated!
-
Bump... hoping someone has some suggestions.