ПБ
Jan. 11, 2019, 6:09 p.m.

Qt, Qml, QAbstractTableModel

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

  1. #ifndef FOODTABLEMODEL_H
  2. #define FOODTABLEMODEL_H
  3.  
  4. #include <QAbstractTableModel>
  5. #include <QModelIndex>
  6. #include <QHash>
  7. #include <QVariant>
  8. #include <QByteArray>
  9. #include <QList>
  10.  
  11. struct Food
  12. {
  13. bool check;
  14. QString description;
  15. int price;
  16. QString code;
  17. };
  18.  
  19. class FoodTableModel : public QAbstractTableModel
  20. {
  21. Q_OBJECT
  22. public:
  23. enum RoleNames
  24. {
  25. CHECK = 0,
  26. DESCRIPTION = 1,
  27. PRICE = 2,
  28. CODE = 3
  29. };
  30. FoodTableModel(QObject *parent = nullptr);
  31.  
  32. int rowCount(const QModelIndex &parent) const;
  33. int columnCount(const QModelIndex &parent) const;
  34. QVariant data(const QModelIndex &index, int role) const;
  35. QHash<int, QByteArray> roleNames() const;
  36. bool setData(const QModelIndex &index, const QVariant &value, int role);
  37. Q_INVOKABLE void checkTable(bool value);
  38.  
  39. private:
  40. QList<Food> foods;
  41. };
  42.  
  43. #endif // FOODTABLEMODEL_H
  1. #include "foodtablemodel.h"
  2. #include <QDebug>
  3.  
  4. FoodTableModel::FoodTableModel(QObject *parent) :
  5. QAbstractTableModel { parent }
  6. {
  7. foods.append({ false, "Pizza", 12, "ASR1" });
  8. foods.append({ false, "Milk", 1, "BE-23" });
  9. foods.append({ false, "Coffe", 4, "ARTY8-9" });
  10. /*foods.append({ "Lizza", "12", "ASR1" });
  11. foods.append({ "SMilk", "34", "BE-23" });
  12. foods.append({ "YCoffe", "5", "QRTY8-9" });*/
  13. }
  14.  
  15. int FoodTableModel::rowCount(const QModelIndex &parent) const
  16. {
  17. return foods.size();
  18. }
  19.  
  20. int FoodTableModel::columnCount(const QModelIndex &parent) const
  21. {
  22. return roleNames().size();
  23. }
  24.  
  25. QVariant FoodTableModel::data(const QModelIndex &index, int role) const
  26. {
  27. QVariant variant;
  28. const int row = index.row();
  29. const int col = role;
  30. switch (col) {
  31. case CHECK: variant = foods.at(row).check; break;
  32. case DESCRIPTION: variant = foods.at(row).description; break;
  33. case PRICE: variant = foods.at(row).price; break;
  34. case CODE: variant = foods.at(row).code; break;
  35. }
  36. return variant;
  37. }
  38.  
  39. QHash<int, QByteArray> FoodTableModel::roleNames() const
  40. {
  41. QHash<int, QByteArray> roles;
  42. roles.insert(CHECK, "check");
  43. roles.insert(DESCRIPTION, "description");
  44. roles.insert(PRICE, "price");
  45. roles.insert(CODE, "code");
  46. return roles;
  47. }
  48.  
  49. bool FoodTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
  50. {
  51. if(index.isValid() && role == CHECK) {
  52. foods[index.row()].check = value.toBool();
  53. emit dataChanged(index, index, { role });
  54. return true;
  55. }
  56. else return false;
  57. }
  58.  
  59. void FoodTableModel::checkTable(bool value)
  60. {
  61. for (int i = 0; i < foods.size(); i++) {
  62. qDebug() << i << value;
  63. foods[i].check = value;
  64. }
  65. }
  66.  
  1. #include <QGuiApplication>
  2. #include <QQmlApplicationEngine>
  3. #include <QQmlContext>
  4. #include "foodtablemodel.h"
  5. #include "person.h"
  6. #include "sortfilterproxymodel.h"
  7.  
  8. int main(int argc, char *argv[]) {
  9. QGuiApplication app(argc, argv);
  10.  
  11. QQmlApplicationEngine engine;
  12.  
  13. FoodTableModel food;
  14.  
  15. qmlRegisterType<SortFilterProxyModel>("SortFilterProxyModel", 0, 1, "SortFilterProxyModel");
  16.  
  17. engine.rootContext()->setContextProperty("food", &food);
  18.  
  19. engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
  20. if (engine.rootObjects().isEmpty())
  21. return -1;
  22.  
  23. return app.exec();
  24. }
  25.  
  1. import QtQuick 2.12
  2. import QtQuick.Controls 1.4 as C1
  3. import QtQuick.Controls.Styles 1.4
  4. import QtQuick.Controls 2.4
  5. import SortFilterProxyModel 0.1
  6. import "JavaScript/TableViewCheck.js" as TVC
  7.  
  8. C1.TableView {
  9. id: tableView
  10. clip: true
  11. sortIndicatorVisible: true
  12. currentRow: rowCount ? 0 : -1
  13. model: SortFilterProxyModel {
  14. source: food.rowCount() > 0 ? food : null
  15.  
  16. sortOrder: tableView.sortIndicatorOrder
  17. sortCaseSensitivity: Qt.CaseInsensitive
  18. sortRole: food.rowCount() && tableView.getColumn(tableView.sortIndicatorColumn).role !== "check" > 0 ?
  19. tableView.getColumn(tableView.sortIndicatorColumn).role : ""
  20. }
  21. C1.TableViewColumn {
  22. width: 30
  23. role: "check"
  24. resizable: false
  25. delegate: C1.CheckBox {
  26. id: checkBox
  27. anchors.left: parent.left
  28. anchors.leftMargin: parent.width / 3
  29. checked: model.check
  30. onVisibleChanged: if (visible) checked = model.check
  31. onClicked: model.check = checked
  32. }
  33. }
  34. C1.TableViewColumn {
  35. role: "description"
  36. title: "Description"
  37. }
  38. C1.TableViewColumn {
  39. role: "price"
  40. title: "Price"
  41. }
  42. C1.TableViewColumn {
  43. role: "code"
  44. title: "Code"
  45. }
  46. style: TableViewStyle {
  47. headerDelegate: Rectangle {
  48. height: textItem.implicitHeight
  49. width: textItem.implicitWidth
  50. Text {
  51. id: textItem
  52. anchors.fill: parent
  53. anchors.leftMargin: 6
  54. verticalAlignment: Text.AlignVCenter
  55. horizontalAlignment: styleData.textAlignment
  56. text: styleData.value
  57. }
  58. C1.CheckBox {
  59. anchors.left: parent.left
  60. anchors.leftMargin: parent.width / 3
  61. property bool isPressed: styleData.pressed
  62. visible: styleData.column === 0
  63. onIsPressedChanged: {
  64. if(isPressed) {
  65. checked = !checked
  66. for(var i = 0; i < rowCount; i++) {
  67. food.setData(foods.index(i, 0), checked, "check")
  68. }
  69. //food.checkTable(checked)
  70. }
  71. }
  72. }
  73. }
  74. }
  75. }
  76.  
3

Do you like it? Share on social networks!

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

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

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

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

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

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

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

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

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

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

            main.c

            1. #include <QGuiApplication>
            2. #include <QQmlApplicationEngine>
            3. #include <QQmlContext>
            4.  
            5. #include "checkmodel.h"
            6.  
            7. int main(int argc, char *argv[])
            8. {
            9. QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
            10.  
            11. QGuiApplication app(argc, argv);
            12.  
            13. QQmlApplicationEngine engine;
            14.  
            15. CheckModel someModel;
            16. engine.rootContext()->setContextProperty("someModel", &someModel);
            17.  
            18. engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
            19. if (engine.rootObjects().isEmpty())
            20. return -1;
            21.  
            22. return app.exec();
            23. }

            checkmodel.h

            1. #ifndef CHECKMODEL_H
            2. #define CHECKMODEL_H
            3.  
            4. #include <QAbstractTableModel>
            5.  
            6. struct SomeStruct
            7. {
            8. bool check;
            9. QString description;
            10. int value;
            11. };
            12.  
            13. class CheckModel : public QAbstractTableModel
            14. {
            15. Q_OBJECT
            16.  
            17. enum Roles
            18. {
            19. CHECK, DESCRIPTION, VALUE
            20. };
            21.  
            22. public:
            23. CheckModel(QObject* parent = nullptr);
            24. int rowCount(const QModelIndex &parent = QModelIndex()) const {
            25. Q_UNUSED(parent); return list.count();
            26. }
            27. int columnCount(const QModelIndex &parent = QModelIndex()) const {
            28. Q_UNUSED(parent); return 3;
            29. }
            30. QHash<int, QByteArray> roleNames() const;
            31. QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
            32. Q_INVOKABLE void setAllChecked(bool value);
            33. Q_INVOKABLE void setRowChecked(int row, bool value);
            34.  
            35. private:
            36. void fillModel();
            37.  
            38. QVector<SomeStruct> list;
            39. };
            40.  
            41. #endif // CHECKMODEL_H

            checkmodel.cpp

            1. #include "checkmodel.h"
            2.  
            3. #include <QtDebug>
            4.  
            5. CheckModel::CheckModel(QObject *parent) : QAbstractTableModel (parent)
            6. {
            7. fillModel();
            8. }
            9.  
            10. QHash<int, QByteArray> CheckModel::roleNames() const
            11. {
            12. QHash<int, QByteArray> roles;
            13. roles.insert(Qt::UserRole+CHECK, "check");
            14. roles.insert(Qt::UserRole+DESCRIPTION, "description");
            15. roles.insert(Qt::UserRole+VALUE, "value");
            16. return roles;
            17. }
            18.  
            19. QVariant CheckModel::data(const QModelIndex &index, int role) const
            20. {
            21. if (!index.isValid())
            22. return QVariant();
            23.  
            24. SomeStruct s = list.at(index.row());
            25. switch (role) {
            26. case Qt::UserRole+CHECK: return s.check;
            27. case Qt::UserRole+DESCRIPTION: return s.description;
            28. case Qt::UserRole+VALUE: return s.value;
            29. }
            30.  
            31. return QVariant();
            32. }
            33.  
            34. void CheckModel::setAllChecked(bool value)
            35. {
            36. beginResetModel();
            37. qDebug() << 1;
            38. for (SomeStruct &s : list) {
            39. s.check = value;
            40. qDebug() << value;
            41. }
            42. endResetModel();
            43. }
            44.  
            45. void CheckModel::setRowChecked(int row, bool value)
            46. {
            47. if (row < 0 || row >= list.count())
            48. return;
            49. SomeStruct &s = list[row];
            50. s.check = value;
            51. emit dataChanged(index(row,0), index(row, columnCount()-1));
            52. }
            53.  
            54. void CheckModel::fillModel()
            55. {
            56. SomeStruct one = {true, "Some definition", 100};
            57. SomeStruct two = {false, "Some definition again", 200};
            58. SomeStruct three = {true, "Definition", 1000};
            59. list << one << two << three;
            60. }

            main.qml

            1. import QtQuick 2.9
            2. import QtQuick.Window 2.2
            3. import QtQuick.Controls 1.4
            4.  
            5. Window {
            6. visible: true
            7. width: 640
            8. height: 480
            9.  
            10. TableView {
            11. anchors.fill: parent
            12.  
            13. model: someModel
            14.  
            15. TableViewColumn {
            16. role: "check"
            17. title: "Check"
            18. delegate: CheckBox {
            19. checked: styleData.value
            20. MouseArea {
            21. anchors.fill: parent
            22. onClicked: {
            23. someModel.setRowChecked(styleData.row, !styleData.value)
            24. }
            25. }
            26. }
            27. }
            28. TableViewColumn {
            29. role: "description"
            30. title: "Description"
            31. }
            32. TableViewColumn {
            33. role: "value"
            34. title: "Value"
            35. }
            36.  
            37. headerDelegate: Item {
            38. height: textItem.implicitHeight
            39.  
            40. Text {
            41. id: textItem
            42. verticalAlignment: Text.AlignVCenter
            43. horizontalAlignment: styleData.textAlignment
            44. text: styleData.value
            45. }
            46. CheckBox {
            47. anchors.left: parent.left
            48. anchors.leftMargin: parent.width / 3
            49. property bool isPressed: styleData.pressed
            50. visible: styleData.column === 0
            51. onIsPressedChanged: {
            52. if (isPressed) {
            53. checked = !checked
            54. someModel.setAllChecked(checked)
            55. }
            56. }
            57. }
            58. }
            59. }
            60. }

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

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

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

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

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

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

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

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

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

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

                    1. #ifndef SORTFILTERPROXYMODEL_H
                    2. #define SORTFILTERPROXYMODEL_H
                    3.  
                    4. #include <QtCore/qsortfilterproxymodel.h>
                    5. #include <QtQml/qjsvalue.h>
                    6.  
                    7. class SortFilterProxyModel : public QSortFilterProxyModel
                    8. {
                    9. Q_OBJECT
                    10. Q_PROPERTY(int count READ count NOTIFY countChanged)
                    11. Q_PROPERTY(QObject *source READ source WRITE setSource)
                    12.  
                    13. Q_PROPERTY(QByteArray sortRole READ sortRole WRITE setSortRole)
                    14. Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder)
                    15.  
                    16. public:
                    17. explicit SortFilterProxyModel(QObject *parent = 0);
                    18.  
                    19. QObject *source() const;
                    20. void setSource(QObject *source);
                    21.  
                    22. QByteArray sortRole() const;
                    23. void setSortRole(const QByteArray &role);
                    24.  
                    25. void setSortOrder(Qt::SortOrder order);
                    26.  
                    27. int count() const;
                    28. Q_INVOKABLE QJSValue get(int index) const;
                    29.  
                    30. signals:
                    31. void countChanged();
                    32.  
                    33. protected:
                    34. int roleKey(const QByteArray &role) const;
                    35. QHash<int, QByteArray> roleNames() const;
                    36. };
                    37.  
                    38. #endif // SORTFILTERPROXYMODEL_H
                    39.  
                    1. #include "sortfilterproxymodel.h"
                    2. #include <QtDebug>
                    3. #include <QtQml>
                    4.  
                    5. SortFilterProxyModel::SortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
                    6. {
                    7. connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(countChanged()));
                    8. connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(countChanged()));
                    9. }
                    10.  
                    11. int SortFilterProxyModel::count() const
                    12. {
                    13. return rowCount();
                    14. }
                    15.  
                    16. QObject *SortFilterProxyModel::source() const
                    17. {
                    18. return sourceModel();
                    19. }
                    20.  
                    21. void SortFilterProxyModel::setSource(QObject *source)
                    22. {
                    23. setSourceModel(qobject_cast<QAbstractItemModel *>(source));
                    24. }
                    25.  
                    26. QByteArray SortFilterProxyModel::sortRole() const
                    27. {
                    28. return roleNames().value(QSortFilterProxyModel::sortRole());
                    29. }
                    30.  
                    31. void SortFilterProxyModel::setSortRole(const QByteArray &role)
                    32. {
                    33. QSortFilterProxyModel::setSortRole(roleKey(role));
                    34. }
                    35.  
                    36. void SortFilterProxyModel::setSortOrder(Qt::SortOrder order)
                    37. {
                    38. QSortFilterProxyModel::sort(0, order);
                    39. }
                    40.  
                    41. QJSValue SortFilterProxyModel::get(int idx) const
                    42. {
                    43. QJSEngine *engine = qmlEngine(this);
                    44. QJSValue value = engine->newObject();
                    45. if (idx >= 0 && idx < count()) {
                    46. QHash<int, QByteArray> roles = roleNames();
                    47. QHashIterator<int, QByteArray> it(roles);
                    48. while (it.hasNext()) {
                    49. it.next();
                    50. value.setProperty(QString::fromUtf8(it.value()), data(index(idx, 0), it.key()).toString());
                    51. }
                    52. }
                    53. return value;
                    54. }
                    55.  
                    56. int SortFilterProxyModel::roleKey(const QByteArray &role) const
                    57. {
                    58. QHash<int, QByteArray> roles = roleNames();
                    59. QHashIterator<int, QByteArray> it(roles);
                    60. while (it.hasNext()) {
                    61. it.next();
                    62. if (it.value() == role)
                    63. return it.key();
                    64. }
                    65. return -1;
                    66. }
                    67.  
                    68. QHash<int, QByteArray> SortFilterProxyModel::roleNames() const
                    69. {
                    70. if (QAbstractItemModel *source = sourceModel())
                    71. return source->roleNames();
                    72. return QHash<int, QByteArray>();
                    73. }
                    74.  
                    75.  
                    76.  

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

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

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

                    1. onIsPressedChanged: {
                    2. if(isPressed && styleData.column === 0) {
                    3. checked = !checked
                    4. food.setAllChecked(checked)
                    5. }
                    6. }
                    7. }
                      Serj Demchenko
                      • Aug. 4, 2021, 9:20 p.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
                        • Last comments
                        • AK
                          April 1, 2025, 11:41 a.m.
                          Добрый день. В данный момент работаю над проектом, где необходимо выводить звук из программы в определенное аудиоустройство (колонки, наушники, виртуальный кабель и т.д). Пишу на Qt5.12.12 поско…
                        • Evgenii Legotckoi
                          March 9, 2025, 9:02 p.m.
                          К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
                        • VP
                          March 9, 2025, 4:14 p.m.
                          Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…
                        • ИМ
                          Nov. 22, 2024, 9:51 p.m.
                          Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
                        • Evgenii Legotckoi
                          Oct. 31, 2024, 11:37 p.m.
                          Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup