1

I am having a problem to properly transpose the table I recieve from db. I followed the path found here , and ended up in subclassing a QAbstractProxyModel - like described here . unfortunatelly, it doesn't fully work, here's where the problem is:

What I have:

X | A  | B
----------
1 | A1 | B1
2 | A2 | B2

What I want:

X | 1  | 2
----------
A | A1 | A2
B | B1 | B2

What I get:

X | 1  | 1
----------
A | A1 | A2
A | B1 | B2

So as you can see, the data is correctly transposed, but the headers get bad... And I really need them :(

I tried to manually set header data, but it also failed:

origModel = new QSqlQueryModel; // set query and so on
transposedModel = new TransposeProxyModel;
transposedModel->setSourceModel(origModel );
for (int i = 0; i < origModel->columnCount(); i++) {
    qDebug() << "origModel->Qt::Horizontal(" << i << ")" << origModel->headerData(i, Qt::Horizontal, Qt::DisplayRole);
    //transposedModel->setHeaderData(i, Qt::Vertical, origModel->headerData(i, Qt::Horizontal, Qt::DisplayRole), Qt::DisplayRole); //#try1
    transposedModel->setHeaderData(i, Qt::Vertical, QVariant( "abc" ), Qt::DisplayRole); // #try2
}

No matter if I try #1, or #2 - call to setHeaderData evaluates to false...

Any ideas?

fixed as @Vinícius Gobbo A. de Oliveira pointed

murison
  • 3,640
  • 2
  • 23
  • 36

2 Answers2

4

Well, if you used the example code you linked you should override the default definition for the headerData method of the TransposeProxyModel class, just like this:

QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const {
    return sourceModel()->headerData(section, (orientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal), role);
}

Forget about the setHeaderData and setData methods: you want a proxy model that rely on the original one!

Well, don't know exactly what's wrong, which Qt release are you using? Try this code, it works perfectly here:

#include <QtCore>
#include <QtWidgets>

class TransposeProxyModel: public QAbstractProxyModel {
public:
    TransposeProxyModel(QObject *p = 0):
        QAbstractProxyModel(p)
    {
    }
    QModelIndex mapFromSource ( const QModelIndex & sourceIndex ) const{
        return index(sourceIndex.column(), sourceIndex.row());
    }
    QModelIndex mapToSource ( const QModelIndex & proxyIndex ) const{
        return sourceModel()->index(proxyIndex.column(), proxyIndex.row());
    }
    QModelIndex index(int r, int c, const QModelIndex &ind=QModelIndex()) const{
        return createIndex(r,c);
    }
    QModelIndex parent(const QModelIndex&) const {
        return QModelIndex();
    }
    int rowCount(const QModelIndex &) const{
        return sourceModel()->columnCount();
    }
    int columnCount(const QModelIndex &) const{
        return sourceModel()->rowCount();
    }
    QVariant data(const QModelIndex &ind, int role) const {
        return sourceModel()->data(mapToSource(ind), role);
    }
    QVariant headerData(int section, Qt::Orientation orientation,
        int role = Qt::DisplayRole) const {
        return sourceModel()->headerData(section,
            (orientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal),
            role);
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QStandardItemModel model(3,3);
    model.setData(model.index(0,0), "1");
    model.setData(model.index(0,1), "2");
    model.setData(model.index(0,2), "3");
    model.setData(model.index(1,0), "4");
    model.setData(model.index(1,1), "5");
    model.setData(model.index(1,2), "6");
    model.setData(model.index(2,0), "7");
    model.setData(model.index(2,1), "8");
    model.setData(model.index(2,2), "9");
    model.setHeaderData(0, Qt::Horizontal, "a");
    model.setHeaderData(1, Qt::Horizontal, "b");
    model.setHeaderData(2, Qt::Horizontal, "c");
    TransposeProxyModel trans;
    trans.setSourceModel(&model);
    QSplitter split;
    QTableView *t1 = new QTableView(&split);
    t1->setModel(&model);
    QTableView *t2 = new QTableView(&split);
    t2->setModel(&trans);
    split.show();
    return a.exec();
}

Most of the code came from the link you provided, I just wrote the headerData method.

pragmanomos
  • 976
  • 8
  • 9
  • I tried doing that (wrote identical code) - didn't help . I put a brakepoint inside - it never triggers. – murison Sep 26 '14 at 12:34
  • My God I'm an idiot... My code was ALMOST identical... I missed `const` in function declaration... – murison Sep 26 '14 at 20:18
0

Funny thing though - the doc states that this is a virtual method:

virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const

(...)

QVariant QAbstractProxyModel::headerData(int section, Qt::Orientation orientation, int role) const [virtual]

Reimplemented from QAbstractItemModel::headerData().

but in the actual headerthe virtual keyword is missing:

C:\Qt\Qt5.2.1\5.2.1\msvc2010\include\QtCore\qabstractproxymodel.h

(...)
QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const; //line76
QVariant headerData(int section, Qt::Orientation orientation, int role) const; //line77

I checked in normal Qt dir and it is also missing:

C:\Qt\Qt5.2.1\5.2.1\Src\qtbase\src\corelib\itemmodels\qabstractproxymodel.h

(...)
QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;

I have made a test:

sim = new QStandardItemModel (3,3);

for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++ ) {
        sim->setData(sim->index(i,j), QString("%1%2").arg(QChar(j+65), QString().setNum(i+1)));
        if (i==0)
            sim->setHeaderData(j, Qt::Horizontal, QChar(j+65));
    }
    sim->setHeaderData(i, Qt::Vertical, i+1);
}

transposedModel = new TransposeProxyModel;
transposedModel->setSourceModel(sim);

QAbstractItemModel * aim = transposedModel;
QAbstractProxyModel *apm = transposedModel;

for (int i = 0; i < apm->rowCount(); i++) {
    qDebug() << "aim->Qt::Vertical(" << i << ")" << aim->headerData(i, Qt::Vertical, Qt::DisplayRole);
    qDebug() << "apm->Qt::Vertical(" << i << ")" << apm->headerData(i, Qt::Vertical, Qt::DisplayRole);
    qDebug() << "transposedModel->Qt::Vertical(" << i << ")" << transposedModel->headerData(i, Qt::Vertical, Qt::DisplayRole);
}

And the debug is:

aim->Qt::Vertical( 0 ) QVariant(int, 1) 
apm->Qt::Vertical( 0 ) QVariant(int, 1) 
[ TransposeProxyModel::headerData ] //qDebug in TransposeProxyModel::headerData
transposedModel->Qt::Vertical( 0 ) QVariant(QChar, 'A') 
aim->Qt::Vertical( 1 ) QVariant(int, 1) 
apm->Qt::Vertical( 1 ) QVariant(int, 1) 
[ TransposeProxyModel::headerData ] //qDebug in TransposeProxyModel::headerData
transposedModel->Qt::Vertical( 1 ) QVariant(QChar, 'B') 
aim->Qt::Vertical( 2 ) QVariant(int, 1) 
apm->Qt::Vertical( 2 ) QVariant(int, 1) 
[ TransposeProxyModel::headerData ] //qDebug in TransposeProxyModel::headerData
transposedModel->Qt::Vertical( 2 ) QVariant(QChar, 'C') 

So I guess that's the problem - the method isn't abstract, and that's why it doesn't call my overridden method. Qt bug ? or is that on purpose? I also took a peek into

C:\Qt\Qt5.2.1\5.2.1\Src\qtbase\src\corelib\itemmodels\qabstractproxymodel.cpp

/*!
    \reimp
 */
QVariant QAbstractProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    Q_D(const QAbstractProxyModel);
    int sourceSection;
    if (orientation == Qt::Horizontal) {
        const QModelIndex proxyIndex = index(0, section);
        sourceSection = mapToSource(proxyIndex).column();
    } else {
        const QModelIndex proxyIndex = index(section, 0);
        sourceSection = mapToSource(proxyIndex).row();
    }
    return d->model->headerData(sourceSection, orientation, role);
}

I guess it should get proper header anyway - it calls mapToSource, which is pure virtual, and this should obtain the correct index?

IceFire
  • 4,016
  • 2
  • 31
  • 51
murison
  • 3,640
  • 2
  • 23
  • 36
  • 1
    In my setup virtual is in place, from C:\Qt\Qt5.2.1\5.2.1\msvc2010_opengl\include\QtCore\qabstractitemmodel.h, lines 188-189, it's: virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const... So perfectly fine. – pragmanomos Sep 26 '14 at 15:09
  • I misspelled the filename - could You please check qabstractproxymodel.h ? – murison Sep 26 '14 at 17:58
  • 1
    Hi, it's not declared virtual in QAbstractProxyModel, but it is in QAbstractItemModel, which is its base class, so everythings is fine. Didi you try the full code from my answer, I just checked here with another Qt setup and it still works perfectly fine. I think you have the right solution there. – pragmanomos Sep 26 '14 at 19:33
  • 1
    About your last question: the default implementation can't work properly in your case, because, even by using its nice way to map headers as if the were data indexes, it is still "searching" in the original model with the original orientation, while you necessarily need to swap horizontal and vertical data addressing. – pragmanomos Sep 26 '14 at 20:17