ПБ
Павел БогдевичҚаң. 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, 2: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, 2:18 Т.Қ.

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

                ПБ
                • Қаң. 12, 2019, 8:13 Т.Ж.
                • (өңделген)

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

                  Александр Панюшкин
                  • Қаң. 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'
                      и брать оттуда

                        Пікірлер

                        Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
                        Кіріңіз немесе Тіркеліңіз
                        Г

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

                        • Нәтиже:66ұпай,
                        • Бағалау ұпайлары-1
                        t

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

                        • Нәтиже:33ұпай,
                        • Бағалау ұпайлары-10
                        t

                        Qt - Тест 001. Сигналы и слоты

                        • Нәтиже:52ұпай,
                        • Бағалау ұпайлары-4
                        Соңғы пікірлер
                        G
                        GoattRockҚыр. 3, 2024, 1:50 Т.Қ.
                        Linux жүйесінде файлдарды қалай көшіруге болады Задумывались когда-нибудь о том, как мы привыкли доверять свои вещи службам грузоперевозок? Сейчас такие услуги стали неотъемлемой частью нашей жизни, особенно когда речь идет о переездах между …
                        d
                        dblas5Шілде 5, 2024, 11:02 Т.Ж.
                        QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
                        k
                        kmssrАқп. 8, 2024, 6:43 Т.Қ.
                        Qt Linux - Сабақ 001. Linux астында Autorun Qt қолданбасы как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
                        АК
                        Анатолий КононенкоАқп. 5, 2024, 1:50 Т.Ж.
                        Qt WinAPI - Сабақ 007. Qt ішінде ICMP Ping арқылы жұмыс істеу Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
                        Енді форумда талқылаңыз
                        Evgenii Legotckoi
                        Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
                        добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
                        F
                        FynjyШілде 22, 2024, 4:15 Т.Ж.
                        при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …
                        BlinCT
                        BlinCTМаусым 25, 2024, 1 Т.Ж.
                        Нарисовать кривую в qml Всем привет. Имеется Лист листов с тосками, точки получаны интерполяцией Лагранжа. Вопрос, как этими точками нарисовать кривую? ChartView отпадает сразу, в qt6.7 появился новый элемент…
                        BlinCT
                        BlinCTМамыр 5, 2024, 5:46 Т.Ж.
                        Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
                        Evgenii Legotckoi
                        Evgenii LegotckoiМамыр 2, 2024, 2:07 Т.Қ.
                        Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.

                        Бізді әлеуметтік желілерде бақылаңыз