Search code examples
c++qtqt4

Checkbox in a header cell in QTableView


I want to have a simple column header with a checkbox that selects/ deselects all rows in a QTableView. Clicking the check box in the header causes either to select or deselect all rows.

enter image description here

When I want to add a check box in a table cell, I have to just return the check state for the Qt::CheckStateRole in the data(..) for the required model indices as below. This is working 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 a header cell, the above method is not working. Hear is my sample code.

QVariant MyModel::headerData( int iSection, Qt::Orientation eOrientation, int iRole) const
{
    ...

    if (iRole == Qt::CheckStateRole)
    {
        return checkstate;
    }

}

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

Why is this behavior? How can I insert a check box in a header cell by only modifying my custom table model?

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


Solution

  • You cannot do it — Qt doesn't support check boxes in headers by default. You can read How can I insert a checkbox into the header of my view? for further information and its realization using a custom QHeaderView:

    How can I insert a checkbox into the header of my view?

    Currently there is no API to insert widgets in the header, but you can paint the checkbox yourself in order to insert it into the header.

    What you could do is to subclass QHeaderView, reimplement paintSection() and then call [drawPrimitive()](http://doc.qt.io/qt-5/qstyle.html#drawPrimitivewith] PE_IndicatorCheckBox in the section where you want to have this checkbox.

    You would also need to reimplement the mousePressEvent() to detect when the checkbox is clicked, in order to paint the checked and unchecked states.

    The example below illustrates how this can be done:

    #include <QtWidgets>
    
    class MyHeader : public QHeaderView
    {
    public:
       MyHeader(Qt::Orientation orientation, QWidget * parent = nullptr) : QHeaderView(orientation, parent) {}
    
    protected:
       void 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(10,10,10,10);
                if (isOn)
                    option.state = QStyle::State_On;
                else
                    option.state = QStyle::State_Off;
                this->style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter);
           }
    
       }
       void mousePressEvent(QMouseEvent *event)
       {
           if (isOn)
               isOn = false;
           else
               isOn = true;
           this->update();
           QHeaderView::mousePressEvent(event);
       }
    private:
        bool isOn;
    };
    
    int main(int argc, char **argv)
    {
       QApplication app(argc, argv);
       QTableWidget table;
       table.setRowCount(4);
       table.setColumnCount(3);
    
       MyHeader *myHeader = new MyHeader(Qt::Horizontal, &table);
       table.setHorizontalHeader(myHeader);
       table.show();
       return app.exec();
    }
    

    To have all your other checkboxes checked, you could use the pressed() signal which should be fired from the mousePressEvent() and connect it to a custom slot where you set all your checkboxes checked or unchecked.