ПБ
Павел БогдевичJan. 11, 2019, 6:09 a.m.

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

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Do you like it? Share on social networks!

11
Александр Панюшкин
  • Jan. 11, 2019, 6:14 a.m.
  • The answer was marked as a solution.

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

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

      Александр Панюшкин
      • Jan. 11, 2019, 6:20 a.m.

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

        Александр Панюшкин
        • Jan. 11, 2019, 6:41 a.m.

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

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

            Александр Панюшкин
            • Jan. 11, 2019, 1:12 p.m.

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

            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

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

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

              Александр Панюшкин
              • Jan. 11, 2019, 1:18 p.m.

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

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

                  Александр Панюшкин
                  • Jan. 12, 2019, 7:23 a.m.

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

                    ПБ
                    • Jan. 12, 2019, 8:01 a.m.
                    • (edited)

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

                    #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
                      • Aug. 4, 2021, 10:20 a.m.

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

                      HeaderClass{
                      id:header
                      }

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

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

                        Comments

                        Only authorized users can post comments.
                        Please, Log in or Sign up
                        B

                        C++ - Test 002. Constants

                        • Result:16points,
                        • Rating points-10
                        B

                        C++ - Test 001. The first program and data types

                        • Result:46points,
                        • Rating points-6
                        FL

                        C++ - Test 006. Enumerations

                        • Result:80points,
                        • Rating points4
                        Last comments
                        k
                        kmssrFeb. 9, 2024, 5:43 a.m.
                        Qt Linux - Lesson 001. Autorun Qt application under Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
                        Qt WinAPI - Lesson 007. Working with ICMP Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
                        EVA
                        EVADec. 25, 2023, 9:30 p.m.
                        Boost - static linking in CMake project under Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
                        J
                        JonnyJoDec. 25, 2023, 7:38 p.m.
                        Boost - static linking in CMake project under Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
                        G
                        GvozdikDec. 19, 2023, 8:01 a.m.
                        Qt/C++ - Lesson 056. Connecting the Boost library in Qt for MinGW and MSVC compilers Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
                        Now discuss on the forum
                        AC
                        Alexandru CodreanuJan. 19, 2024, 10:57 p.m.
                        QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…
                        BlinCT
                        BlinCTDec. 27, 2023, 7:57 p.m.
                        Растягивать Image на парент по высоте Ну и само собою дял включения scrollbar надо чтобы был Flickable. Так что выходит как то так Flickable{ id: root anchors.fill: parent clip: true property url linkFile p…
                        Дмитрий
                        ДмитрийJan. 10, 2024, 3:18 p.m.
                        Qt Creator загружает всю оперативную память Проблема решена. Удалось разобраться с помощью утилиты strace. Запустил ее: strace ./qtcreator Начал выводиться весь лог работы креатора. В один момент он начал считывать фай…
                        Evgenii Legotckoi
                        Evgenii LegotckoiDec. 12, 2023, 5:48 p.m.
                        Побуквенное сравнение двух строк Добрый день. Там случайно не высылается этот сигнал textChanged ещё и при форматировани текста? Если решиать в лоб, то можно просто отключать сигнал/слотовое соединение внутри слота и …

                        Follow us in social networks