Замена заголовков в QSqlQueryModel (транспонирование таблицы)

У меня возникла проблема с правильным переносом таблицы, которую я получаю из БД. Я пошел по пути, найденному здесь, и в итоге получил подкласс QAbstractProxyModel — как описано здесь. к сожалению, это не полностью работает, вот в чем проблема:

Что я имею:

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

Что я хочу:

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

Что я получаю:

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

Как видите, данные транспонируются правильно, но заголовки портятся... А они мне очень нужны :(

Я попытался вручную установить данные заголовка, но это также не удалось:

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
}

Независимо от того, пробую ли я № 1 или № 2, вызов setHeaderData оценивается как false...

Любые идеи?

исправлено, как указал @Vinícius Gobbo A. de Oliveira


person murison    schedule 26.09.2014    source источник
comment
Сейчас поздняя ночь... но, если я правильно понимаю, то, что вы хотите, и то, что вы получаете, одно и то же. Может быть, вы хотите проверить это ;D   -  person Vinícius Gobbo A. de Oliveira    schedule 26.09.2014
comment
Да, я ошибся в том, чего на самом деле добился. проблема в том, что я получаю один и тот же заголовок во всех столбцах (из первого столбца) и во всех строках (из первой строки). И я не могу его изменить - даже вручную...   -  person murison    schedule 26.09.2014
comment
Эй, ссылка в вашем посте, к сожалению, мертва, можете ли вы опубликовать свой полный рабочий код для дальнейшего использования?   -  person i know nothing    schedule 26.08.2015
comment
@я-не знаю-ничего, посмотри принятый ответ   -  person murison    schedule 27.08.2015


Ответы (2)


Ну, если вы использовали пример кода, который вы связали, вы должны переопределить определение по умолчанию для метода headerData класса TransposeProxyModel, как это:

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

Забудьте о методах setHeaderData и setData: вам нужна прокси-модель, основанная на исходной!

Ну, не знаю точно, что не так, какую версию Qt вы используете? Попробуйте этот код, здесь он отлично работает:

#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();
}

Большая часть кода взята из предоставленной вами ссылки, я только что написал метод headerData.

person Albert    schedule 26.09.2014
comment
Пробовал так делать (написал идентичный код) - не помогло. Ставлю внутрь брейкпоинт - никогда не срабатывает. - person murison; 26.09.2014
comment
Боже мой, я идиот... Мой код был ПОЧТИ идентичным... Я пропустил const в объявлении функции... - person murison; 27.09.2014

Забавно, но в doc указано, что это виртуальный метод:

виртуальный QVariant headerData (раздел int, ориентация Qt::Orientation, роль int) const

(...)

QVariant QAbstractProxyModel::headerData(раздел int, ориентация Qt::Orientation, роль int) const [виртуальный]

Реализован из QAbstractItemModel::headerData().

но в самом заголовке ключевое слово virtual отсутствует:

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

Я проверил в обычном каталоге Qt, и он также отсутствует:

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;

Я сделал тест:

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);
}

И отладка:

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') 

Так что я думаю, что проблема в том, что метод не абстрактный, и поэтому он не вызывает мой переопределенный метод. Qt ошибка? или это специально? я тоже заглянула

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);
}

Я предполагаю, что в любом случае он должен получить правильный заголовок - он вызывает mapToSource, который является чисто виртуальным, и это должно получить правильный индекс?

person murison    schedule 26.09.2014
comment
В моей настройке виртуальный находится на месте, из C:\Qt\Qt5.2.1\5.2.1\msvc2010_opengl\include\QtCore\qabstractitemmodel.h, строки 188-189, это: виртуальный QVariant headerData(int section, Qt::Orientation ориентация, int role = Qt::DisplayRole) const... Так что все в порядке. - person Albert; 26.09.2014
comment
Я неправильно написал имя файла. Не могли бы вы проверить qabstractproxymodel.h? - person murison; 26.09.2014
comment
Привет, он не объявлен виртуальным в QAbstractProxyModel, но он есть в QAbstractItemModel, который является его базовым классом, так что все в порядке. Диди, попробуй полный код из моего ответа, я только что проверил здесь с другой настройкой Qt, и он все еще работает отлично. Я думаю, что у вас есть правильное решение. - person Albert; 26.09.2014
comment
О вашем последнем вопросе: реализация по умолчанию не может работать должным образом в вашем случае, потому что, даже используя хороший способ сопоставления заголовков, как если бы это были индексы данных, он все еще ищет в исходной модели с исходной ориентацией, в то время как вы обязательно нужно поменять местами горизонтальную и вертикальную адресацию данных. - person Albert; 27.09.2014