Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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")]
    }

Log in to reply