Checkbox in header cell in QTableView

I want to have a simple column heading with a checkbox that selects / deselects all rows in a QTableView. When you click on the check box in the header, you can select or deselect all rows.

enter image description here

When I want to add a checkbox in a table cell, I just need to return the check state for Qt :: CheckStateRole to the data (..) for the required model indices, as shown below. This works as expected.

QVariant MyModel::data( const QModelIndex & rIndex, int iRole) const { ... if (iRole == Qt::Qt::CheckStateRole) { return checkstate; } } 

But when I want to add a checkbox in the header cell, the above method does not work. Listen to my sample code.

 QVariant MyModel::headerData( int iSection, Qt::Orientation eOrientation, int iRole) const { ... if (iRole == Qt::CheckStateRole) { return checkstate; } } 

QTableView does not call the headerData () function in my model with Qt :: CheckStateRole, as it does with the data () function.

Why is this behavior? How to set a checkbox in the header cell only by changing my custom table model?

(for this purpose I do not want to create custom QTableView or QHeaderView)

+6
source share
3 answers

You cannot do this - Qt does not support checkboxes in headers by default. You can read https://wiki.qt.io/Qt_project_org_faq#How_can_I_insert_a_checkbox_into_the_header_of_my_view.3F for more information and its implementation using custom QHeaderView

+4
source

Here a little changed / fixed (the checkbox was displayed as disabled, and the redraw did not work) the code from the link in the accepted answer.

.h

 class CheckBoxHeader : public QHeaderView { Q_OBJECT public: CheckBoxHeader(Qt::Orientation orientation, QWidget* parent = 0); bool isChecked() const { return isChecked_; } void setIsChecked(bool val); signals: void checkBoxClicked(bool state); protected: void paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const; void mousePressEvent(QMouseEvent* event); private: bool isChecked_; void redrawCheckBox(); }; 

.cpp

 #include "CheckBoxHeader.h" CheckBoxHeader::CheckBoxHeader(Qt::Orientation orientation, QWidget* parent /*= 0*/) : QHeaderView(orientation, parent) { isChecked_ = true; } void CheckBoxHeader::paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const { painter->save(); QHeaderView::paintSection(painter, rect, logicalIndex); painter->restore(); if (logicalIndex == 0) { QStyleOptionButton option; option.rect = QRect(1,3,20,20); option.state = QStyle::State_Enabled | QStyle::State_Active; if (isChecked_) option.state |= QStyle::State_On; else option.state |= QStyle::State_Off; option.state |= QStyle::State_Off; style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter); } } void CheckBoxHeader::mousePressEvent(QMouseEvent* event) { setIsChecked(!isChecked()); emit checkBoxClicked(isChecked()); } void CheckBoxHeader::redrawCheckBox() { viewport()->update(); } void CheckBoxHeader::setIsChecked(bool val) { if (isChecked_ != val) { isChecked_ = val; redrawCheckBox(); } } 

Using

 CheckBoxHeader* header = new CheckBoxHeader(Qt::Horizontal, &table); table.setHorizontalHeader(header); // handle signal if needed (to set checkboxes in all rows, etc.) //connect(header, &CheckBoxHeader::checkBoxClicked, this, &MyForm::on_header_checkBoxClicked); 
+4
source

I am surprised that such a hacker solution is recommended and used.

First: The validation state should be stored in the model. All tools already exist.

 bool MyModel::setHeaderData(int index, Qt::Orientation orient, const QVariant& val, int role) { if(Qt::Vertical != orient) return Base::setHeaderData(index, orient, val, role); storeCheckState(index, val); emit headerDataChanged(orient, index, index); return true; } QVariant MyModel::headerData(int index, Qt::Orientation o, int role) const { if(Qt::Vertical != orient) return Base::headerData(index, o, role); switch(role) { ... case Qt::CheckStateRole: return fetchCheckState(index); } return Base::headerData(index, o, role); } 

Second: We switch the checked state simply by processing the click signal on the header.

 connect(header, &QHeaderView::sectionClicked, receiver , [receiver](int sec) { const auto index = logicalIndex(sec); model()->setHeaderData(index , Qt::Vertical , Qt::CheckState(model()->headerData(index, Qt::Vertical, Qt::CheckStateRole).toUInt()) != Qt::Checked ? Qt::Checked : Qt::Unchecked , Qt::CheckStateRole); }); 

Third: At this stage we fully functionally check the behavior, only a part is missing - this is visualization. The smartest way is to reuse the model using Qt::DecorationRole . Here is a dummy implementation:

 QVariant MyModel::headerData(int index, Qt::Orientation o, int role) const { if(Qt::Vertical != orient) return Base::headerData(index, o, role); switch(role) { case Qt::DecorationRole: { QPixmap p{12,12}; p.fill(Qt::CheckState(headerData(index, o, Qt::CheckStateRole).toUInt()) ? Qt::green : Qt::red); return p; } break; ... } return Base::headerData(index, o, role); } 

Of course, you can draw a real flag using a stylized drawing.

Please note that this solution does not require subclassification and custom widgets.

In addition, the verification state is separate from the / UI view. The only drawback is that the visual effects are processed by the model, but this is not necessary - any method can be used to draw the check state, including from alternative answers.

+1
source

All Articles