ПБ
Павел БогдевичҚаң. 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'
                      и брать оттуда

                        Пікірлер

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

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

                        • Нәтиже:50ұпай,
                        • Бағалау ұпайлары-4
                        m
                        • molni99
                        • Қаз. 26, 2024, 1:37 Т.Ж.

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

                        • Нәтиже:80ұпай,
                        • Бағалау ұпайлары4
                        m
                        • molni99
                        • Қаз. 26, 2024, 1:29 Т.Ж.

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

                        • Нәтиже:20ұпай,
                        • Бағалау ұпайлары-10
                        Соңғы пікірлер
                        ИМ
                        Игорь МаксимовҚар. 22, 2024, 11:51 Т.Ж.
                        Django - Оқулық 017. Теңшелген Django кіру беті Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
                        Evgenii Legotckoi
                        Evgenii LegotckoiҚаз. 31, 2024, 2:37 Т.Қ.
                        Django - Сабақ 064. Python Markdown кеңейтімін қалай жазуға болады Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
                        A
                        ALO1ZEҚаз. 19, 2024, 8:19 Т.Ж.
                        Qt Creator көмегімен fb3 файл оқу құралы Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
                        ИМ
                        Игорь МаксимовҚаз. 5, 2024, 7:51 Т.Ж.
                        Django - Сабақ 064. Python Markdown кеңейтімін қалай жазуға болады Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
                        d
                        dblas5Шілде 5, 2024, 11:02 Т.Ж.
                        QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
                        Енді форумда талқылаңыз
                        Evgenii Legotckoi
                        Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
                        добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
                        t
                        tonypeachey1Қар. 15, 2024, 6:04 Т.Ж.
                        google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
                        NSProject
                        NSProjectМаусым 4, 2022, 3:49 Т.Ж.
                        Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
                        9
                        9AnonimҚаз. 25, 2024, 9:10 Т.Ж.
                        Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

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