ПБ
Павел Богдевич11 января 2019 г. 7:09

Qt, Qml, QAbstractTableModel

Всем привет, часто нахожу ответы на вашем форуме. Огромное спасибо. Пролистал кучу других форумов, просмотрел документацию. Ничего не получается.
Не могу заставить чекбокс в хэдере таблицы работать с чекбоксами в колонке таблицы. Мне нужно либо выделить все, либо снять все чекбоксы. Пробовал двумя способами. Не работает. Помогите, пожалуйста.

#ifndef FOODTABLEMODEL_H
#define FOODTABLEMODEL_H

#include <QAbstractTableModel>
#include <QModelIndex>
#include <QHash>
#include <QVariant>
#include <QByteArray>
#include <QList>

struct Food
{
    bool check;
    QString description;
    int price;
    QString code;
};

class FoodTableModel : public QAbstractTableModel
{
    Q_OBJECT
public:
    enum RoleNames
    {
        CHECK = 0,
        DESCRIPTION = 1,
        PRICE = 2,
        CODE = 3
    };
    FoodTableModel(QObject *parent = nullptr);

    int rowCount(const QModelIndex &parent) const;
    int columnCount(const QModelIndex &parent) const;
    QVariant data(const QModelIndex &index, int role) const;
    QHash<int, QByteArray> roleNames() const;
    bool setData(const QModelIndex &index, const QVariant &value, int role);
    Q_INVOKABLE void checkTable(bool value);

private:
    QList<Food> foods;
};

#endif // FOODTABLEMODEL_H
#include "foodtablemodel.h"
#include <QDebug>

FoodTableModel::FoodTableModel(QObject *parent) :
    QAbstractTableModel { parent }
{
    foods.append({ false, "Pizza", 12, "ASR1" });
    foods.append({ false, "Milk", 1, "BE-23" });
    foods.append({ false, "Coffe", 4, "ARTY8-9" });
    /*foods.append({ "Lizza", "12", "ASR1" });
    foods.append({ "SMilk", "34", "BE-23" });
    foods.append({ "YCoffe", "5", "QRTY8-9" });*/
}

int FoodTableModel::rowCount(const QModelIndex &parent) const
{
    return foods.size();
}

int FoodTableModel::columnCount(const QModelIndex &parent) const
{
    return roleNames().size();
}

QVariant FoodTableModel::data(const QModelIndex &index, int role) const
{
    QVariant variant;
    const int row = index.row();
    const int col = role;
    switch (col) {
    case CHECK: variant = foods.at(row).check; break;
    case DESCRIPTION: variant = foods.at(row).description; break;
    case PRICE: variant = foods.at(row).price; break;
    case CODE: variant = foods.at(row).code; break;
    }
    return variant;
}

QHash<int, QByteArray> FoodTableModel::roleNames() const
{
    QHash<int, QByteArray> roles;
    roles.insert(CHECK, "check");
    roles.insert(DESCRIPTION, "description");
    roles.insert(PRICE, "price");
    roles.insert(CODE, "code");
    return roles;
}

bool FoodTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if(index.isValid() && role == CHECK) {
        foods[index.row()].check = value.toBool();
        emit dataChanged(index, index, { role });
        return true;
    }
    else return false;
}

void FoodTableModel::checkTable(bool value)
{
    for (int i = 0; i < foods.size(); i++) {
        qDebug() << i << value;
        foods[i].check = value;
    }
}

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "foodtablemodel.h"
#include "person.h"
#include "sortfilterproxymodel.h"

int main(int argc, char *argv[]) {
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;

    FoodTableModel food;

    qmlRegisterType<SortFilterProxyModel>("SortFilterProxyModel", 0, 1, "SortFilterProxyModel");

    engine.rootContext()->setContextProperty("food", &food);

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

import QtQuick 2.12
import QtQuick.Controls 1.4 as C1
import QtQuick.Controls.Styles 1.4
import QtQuick.Controls 2.4
import SortFilterProxyModel 0.1
import "JavaScript/TableViewCheck.js" as TVC

C1.TableView {
    id: tableView
    clip: true
    sortIndicatorVisible: true
    currentRow: rowCount ? 0 : -1
    model: SortFilterProxyModel {
        source: food.rowCount() > 0 ? food : null

        sortOrder: tableView.sortIndicatorOrder
        sortCaseSensitivity: Qt.CaseInsensitive
        sortRole: food.rowCount() && tableView.getColumn(tableView.sortIndicatorColumn).role !== "check" > 0 ?
                      tableView.getColumn(tableView.sortIndicatorColumn).role : ""
    }
    C1.TableViewColumn {
        width: 30
        role: "check"
        resizable: false
        delegate: C1.CheckBox {
            id: checkBox
            anchors.left: parent.left
            anchors.leftMargin: parent.width / 3
            checked: model.check
            onVisibleChanged: if (visible) checked = model.check
            onClicked: model.check = checked
        }
    }
    C1.TableViewColumn {
        role: "description"
        title: "Description"
    }
    C1.TableViewColumn {
        role: "price"
        title: "Price"
    }
    C1.TableViewColumn {
        role: "code"
        title: "Code"
    }
    style: TableViewStyle {
                headerDelegate: Rectangle {
                    height: textItem.implicitHeight
                    width: textItem.implicitWidth
                    Text {
                        id: textItem
                        anchors.fill: parent
                        anchors.leftMargin: 6
                        verticalAlignment: Text.AlignVCenter
                        horizontalAlignment: styleData.textAlignment
                        text: styleData.value
                    }
                    C1.CheckBox {
                        anchors.left: parent.left
                        anchors.leftMargin: parent.width / 3
                        property bool isPressed: styleData.pressed
                        visible: styleData.column === 0
                        onIsPressedChanged: {
                            if(isPressed) {
                                checked  = !checked
                                for(var i = 0; i < rowCount; i++) {
                                    food.setData(foods.index(i, 0), checked, "check")
                                }
                                //food.checkTable(checked)
                            }
                        }
                    }
                }
            }
}

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Вам это нравится? Поделитесь в социальных сетях!

11
Александр Панюшкин
  • 11 января 2019 г. 7:14
  • Ответ был помечен как решение.

Добрый день. Интересная задача. Мне тоже нужно будет что-то подобное сделать. Так что с удовольствием поковыряюсь. Но большая просьба - оформите исходники, пожалуйста. В текущем виде ими невозможно воспользоваться.

    Александр, я все исправил, но случайно ответил ваш ответ как решение. Теперь вы просто обязаны мне помочь.

      Александр Панюшкин
      • 11 января 2019 г. 7:20

      :)))) это было очень ошибочное решение, настолько поверить в меня. Но я постараюсь.

        Александр Панюшкин
        • 11 января 2019 г. 7:41

        Не увидел в исходниках описание этой модели - SortFilterProxyModel.
        Из QML кода обращение идёт к ней.

          Эта модель отвечает за сортировку колонок, поэтому я не скидывал файлы реализации.

            Александр Панюшкин
            • 11 января 2019 г. 14:12

            Несколько упросил ваш пример и сделал его рабочим - проверяйте:

            main.c

            #include <QGuiApplication>
            #include <QQmlApplicationEngine>
            #include <QQmlContext>
            
            #include "checkmodel.h"
            
            int main(int argc, char *argv[])
            {
              QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
            
              QGuiApplication app(argc, argv);
            
              QQmlApplicationEngine engine;
            
              CheckModel someModel;
              engine.rootContext()->setContextProperty("someModel", &someModel);
            
              engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
              if (engine.rootObjects().isEmpty())
                return -1;
            
              return app.exec();
            }
            

            checkmodel.h

            #ifndef CHECKMODEL_H
            #define CHECKMODEL_H
            
            #include <QAbstractTableModel>
            
            struct SomeStruct
            {
                bool check;
                QString description;
                int value;
            };
            
            class CheckModel : public QAbstractTableModel
            {
            Q_OBJECT
            
              enum Roles
                  {
                      CHECK, DESCRIPTION, VALUE
                  };
            
            public:
              CheckModel(QObject* parent = nullptr);
              int rowCount(const QModelIndex &parent = QModelIndex()) const {
                Q_UNUSED(parent); return list.count();
              }
              int columnCount(const QModelIndex &parent = QModelIndex()) const {
                Q_UNUSED(parent); return 3;
              }
              QHash<int, QByteArray> roleNames() const;
              QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
              Q_INVOKABLE void setAllChecked(bool value);
              Q_INVOKABLE void setRowChecked(int row, bool value);
            
            private:
              void fillModel();
            
              QVector<SomeStruct> list;
            };
            
            #endif // CHECKMODEL_H
            

            checkmodel.cpp

            #include "checkmodel.h"
            
            #include <QtDebug>
            
            CheckModel::CheckModel(QObject *parent) : QAbstractTableModel (parent)
            {
              fillModel();
            }
            
            QHash<int, QByteArray> CheckModel::roleNames() const
            {
              QHash<int, QByteArray> roles;
              roles.insert(Qt::UserRole+CHECK, "check");
              roles.insert(Qt::UserRole+DESCRIPTION, "description");
              roles.insert(Qt::UserRole+VALUE, "value");
              return roles;
            }
            
            QVariant CheckModel::data(const QModelIndex &index, int role) const
            {
              if (!index.isValid())
                return QVariant();
            
              SomeStruct s = list.at(index.row());
              switch (role) {
                case Qt::UserRole+CHECK: return s.check;
                case Qt::UserRole+DESCRIPTION: return s.description;
                case Qt::UserRole+VALUE: return s.value;
                }
            
              return QVariant();
            }
            
            void CheckModel::setAllChecked(bool value)
            {
              beginResetModel();
              qDebug() << 1;
              for (SomeStruct &s : list) {
                  s.check = value;
                  qDebug() << value;
                }
              endResetModel();
            }
            
            void CheckModel::setRowChecked(int row, bool value)
            {
              if (row < 0 || row >= list.count())
                return;
              SomeStruct &s = list[row];
              s.check = value;
              emit dataChanged(index(row,0), index(row, columnCount()-1));
            }
            
            void CheckModel::fillModel()
            {
              SomeStruct one = {true, "Some definition", 100};
              SomeStruct two = {false, "Some definition again", 200};
              SomeStruct three = {true, "Definition", 1000};
              list << one << two << three;
            }
            

            main.qml

            import QtQuick 2.9
            import QtQuick.Window 2.2
            import QtQuick.Controls 1.4
            
            Window {
                visible: true
                width: 640
                height: 480
            
                TableView {
                    anchors.fill: parent
            
                    model: someModel
            
                    TableViewColumn {
                        role: "check"
                        title: "Check"
                        delegate: CheckBox {
                            checked: styleData.value
                            MouseArea {
                                anchors.fill: parent
                                onClicked: {
                                    someModel.setRowChecked(styleData.row, !styleData.value)
                                }
                            }
                        }
                    }
                    TableViewColumn {
                        role: "description"
                        title: "Description"
                    }
                    TableViewColumn {
                        role: "value"
                        title: "Value"
                    }
            
                    headerDelegate: Item {
                        height: textItem.implicitHeight
            
                        Text {
                            id: textItem
                            verticalAlignment: Text.AlignVCenter
                            horizontalAlignment: styleData.textAlignment
                            text: styleData.value
                        }
                        CheckBox {
                            anchors.left: parent.left
                            anchors.leftMargin: parent.width / 3
                            property bool isPressed: styleData.pressed
                            visible: styleData.column === 0
                            onIsPressedChanged: {
                                if (isPressed) {
                                    checked  = !checked
                                    someModel.setAllChecked(checked)
                                }
                            }
                        }
                    }
                }
            }
            

            Также прикладываю исходники для удобства.
            QmlAllColumnCheckBox.zip QmlAllColumnCheckBox.zip

            На идеальный код мой вариант не претендует. Если будут замечания у вас или других более опытных коллег - буду рад услышать.
            Но в целом поставленную вами задачу он решает.

            Будут вопросы - задавайте.

              Александр Панюшкин
              • 11 января 2019 г. 14:18

              Маленькие заметки:
              - QML != JS. Старайтесь избегать использования кода javascript.
              - struct заставляет выдумывать массу не нужных костылей (enum, например, или ручное прописывание ролей) - замените его на класс, туда же можно кучу логики вынести.
              - в своем примере вы добавили в качестве модели прокси-модель - это хорошо, но я не зря просил её реализацию, т.к. в дальнейшем вы пытались обращаться к функциям модели. В таком случае вам нужно было либо сделать подобные функции в прокси, либо обращаться к модели прокси-модели.

                Александр, огромное спасибо. Я надеюсь, что у вас опыта побольше, чем у меня, и те нюансы, которые вы внесли, они обоснованы. Если у меня возникнут проблемы с объединением прокси модели и модели, которую вы реализовали, можно я буду у вас спрашивать здесь (или лучше задать новый вопрос)?

                  Александр Панюшкин
                  • 12 января 2019 г. 8:23

                  Обращайтесь, чем смогу - помогу.

                    ПБ
                    • 12 января 2019 г. 9:01
                    • (ред.)

                    Александр, как я и ожидал, проблема появилась, причем очень странная. Когда кликая по хедеру табдицы, чтобы отсортировать колонку таблицы, то выделяются все чекбоксы. Прикладываю исходники модели сортровки (Это код не мой, я его нашел на просторах интернета). Стоит объеденить модели?

                    #ifndef SORTFILTERPROXYMODEL_H
                    #define SORTFILTERPROXYMODEL_H
                    
                    #include <QtCore/qsortfilterproxymodel.h>
                    #include <QtQml/qjsvalue.h>
                    
                    class SortFilterProxyModel : public QSortFilterProxyModel
                    {
                        Q_OBJECT
                        Q_PROPERTY(int count READ count NOTIFY countChanged)
                        Q_PROPERTY(QObject *source READ source WRITE setSource)
                    
                        Q_PROPERTY(QByteArray sortRole READ sortRole WRITE setSortRole)
                        Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder)
                    
                    public:
                        explicit SortFilterProxyModel(QObject *parent = 0);
                    
                        QObject *source() const;
                        void setSource(QObject *source);
                    
                        QByteArray sortRole() const;
                        void setSortRole(const QByteArray &role);
                    
                        void setSortOrder(Qt::SortOrder order);
                    
                        int count() const;
                        Q_INVOKABLE QJSValue get(int index) const;
                    
                    signals:
                        void countChanged();
                    
                    protected:
                        int roleKey(const QByteArray &role) const;
                        QHash<int, QByteArray> roleNames() const;
                    };
                    
                    #endif // SORTFILTERPROXYMODEL_H
                    
                    
                    #include "sortfilterproxymodel.h"
                    #include <QtDebug>
                    #include <QtQml>
                    
                    SortFilterProxyModel::SortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
                    {
                        connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(countChanged()));
                        connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(countChanged()));
                    }
                    
                    int SortFilterProxyModel::count() const
                    {
                        return rowCount();
                    }
                    
                    QObject *SortFilterProxyModel::source() const
                    {
                        return sourceModel();
                    }
                    
                    void SortFilterProxyModel::setSource(QObject *source)
                    {
                        setSourceModel(qobject_cast<QAbstractItemModel *>(source));
                    }
                    
                    QByteArray SortFilterProxyModel::sortRole() const
                    {
                        return roleNames().value(QSortFilterProxyModel::sortRole());
                    }
                    
                    void SortFilterProxyModel::setSortRole(const QByteArray &role)
                    {
                        QSortFilterProxyModel::setSortRole(roleKey(role));
                    }
                    
                    void SortFilterProxyModel::setSortOrder(Qt::SortOrder order)
                    {
                        QSortFilterProxyModel::sort(0, order);
                    }
                    
                    QJSValue SortFilterProxyModel::get(int idx) const
                    {
                        QJSEngine *engine = qmlEngine(this);
                        QJSValue value = engine->newObject();
                        if (idx >= 0 && idx < count()) {
                            QHash<int, QByteArray> roles = roleNames();
                            QHashIterator<int, QByteArray> it(roles);
                            while (it.hasNext()) {
                                it.next();
                                value.setProperty(QString::fromUtf8(it.value()), data(index(idx, 0), it.key()).toString());
                            }
                        }
                        return value;
                    }
                    
                    int SortFilterProxyModel::roleKey(const QByteArray &role) const
                    {
                        QHash<int, QByteArray> roles = roleNames();
                        QHashIterator<int, QByteArray> it(roles);
                        while (it.hasNext()) {
                            it.next();
                            if (it.value() == role)
                                return it.key();
                        }
                        return -1;
                    }
                    
                    QHash<int, QByteArray> SortFilterProxyModel::roleNames() const
                    {
                        if (QAbstractItemModel *source = sourceModel())
                            return source->roleNames();
                        return QHash<int, QByteArray>();
                    }
                    
                    
                    
                    

                    Это измененное место инициализации модели в файле main.qml

                    model: SortFilterProxyModel {
                            source: someModel
                            sortOrder: tableView.sortIndicatorOrder
                            sortCaseSensitivity: Qt.CaseInsensitive
                            sortRole: someModel.rowCount() > 0 && tableView.getColumn(tableView.sortIndicatorColumn).role !== "check" ?
                                          tableView.getColumn(tableView.sortIndicatorColumn).role : ""
                        }
                    

                    Дописал вопрос, и понял. Выделение должно происходить, когда мы кликаем по чекбоксу в хедере, а оно происходит при нажатии в любом месте хедера.
                    Вот решение.

                    onIsPressedChanged: {
                                    if(isPressed && styleData.column === 0) {
                                        checked  = !checked
                                        food.setAllChecked(checked)
                                        }
                                    }
                                }
                    
                      Serj Demchenko
                      • 4 августа 2021 г. 11:20

                      Qt не поддерживает чекбоксы в хедерах таблицы, чтобы это сделать
                      Попробуйте создать класс отнаследованный от QHeaderView и в нем переопределить метод data ( if(role==CheckStateRole и т.д.)
                      и добавить сигнал (checkedChanged)

                      HeaderClass{
                      id:header
                      }

                      // in tableView
                      delegate: Checkbox {
                      id: headerDelegate
                      checked: header.checked
                      onCheckedChanged: headerDelegate.checked = checked // пишем состояние куда надо
                      }

                      или засунуть в роли модели роль 'HeaderCheckedState'
                      и брать оттуда

                        Комментарии

                        Только авторизованные пользователи могут публиковать комментарии.
                        Пожалуйста, авторизуйтесь или зарегистрируйтесь
                        SH
                        • Sak Hax
                        • 26 апреля 2024 г. 0:00

                        C++ - Тест 001. Первая программа и типы данных

                        • Результат:33баллов,
                        • Очки рейтинга-10
                        г
                        • ги
                        • 24 апреля 2024 г. 1:51

                        C++ - Тест 005. Структуры и Классы

                        • Результат:41баллов,
                        • Очки рейтинга-8
                        l
                        • laei
                        • 23 апреля 2024 г. 19:19

                        C++ - Тест 004. Указатели, Массивы и Циклы

                        • Результат:10баллов,
                        • Очки рейтинга-10
                        Последние комментарии
                        k
                        kmssr9 февраля 2024 г. 5:43
                        Qt Linux - Урок 001. Автозапуск Qt приложения под Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
                        АК
                        Анатолий Кононенко5 февраля 2024 г. 12:50
                        Qt WinAPI - Урок 007. Работаем с ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
                        EVA
                        EVA25 декабря 2023 г. 21:30
                        Boost - статическая линковка в CMake проекте под Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
                        J
                        JonnyJo25 декабря 2023 г. 19:38
                        Boost - статическая линковка в CMake проекте под Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
                        G
                        Gvozdik19 декабря 2023 г. 8:01
                        Qt/C++ - Урок 056. Подключение библиотеки Boost в Qt для компиляторов MinGW и MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
                        Сейчас обсуждают на форуме
                        G
                        Gar22 апреля 2024 г. 15:46
                        Clipboard Как скопировать окно целиком в clipb?
                        DA
                        Dr Gangil Academics20 апреля 2024 г. 17:45
                        Unlock Your Aesthetic Potential: Explore MSC in Facial Aesthetics and Cosmetology in India Embark on a transformative journey with an msc in facial aesthetics and cosmetology in india . Delve into the intricate world of beauty and rejuvenation, guided by expert faculty and …
                        a
                        a_vlasov14 апреля 2024 г. 16:41
                        Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
                        Павел Дорофеев
                        Павел Дорофеев14 апреля 2024 г. 12:35
                        QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
                        f
                        fastrex4 апреля 2024 г. 14:47
                        Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…

                        Следите за нами в социальных сетях