Ruslan Polupan
April 24, 2019, 4:07 p.m.

Помогите разобраться с CheckBox delegate в QAbstractTableModel

Delegate, qcheckbox, QAbstractTableModel

Доброго времени суток.
Где то я упустил что-то при изучении делегатов и их использовании.

Отображение работает. Но после того как выбираешь другую строку таблицы, предидущее выделение сбрсывается.
Не получается выбрать несколько объектов.


Файлы реализации делегата CheckBoxDelegate использовал такие же как в этой статье https://evileg.com/ru/post/478/

Реализация модели
modelterminals.h

  1. #ifndef MODELTERMINALS_H
  2. #define MODELTERMINALS_H
  3. #include "terminalclass.h"
  4.  
  5. #include <QObject>
  6. #include <QAbstractTableModel>
  7.  
  8. class ModelTerminals : public QAbstractTableModel
  9. {
  10. Q_OBJECT
  11. QList<TerminalClass> m_listTerm;
  12. public:
  13. ModelTerminals(QList<TerminalClass> lsTerm);
  14.  
  15. // QAbstractItemModel interface
  16. public:
  17. int rowCount(const QModelIndex &parent) const;
  18. int columnCount(const QModelIndex &parent) const;
  19. QVariant data(const QModelIndex &index, int role) const;
  20. bool setData(const QModelIndex &index, const QVariant &value, int role);
  21. Qt::ItemFlags flags(const QModelIndex &index) const;
  22. QVariant headerData(int section, Qt::Orientation orientation, int role) const;
  23.  
  24.  
  25.  
  26. };
  27.  
  28. #endif // MODELTERMINALS_H

modelterminals.cpp

  1. #include "modelterminals.h"
  2.  
  3. ModelTerminals::ModelTerminals(QList<TerminalClass> lsTerm ) :
  4. m_listTerm(lsTerm)
  5. {
  6.  
  7. }
  8.  
  9.  
  10. int ModelTerminals::rowCount(const QModelIndex &parent) const
  11. {
  12. return m_listTerm.size();
  13. Q_UNUSED(parent)
  14. }
  15.  
  16. int ModelTerminals::columnCount(const QModelIndex &parent) const
  17. {
  18. return m_listTerm.at(0).colParam();
  19. Q_UNUSED(parent)
  20. }
  21.  
  22. QVariant ModelTerminals::data(const QModelIndex &index, int role) const
  23. {
  24. if(!index.isValid()) { return QVariant();}
  25. TerminalClass t = m_listTerm[index.row()];
  26. switch (role) {
  27. case Qt::DisplayRole:
  28. switch (index.column()) {
  29. case 0: return t.isCheced();
  30. case 1: return t.terminalID();
  31. case 2: return t.terminalName();
  32. case 3: return t.regionID();
  33. case 4: return t.serverName();
  34. }
  35. break;
  36. case Qt::CheckStateRole:
  37. if(index.column() == 0) {
  38. return (!t.isCheced()) ? Qt::Checked : Qt::Unchecked;
  39. }
  40. break;
  41. default:
  42. break;
  43. }
  44. return QVariant();
  45. }
  46.  
  47. bool ModelTerminals::setData(const QModelIndex &index, const QVariant &value, int role)
  48. {
  49. if(!index.isValid()) {return false;}
  50.  
  51. TerminalClass t = m_listTerm[index.row()];
  52.  
  53. if(role == Qt::CheckStateRole){
  54. if(value.toInt() == Qt::Checked){
  55. t.setIsCheced(true);
  56. } else {
  57. t.setIsCheced(false);
  58. }
  59. emit dataChanged(index,index);
  60. return true;
  61. }
  62. return false;
  63. }
  64.  
  65. Qt::ItemFlags ModelTerminals::flags(const QModelIndex &index) const
  66. {
  67. Qt::ItemFlags flags = QAbstractTableModel::flags(index);
  68. if(index.isValid() && index.column() == 0){
  69. flags |= Qt::ItemIsEditable;
  70. }
  71. return flags;
  72. }
  73.  
  74. QVariant ModelTerminals::headerData(int section, Qt::Orientation orientation, int role) const
  75. {
  76. if( role != Qt::DisplayRole) { return QVariant();}
  77.  
  78. if( orientation == Qt::Vertical ) { return section; }
  79.  
  80. switch (section) {
  81. case 0: return "";
  82. case 1: return "АЗС";
  83. case 2: return "Адрес";
  84. case 3: return "Регион";
  85. case 4: return "Сервер";
  86. default:
  87. break;
  88. }
  89. return QVariant();
  90. }
  91.  

Получаю данные для модели и создаю модель

  1. #include "fuelnamedialog.h"
  2. #include "ui_fuelnamedialog.h"
  3.  
  4. FuelNameDialog::FuelNameDialog(QWidget *parent) :
  5. QDialog(parent),
  6. ui(new Ui::FuelNameDialog)
  7. {
  8. ui->setupUi(this);
  9.  
  10. createListTerminals();
  11. createModelterminals();
  12. }
  13.  
  14. FuelNameDialog::~FuelNameDialog()
  15. {
  16. delete ui;
  17. }
  18.  
  19. void FuelNameDialog::createModelterminals()
  20. {
  21. qInfo(logInfo()) << "Заружено терминалов:" << listTerminals.size();
  22. CheckBoxDelegate *delegate = new CheckBoxDelegate();
  23. modelTerminals = new ModelTerminals(listTerminals);
  24.  
  25. ui->tableViewTerminals->setModel(modelTerminals);
  26. ui->tableViewTerminals->setItemDelegateForColumn(0,delegate);
  27.  
  28. ui->tableViewTerminals->verticalHeader()->hide();
  29. ui->tableViewTerminals->resizeColumnsToContents();
  30.  
  31. }
  32.  
  33. void FuelNameDialog::createListTerminals()
  34. {
  35. QSqlQuery q;
  36. TerminalClass t;
  37. if(!q.exec("SELECT t.TERMINAL_ID, TRIM(t.name) AS NAME, t.OWNER_ID AS REGION, c.SERVER_NAME FROM TERMINALS t "
  38. "LEFT JOIN CONNECTIONS c ON c.TERMINAL_ID = t.TERMINAL_ID "
  39. "WHERE t.TERMINALTYPE = 3 AND c.CONNECT_ID = 2 "
  40. "ORDER BY t.TERMINAL_ID")) {
  41. qInfo(logInfo()) << Q_FUNC_INFO << "Не возможно получить список терминалов" << q.lastError().text();
  42. return;
  43. }
  44. while(q.next()){
  45. t.setIsCheced(false);
  46. t.setTerminalID(q.value(0).toInt());
  47. t.setTerminalName(q.value(1).toString());
  48. t.setRegionID(q.value(2).toInt());
  49. t.setServerName(q.value(3).toString());
  50. listTerminals.append(t);
  51. }
  52.  
  53. }
2

Do you like it? Share on social networks!

10
Evgenii Legotckoi
  • April 24, 2019, 5:09 p.m.

Добрый день

А вот этот код

  1. case Qt::CheckStateRole:
  2. if(index.column() == 0) {
  3. return (!t.isCheced()) ? Qt::Checked : Qt::Unchecked;
  4. }

Не должен быть написан так?

  1. case Qt::CheckStateRole:
  2. if(index.column() == 0) {
  3. return (t.isCheced()) ? Qt::Checked : Qt::Unchecked;
  4. }
    Ruslan Polupan
    • April 24, 2019, 5:22 p.m.

    Согласен. но ситуация не поменялась. Такое чуство что данные не записываются в модель.

      Evgenii Legotckoi
      • April 25, 2019, 1:45 p.m.

      Думаю, что в методе setData нужно в роли Qt::EditRole делать установку значения. Скорее всего у вас просто не заходит в ту ветку кода выполнение программы. Обычно для установки значения используется именно роль Qt::EditRole

        Ruslan Polupan
        • April 25, 2019, 4:51 p.m.
        • The answer was marked as a solution.

        Изменил функцию
        теперь работает.

        1. bool ModelTerminals::setData(const QModelIndex &index, const QVariant &value, int role)
        2. {
        3. Q_UNUSED(role)
        4. if(!index.isValid()) {return false;}
        5.  
        6. m_listTerm[index.row()].setIsCheced(value.toBool());
        7. emit QAbstractTableModel::dataChanged(index,index);
        8. return true;
        9.  
        10. }
          Ruslan Polupan
          • May 7, 2019, 1:36 p.m.

          Продолжаем вопросы... (Сори если что :-)
          Как теперь отловить сигнал checked от делегата?

            Ruslan Polupan
            • May 7, 2019, 2:09 p.m.

            Пока сделал так:

            1. connect(ui->tableViewTerminals->model(),&QAbstractTableModel::dataChanged,this,&SelectTerminalPage::terminalChecked);

            Где:

            1. void SelectTerminalPage::terminalChecked()
            2. {
            3. const int rowCount = ui->tableViewTerminals->model()->rowCount(QModelIndex());
            4.  
            5. for(int i =0; i < rowCount; ++i){
            6. if(ui->tableViewTerminals->model()->index(i,0).data(Qt::CheckStateRole).toInt() == Qt::Checked)
            7. qInfo(logInfo()) << "Выбрана" << i << "строка";
            8. }
            9.  
            10. }

            но это отрабатывает изменение модели
            А вот сам клик на checkbox бы отловить...

              Evgenii Legotckoi
              • May 7, 2019, 2:30 p.m.

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

                Ruslan Polupan
                • May 7, 2019, 4:47 p.m.

                Еще обнаружил такую ситуацию. Оказывается данные в модель записываются, лиш после того как фокус получает любой item TableView.
                Т.е. Состояние CheckBox записывается в модель не при изменении его статуса а лишь тогда когда с него теряется фокус.

                  Evgenii Legotckoi
                  • May 7, 2019, 4:51 p.m.

                  Ну в общем-то да, есть такая ситуация. Думаю, что это правильное поведение. Дело в том, что если логика под капотом очень сложная и влияет на большое количество компонентов, то апдейт при редактировании может очень сильно уменьшить скорость работы софта, поскольку такие апдейты будут выполняться тогда, когда по факту в 99% случае они вовсе не нужны.

                    Ruslan Polupan
                    • May 7, 2019, 5:01 p.m.
                    • (edited)

                    Реализовать такую ситуацию:
                    Я выбрал несколько строк и не прешел на другую ячейку а нажал кнопку "Выполнить" например.
                    И тогда получается что последняя мною выбранная запись не попадет для дальнейшей обработки.
                    Или есть таки способ принудительно опросить делегаты и записать их состояние в модель?

                      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