PDA

View Full Version : Subclassing QSortFilterProxyModel problem



e79ene
21st February 2011, 12:07
Hello!

I've met a problem with subclassing QSortFilterProxyModel.
What I want is a proxy model which hides some of the source table model columns (and also supports sorting, and do some other things).

I subclass a QSortFilterProxyModel and override filterAcceptsColumn() function.

The decision on which column to hide is based on rather huge computation. So I wish to do such computations only when source model changes significantly, e,g, when new column is added. Thus I connect source model's corresponding signal to my proxy's updateCache() slot. That slot does the computation and saves it's results in to a vector of bools.

filterAcceptsColumn() function simply uses this vector to find out which column to hide.

Below is a complete test code which I expect to work. But it crashes.

The reason of the failure is that QSortFilterProxyModel() constructor also connects that signal to it's private slot. That slot is invoked before my updateCache() slot in a subclass. That parent slot emits similsr signal which is connected to a view object. The view tries to react on model change and calls columnCount() functoin of the proxy which in turn calls filterAcceptsColumn() for every column of a changed source model. But my updateCache() slot in a subclass was not called yet. Thus the required computation was not done for a new number of a source columns and the vector was not resized properly. The program crashes trying to access non existent element of the vector.

Any suggestions on how to solve this problem?

MyProxy.h


#ifndef MYPROXY_H
#define MYPROXY_H

#include <QSortFilterProxyModel>
#include <QDebug>
#include <QVector>

class MyProxy : public QSortFilterProxyModel
{
Q_OBJECT
private:
QVector<bool> hiddenColumns;

public:
void setSourceModel(QAbstractItemModel* sourceModel)
{
QSortFilterProxyModel::setSourceModel(sourceModel) ;

updateCache();

connect(sourceModel, SIGNAL(columnsInserted(const QModelIndex&, int, int)),
SLOT(updateCache()));
}

virtual bool filterAcceptsColumn(int source_column, const QModelIndex&) const
{
qDebug() << "filterAcceptsColumn(): column = " << source_column <<
"\tcached = " << hiddenColumns.size();
return ! hiddenColumns.at(source_column);
}

private slots:
void updateCache()
{
qDebug() << "updateCache()";

hiddenColumns.resize(sourceModel()->columnCount());

for (int c = 0; c < hiddenColumns.size(); c++)
hiddenColumns[c] = c % 2; // consider huge computation here
}

};

#endif // MYPROXY_H

MyProxy.cpp


#include "MyProxy.h"

#include <QApplication>
#include <QTableView>
#include <QStandardItemModel>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);

QStandardItemModel source(1, 1);
MyProxy proxy;
proxy.setSourceModel(& source);

QTableView v;
v.setModel(& source);
v.show();

qDebug() << "adding column...";
source.setColumnCount(2);

return a.exec();
}

Program output:


updateCache()
adding column...
filterAcceptsColumn(): column = 0 cached = 1
filterAcceptsColumn(): column = 1 cached = 1
ASSERT failure in QVector<T>::at: "index out of range", file ..\..\..\..\Qt\2010.05\qt\include/QtCore/../../src/corelib/tools/qvector.h, line 339
Invalid parameter passed to C runtime function.
Invalid parameter passed to C runtime function.


P.S. I use Qt 4.7 on Windows with gcc

wysota
21st February 2011, 12:21
One idea would be to connecto to columnsAboutToBeInserted. Another would be to perform the computations in filterAcceptsColumn() after detecting that the column count has changed.

e79ene
21st February 2011, 13:23
Thanks for your answer. However...


One idea would be to connecto to columnsAboutToBeInserted.

I need the new (inserted, changed) data for the computation. On the "...AboutToBe..." stage the new data is not available.


Another would be to perform the computations in filterAcceptsColumn() after detecting that the column count has changed.

This should work with column addition (and removal). However my proxy have to react on other events as well, such as (headerDataChanged, layoutChanged, modelReset). I omitted them in the example code for simplicity.