HorizontalHeaderView not reading data from headerData
-
I have the following constellation:
A QuestionSqlTableModel model which is based on QSqlTableModel.
A QuestionsProxyModel model which is based on QIdentityProxyModel
In Qml I expose QuestionsProxyModel it should work as an adaptor to adapt the role names in QML to the SQL columns in the QSqlTableModel.
Now this already looks like it works fine. I see the correct data of the QSqlTableModel in QML. However what does not work anymore is the
HorizontalHeaderView
.In qml I disply the data like this in a tableView:
import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Window 2.15 import Qt.labs.qmlmodels 1.0 import "data" Item { id: root HorizontalHeaderView { id: horizontalHeaderView syncView: tableView anchors.left: tableView.left } TableView { id: tableView width: parent.width height: parent.height - horizontalHeaderView.height anchors.top: horizontalHeaderView.bottom boundsBehavior: Flickable.StopAtBounds reuseItems: true clip: true property var columnWidths: [60, 220, 220, 220, 220, 220, 100, 140] columnWidthProvider: function (column) { return columnWidths[column] } model: questionsProxyModel delegate: // Various delegates with Delegate Chooser .... ScrollBar.vertical: ScrollBar {} } }
Now on start I get these errors with HorizontalHeaderView:
file:///home/sandro/Qt/5.15.0/gcc_64/qml/QtQuick/Controls.2/Universal/HorizontalHeaderView.qml:60:13: Unable to assign [undefined] to QString
The QuestionsProxyModel which is based on
QIdentityProxyModel
provides the header data like this:#ifndef QUESTIONSPROXYMODEL_H #define QUESTIONSPROXYMODEL_H #include <QObject> #include <QIdentityProxyModel> class QuestionsProxyModel : public QIdentityProxyModel { Q_OBJECT enum questionRoles { idRole = Qt::UserRole + 1, askedQuestionRole, answer1Role, answer2Role, answer3Role, answer4Role, correctAnswerRole, pictureRole }; public: QuestionsProxyModel(QObject* parent = nullptr); QHash<int, QByteArray> roleNames() const override; Q_INVOKABLE QVariant headerData(int section, Qt::Orientation orientation, int role) const override; Q_INVOKABLE QVariant data(const QModelIndex &index, int role) const override; private: QModelIndex mapIndex(const QModelIndex &index, int role) const; }; #endif // QUESTIONSPROXYQML_H
#include "../include/questionsproxymodel.h" #include "../include/questionsqlcolumnnames.h" #include <QPixmap> #include <QBuffer> #include <QByteArray> QuestionsProxyModel::QuestionsProxyModel(QObject* parent) :QIdentityProxyModel(parent) { } QHash<int, QByteArray> QuestionsProxyModel::roleNames() const { QHash <int,QByteArray> roles; roles[idRole] = "id"; roles[askedQuestionRole] = "askedQuestion"; roles[answer1Role] = "answer1"; roles[answer2Role] = "answer2"; roles[answer3Role] = "answer3"; roles[answer4Role] = "answer4"; roles[correctAnswerRole] = "correctAnswer"; roles[pictureRole] = "picture"; return roles; } QVariant QuestionsProxyModel::headerData(int section, Qt::Orientation orientation, int role) const { if(orientation != Qt::Orientation::Horizontal) { return QVariant{}; } if(role == Qt::DisplayRole) { switch (section) { case 0: return tr("Id"); case 1: return tr("Question"); case 2: return tr("Answer 1"); case 3: return tr("Answer 2"); case 4: return tr("Answer 3"); case 5: return tr("Answer 4"); case 6: return tr("Correct Answer"); case 7: return tr("Picture"); } } return QVariant{}; } QVariant QuestionsProxyModel::data(const QModelIndex &index, int role) const { QModelIndex newIndex = mapIndex(index, role); if (role == idRole || role == askedQuestionRole || role == answer1Role || role == answer2Role || role == answer3Role || role == answer4Role || role == correctAnswerRole || role == pictureRole) { return QIdentityProxyModel::data(newIndex, Qt::DisplayRole); } return QIdentityProxyModel::data(newIndex, role); } QModelIndex QuestionsProxyModel::mapIndex(const QModelIndex &source, int role) const { switch(role) { case idRole: return createIndex(source.row(), QuestionColumn::id); case askedQuestionRole: return createIndex(source.row(), QuestionColumn::askedQuestion); case answer1Role: return createIndex(source.row(), QuestionColumn::answer1); case answer2Role: return createIndex(source.row(), QuestionColumn::answer2); case answer3Role: return createIndex(source.row(), QuestionColumn::answer3); case answer4Role: return createIndex(source.row(), QuestionColumn::answer4); case correctAnswerRole: return createIndex(source.row(), QuestionColumn::correct_answer); case pictureRole: return createIndex(source.row(), QuestionColumn::picture); } return source; }
I assumed that horizontalHeaderView calls
QuestionsProxyModel::headerData
but it does not even call it somehow.The full code can be found here:
https://github.com/SandroWissmann/Quiz/tree/Editable-Question-Table -
I think I found the reason why it is not working. The Qt docs state this for the model of headerView:
If the model is a QAbstractTableModel, then the header will display the model's horizontal headerData(); otherwise, the model's data().
So how do I have to implement data to show the headers? I tried this:
QVariant QuestionsProxyModel::data(const QModelIndex &index, int role) const { if(role == Qt::DisplayRole) { switch (index.column()) { case 0: return tr("Id"); case 1: return tr("Question"); case 2: return tr("Answer 1"); case 3: return tr("Answer 2"); case 4: return tr("Answer 3"); case 5: return tr("Answer 4"); case 6: return tr("Correct Answer"); case 7: return tr("Picture"); } } QModelIndex newIndex = mapIndex(index, role); if (role == idRole || role == askedQuestionRole || role == answer1Role || role == answer2Role || role == answer3Role || role == answer4Role || role == correctAnswerRole || role == pictureRole) { return QIdentityProxyModel::data(newIndex, Qt::DisplayRole); } return QIdentityProxyModel::data(newIndex, role); }
With the same result as before.
The hacky workarround was to set an own model on headerView with the names:
HorizontalHeaderView { id: horizontalHeaderView syncView: tableView anchors.left: tableView.left model: [qsTr("Id"), qsTr("Question"), qsTr("Answer 1"), qsTr( "Answer 2"), qsTr("Answer 3"), qsTr("Answer 4"), qsTr( "Correct Answer"), qsTr("Picture")] }