ПБ
Павел БогдевичJan. 11, 2019, 7: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, 7:14 a.m.
  • The answer was marked as a solution.

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

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

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

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

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

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

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

            Александр Панюшкин
            • Jan. 11, 2019, 2: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, 2:18 p.m.

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

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

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

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

                    ПБ
                    • Jan. 12, 2019, 9: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, 11: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
                        AD

                        C ++ - Test 004. Pointers, Arrays and Loops

                        • Result:50points,
                        • Rating points-4
                        m

                        C ++ - Test 004. Pointers, Arrays and Loops

                        • Result:80points,
                        • Rating points4
                        m

                        C ++ - Test 004. Pointers, Arrays and Loops

                        • Result:20points,
                        • Rating points-10
                        Last comments
                        ИМ
                        Игорь МаксимовNov. 22, 2024, 7:51 p.m.
                        Django - Tutorial 017. Customize the login page to Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
                        Evgenii Legotckoi
                        Evgenii LegotckoiOct. 31, 2024, 9:37 p.m.
                        Django - Lesson 064. How to write a Python Markdown extension Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
                        A
                        ALO1ZEOct. 19, 2024, 3:19 p.m.
                        Fb3 file reader on Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
                        ИМ
                        Игорь МаксимовOct. 5, 2024, 2:51 p.m.
                        Django - Lesson 064. How to write a Python Markdown extension Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
                        d
                        dblas5July 5, 2024, 6:02 p.m.
                        QML - Lesson 016. SQLite database and the working with it in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
                        Now discuss on the forum
                        Evgenii Legotckoi
                        Evgenii LegotckoiJune 24, 2024, 10:11 p.m.
                        добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
                        t
                        tonypeachey1Nov. 15, 2024, 2:04 p.m.
                        google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
                        NSProject
                        NSProjectJune 4, 2022, 10:49 a.m.
                        Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
                        9
                        9AnonimOct. 25, 2024, 4:10 p.m.
                        Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

                        Follow us in social networks