Solved QTableview confusion
-
@Toby
Good start! You can omit all those explicitQVariant( ... )
conversions, C++ will do that for you from function signatureQVariant MyItemModel::data()
, e.g justreturn Qt::AlignCenter;
orreturn QColor( 255, 0, 0 );
. -
I explicitely used that QVariant, everywhere because it doesnt work for combined Alignment-Flags like "Qt::AlignLeft | Qt::AlignVCenter". But it works at least for single alignments, colors and strings.
Working on filtering at the moment, but "filterAcceptsRow" never gets called.
Still a lot to learn ;-) -
@Toby
filterAcceptsRow
does get called. You have to subclassQSortFilterProxyModel
to re-implement/place a breakpoint etc. -
My subclassed QSortFilterProxyModel still doesnt work.
At the moment it's basically a fulltextsearch, where the SearchText gets specified via slot and calls invalidateFilter().Header-File:
class ProxyModel : public QSortFilterProxyModel { Q_OBJECT public: ProxyModel( QObject *parent_ ) : QSortFilterProxyModel( parent_ ) {} virtual ~ProxyModel() = default; bool filterAcceptsRow( int, const QModelIndex & ) const override; QVariant headerData( int, Qt::Orientation, int ) const override; bool lessThan( const QModelIndex &, const QModelIndex & ) const override; public slots: void setSearchText( const QString & ); private: QString SearchText = ""; };
Source-File:
bool ProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const { qDebug() << "lessThan"; return true; } bool ProxyModel::filterAcceptsRow( int source_row, const QModelIndex & ) const { qDebug() << "filterAcceptsRow"; return true; } QVariant ProxyModel::headerData(int section, Qt::Orientation orientation, int role ) const { return sourceModel()->headerData( section, orientation, role ); } void ProxyModel::setSearchText( const QString &text_ ) { SearchText = text_; qDebug() << "New Searchtext: " << SearchText; invalidateFilter(); }
MainWindow:
auto tbl = new QTableView( h.parentWidget() ); h.addWidget( tbl ); tbl->setEditTriggers( QAbstractItemView::NoEditTriggers ); model = new MyTableModel( this ); proxy = new ProxyModel( this ); proxy->setSourceModel( model ); proxy->setFilterRole( MyTableModel::Name ); tbl->setModel( model ); tbl->verticalHeader()->hide(); tbl->setSelectionBehavior( QAbstractItemView::SelectionBehavior::SelectRows ); tbl->setColumnWidth( 0, 100 ); tbl->horizontalHeader()->setSectionResizeMode( 1, QHeaderView::Stretch ); tbl->verticalHeader()->setDefaultSectionSize( 20 ); tbl->setSortingEnabled( true ); tbl->sortByColumn( 0, Qt::DescendingOrder ); connect( edTextSearch, SIGNAL(textChanged(QString)), proxy, SLOT(setSearchText(QString)));
-
Hi,
@Toby said in QTableview confusion:
tbl->setModel( model );
That's your issue, you are setting your model rather than the proxy, therefore it's not used.
-
Now i dont see anything. My data is not visualized anymore. And filterAcceptsRow() isn't called either.
But it is correct to fill the SourceModel with all the user-data ( data()-Method ) and to only override the methods "lessThan()" and "filterAcceptsRow()" in the ProxyModel?
-
Just as a test i also tried overriding some other methods:
int ProxyModel::columnCount(const QModelIndex &index ) const { return sourceModel()->columnCount( index ); } int ProxyModel::rowCount( const QModelIndex &index ) const { return sourceModel()->rowCount( index ); } QVariant ProxyModel::data(const QModelIndex &index, int role ) const { return sourceModel()->data( index, role ); }
but the data()-method of the ProxyModel doesnt get called. Defining rowCount() and columnCount() at least made the view draw the Grid-Lines. But no visible content so far in the view...
Ok when i use the default QSortFilterProxyModel it works ( of course my fulltextsearch doesnt work with the default proxymodel ). So it's an issue with the subclassing....It works, using QStandardItemModel and QSortFilterProxyModel ... but it doesnt work using my own model and QSortFilterProxyModel
What i found out:
Using my own Data-Model and the default QSortFilterProxyModel , only the following methods of my model get called:- rowCount()
- columnCount()
- headerData()
Overridden methods that don't get called:
- data()
- index()
- parent()
-
@Toby
There are a couple of things wrong in your implementation. Whether they cause the behaviour you see I cannot say, but you should get the code right before you do any investigations/analysis.-
ProxyModel::lessThan()
: Yours always goesreturn true
. You must not do this, it will confuse any sorting. You will find the Qt infrastructure callslessThan()
with the same model indexes but reversing left and right. By returningtrue
in both cases you can make it impossible for it to sort correctly. -
When writing a
QSortFilterProxyModel
overridden function which deals withQIndex
es you must be aware of whether indexes point into the proxy model or the source model. You may need to map correctly between these. See theQSortFilterProxyModel::mapFrom
/To...()
methods, and use as appropriate.
There is a Custom Sort/Filter Model Example. You should look carefully at that, test it works, then compare against your code.
-
-
@JonB i downloaded that example here:
https://www.walletfox.com/course/qsortfilterproxymodelexample.php
which works fine for me. He subclasses QAbstractTableModel, while i subclass QAbstractItemModel. I did the same but it did not improve anything.
atm i am using the default QSortFilterProxyModel to reduce complexity. Seems like there is still an issue with my TableModel (sourcemodel). -
I've found out about my issue.
The problem was, that i created view and models first, and initiated the userdata a little later via setter-Function. Now that i deliver the data already within the models constructor, it works perfectly, also with my subclassed QSortFilterProxyModel.
So the real issue was the update/invalidate-functionality. I probably did something wrong.
This is how i updated my models userdata:
UserData.clear(); UserData.reserve( data_.size() ); for ( auto &it : data_ ) UserData.push_back( it ); QModelIndex topleft = index( 0, 0 ); QModelIndex bottomright = index( rowCount() - 1, columnCount() - 1 ); emit dataChanged( topleft , bottomright );
And that dataChanged-signal doesn't seem to do what i expected it to do...
Edit:
My solution is now to use beginModelReset() and endModelReset around the updating-process.
beginModelReset(); UserData.clear(); UserData.reserve( data_.size() ); for ( auto &it : data_ ) UserData.push_back( it ); endModelReset();
which works perfectly, although it is probably overkill, but an easy way if one doesn't know where the changes exactly occur.
Thanks to all of you for your help! :-)
-
@Toby said in QTableview confusion:
which works perfectly, although it is probably overkill, but an easy way if one doesn't know where the changes exactly occur.
It this case it's not overkill but the right thing to do. You complexly nuke your model content and repopulate it so it is, in fact, a reset.