Ruslan Polupan
11 апреля 2019 г. 16:01

QCheckBox в качестве делегата QTableView

Доброго времени суток.
Возникла необходимость использовать делегат для отображения QCheckBox в таблице (выбирать объекты из списка для последующей работы с ними).
Для того чтобы разобратся с делегатами создал небольшой проект.
На выходе получаем следующее:


Структура проекта:

checkboxdelegate.h

  1. #ifndef CHECKBOXDELEGATE_H
  2. #define CHECKBOXDELEGATE_H
  3. #include <QStyledItemDelegate>
  4.  
  5.  
  6. class CheckBoxDelegate : public QStyledItemDelegate
  7. {
  8. Q_OBJECT
  9.  
  10. public:
  11. CheckBoxDelegate(QObject *parent = nullptr);
  12.  
  13. // QAbstractItemDelegate interface
  14. public:
  15. void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
  16. QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
  17. void setEditorData(QWidget *editor, const QModelIndex &index) const;
  18. void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
  19. void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const;
  20. };
  21.  
  22. #endif // CHECKBOXDELEGATE_H
  23.  

checkboxdelegate.cpp

  1. #include "checkboxdelegate.h"
  2. #include <QCheckBox>
  3. #include <QApplication>
  4.  
  5. CheckBoxDelegate::CheckBoxDelegate(QObject *parent)
  6. :QStyledItemDelegate (parent)
  7. {
  8.  
  9. }
  10.  
  11. QWidget *CheckBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
  12. {
  13. Q_UNUSED(option)
  14. Q_UNUSED(index)
  15.  
  16. //Cоздаем checkbox editor
  17. QCheckBox *editor = new QCheckBox(parent);
  18. return editor;
  19. }
  20.  
  21. void CheckBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
  22. {
  23. //Установливаем выбрано/не выбрано
  24. QCheckBox *cb = qobject_cast<QCheckBox *>(editor);
  25. cb->setChecked(index.data().toBool());
  26. }
  27.  
  28. void CheckBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
  29. {
  30. //Записываем данные в модель
  31. QCheckBox *cb = static_cast<QCheckBox *>(editor);
  32. int value = (cb->checkState()==Qt::Checked)? 1 : 0;
  33. model->setData(index, value, Qt::EditRole);
  34. }
  35.  
  36. void CheckBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
  37. {
  38. Q_UNUSED(index);
  39. QStyleOptionButton checkboxstyle;
  40. QRect checkbox_rect = QApplication::style()->subElementRect(QStyle::SE_CheckBoxIndicator, &checkboxstyle);
  41.  
  42. //Центрирование
  43. checkboxstyle.rect = option.rect;
  44. checkboxstyle.rect.setLeft(option.rect.x() +
  45. option.rect.width()/2 - checkbox_rect.width()/2);
  46.  
  47. editor->setGeometry(checkboxstyle.rect);
  48. }
  49.  
  50.  
  51. void CheckBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
  52. {
  53. //Получаем данные
  54. bool data = index.model()->data(index, Qt::DisplayRole).toBool();
  55.  
  56. //Создаем стиль CheckBox
  57. QStyleOptionButton checkboxstyle;
  58. QRect checkbox_rect = QApplication::style()->subElementRect(QStyle::SE_CheckBoxIndicator, &checkboxstyle);
  59.  
  60. //Центрирование
  61. checkboxstyle.rect = option.rect;
  62. checkboxstyle.rect.setLeft(option.rect.x() +
  63. option.rect.width()/2 - checkbox_rect.width()/2);
  64. //Выбрано или не выбрано
  65. if(data)
  66. checkboxstyle.state = QStyle::State_On|QStyle::State_Enabled;
  67. else
  68. checkboxstyle.state = QStyle::State_Off|QStyle::State_Enabled;
  69.  
  70. //Готово! Отображаем!
  71. QApplication::style()->drawControl(QStyle::CE_CheckBox, &checkboxstyle, painter);
  72. }
  73.  

main.cpp

  1. #include "checkboxdelegate.h"
  2. #include <QApplication>
  3. #include <QStandardItemModel>
  4. #include <QHeaderView>
  5. #include <QTableView>
  6.  
  7. #define MODEL_ROWS 4 //Количество строк модели
  8. #define MODEL_COLUMN 2 //Количество столбцов модели
  9. #define DELEGATE_COLUMN 0 //Столбец с делегатом
  10.  
  11. int main(int argc, char *argv[])
  12. {
  13. QApplication a(argc, argv);
  14.  
  15. //Создаем модель и представление
  16. QStandardItemModel *model = new QStandardItemModel(MODEL_ROWS, MODEL_COLUMN);
  17. QTableView *tableView = new QTableView();
  18. CheckBoxDelegate *delegate = new CheckBoxDelegate();
  19.  
  20. //Заолняем модель данными
  21. for (int row =0; row < MODEL_ROWS; ++row) {
  22. for (int column = 0; column < MODEL_COLUMN; ++column) {
  23. QModelIndex index = model->index(row, column, QModelIndex());
  24. if(column == 0)
  25. model->setData(index, QVariant(0));
  26. else
  27. model->setData(index, QVariant((row + 1) * (column + 1)));
  28. }
  29.  
  30. }
  31. //Устанавливаем модель в представление
  32. tableView->setModel(model);
  33. //Устанавливаем делегат в столбец
  34. tableView->setItemDelegateForColumn(DELEGATE_COLUMN, delegate);
  35. //Внешний вид предтавления
  36. tableView->resizeColumnsToContents();
  37. tableView->verticalHeader()->hide();
  38. tableView->horizontalHeader()->setStretchLastSection(true);
  39.  
  40. tableView->setWindowTitle("Check Box Delegate");
  41. tableView->show();
  42.  
  43. return a.exec();
  44. }
  45.  

Файл проекта:

CheckBoxDelegate_PG92889.7z CheckBoxDelegate_PG92889.7z

Единственная проблема состоит в том, что для выделения QCheckBox приходится дважны нажимать мышь. Получается что сначала выделяется ячейка. а только потом фокус уже попадает на виджет.

Рекомендуемые статьи по этой тематике

По статье задано0вопрос(ов)

5

Вам это нравится? Поделитесь в социальных сетях!

D
  • 12 августа 2019 г. 5:50

Пример лучше скину после отпуска.

Ruslan Polupan
  • 12 августа 2019 г. 12:44

Было бы интересно.

Evgenii Legotckoi
  • 12 августа 2019 г. 14:20

Если это будет достаточно самостоятельный код, то вы бы тогда могли написать небольшую статью на эту тематику в раздел Qt.

D
  • 17 августа 2019 г. 19:04
  • (ред.)

github ChekableTView

Правой групповая смена значения при перетаскивании левой как обычно.

m
  • 27 января 2020 г. 20:53

Единственная проблема состоит в том, что для выделения QCheckBox приходится дважны нажимать мышь. Получается что сначала выделяется ячейка. а только потом фокус уже попадает на виджет.

Ну это как бы не проблема. Это отсутствие решения.

ПБ
  • 12 февраля 2020 г. 13:42

Как изменить цвет фона внутри checkbox (по возможности убрать рамку вокруг)?
Пробовал с цветом так:

  1. QWidget *CheckBoxDelegate::createEditor(QWidget *parent,
  2. const QStyleOptionViewItem &,
  3. const QModelIndex &) const
  4. {
  5. auto checkBox = new QCheckBox(parent);
  6. checkBox->setStyleSheet("QCheckBox::indicator {background-color: blue;}");
  7.  
  8. return checkBox;
  9. }
Ruslan Polupan
  • 12 февраля 2020 г. 13:54
  • (ред.)

Я сейчас просто меняю изображения на CheckBox

  1. ui->checkBoxIsActive->setStyleSheet(
  2. "QCheckBox::indicator:unchecked {image: url(:/Images/check_box_unchek.png);}"
  3. "QCheckBox::indicator:checked {image: url(:/Images/check_box.png);}");
ПБ
  • 12 февраля 2020 г. 14:30

Это не то, мне нужно именно делегат отредактировать (без изображения)

D
  • 21 сентября 2020 г. 1:34
  • (ред.)
  1. bool Node::setData(const QModelIndex& index, const QVariant& value, int role)
  2. {
  3. switch (index.column()) {
  4. case 0:
  5. switch (role) {
  6. case Qt::CheckStateRole:// <- то что надо
  7. shape()->setVisible(value.value<Qt::CheckState>() == Qt::Checked);
  8. return true;
  9. case Qt::EditRole:
  10. if (auto text = dynamic_cast<Text*>(shape()); text)
  11. text->setText(value.toString());
  12. return true;
  13. default:
  14. return false;
  15. }
  16. case 1:
  17. if (auto text = dynamic_cast<Text*>(shape()); text) {
  18. switch (role) {
  19. case Qt::EditRole:
  20. text->setSide(static_cast<Side>(value.toBool()));
  21. return true;
  22. default:
  23. return false;
  24. }
  25. }
  26. default:
  27. return false;
  28. }
  29. }
  30. QVariant Node::data(const QModelIndex& index, int role) const
  31. {
  32. switch (index.column()) {
  33. case 0:
  34. switch (role) {
  35. case Qt::DisplayRole:
  36. if (shape()->type() == GiShapeT)
  37. return QString("%1 (%2, %3)")
  38. .arg(shape()->name())
  39. .arg(m_id)
  40. .arg(static_cast<Text*>(shape())->text());
  41. else
  42. return QString("%1 (%2)")
  43. .arg(shape()->name())
  44. .arg(m_id);
  45. // case Qt::ToolTipRole:
  46. // return file()->shortName() + "\n" + file()->name();
  47. case Qt::CheckStateRole:// <- то что надо
  48. return shape()->isVisible() ? Qt::Checked : Qt::Unchecked;
  49. case Qt::DecorationRole:
  50. return shape()->icon();
  51. case Qt::UserRole:
  52. return m_id;
  53. case Qt::EditRole:
  54. if (shape()->type() == GiShapeT)
  55. return static_cast<Text*>(shape())->text();
  56. return QVariant();
  57. default:
  58. return QVariant();
  59. }
  60. case 1:
  61. if (auto text = dynamic_cast<Text*>(shape()); text) {
  62. switch (role) {
  63. case Qt::DisplayRole:
  64. case Qt::ToolTipRole:
  65. return sideStrList[text->side()];
  66. case Qt::EditRole:
  67. return static_cast<bool>(text->side());
  68. default:
  69. return QVariant();
  70. }
  71. }
  72. default:
  73. return QVariant();
  74. }
  75. }
  76.  
  77. Qt::ItemFlags Node::flags(const QModelIndex& index) const
  78. {
  79. Qt::ItemFlags itemFlag = Qt::ItemIsEnabled | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable /*| Qt::ItemIsDragEnabled*/;
  80. switch (index.column()) {
  81. case 0:
  82. return itemFlag | Qt::ItemIsUserCheckable // <- то что надо
  83. | (shape()->type() == GiShapeT
  84. ? Qt::ItemIsEditable
  85. : Qt::NoItemFlags);
  86. case 1:
  87. return itemFlag
  88. | (shape()->type() == GiShapeT
  89. ? Qt::ItemIsEditable
  90. : Qt::NoItemFlags);
  91. default:
  92. return itemFlag;
  93. }
  94. }

И не надо ни какой мороки с делегатами. Смотреть на строки с комментом "// <- то что надо"

Evgenii Legotckoi
  • 21 сентября 2020 г. 2:34
  • (ред.)

До тех пор, пока у вас проект содержит только одну таблицу, или несколько то может быть.
Когда их будет 1000 и чекбоксы в разных колонках, то без делегатов и переопределения возвращаемых редакторов в ячейках не обойдётесь )))

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь