Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checkbox in a header cell in QTableView

Tags:

c++

qt

qt4

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)

like image 592
SRF Avatar asked Feb 04 '14 16:02

SRF


People also ask

How to add a column of checkable cells in qsqlquerymodel?

I think the best way to have a column of checkable cells is to create your item model, e.g. by subclassing the QSqlQueryModel . You must reimplement the flags () method to make checkable the cells. Also you need to reimplement the data () method to return the check state and the setData () method and to set the check state.

Who can see the checkbox in qtableview?

Checkbox in Qtableview. This topic has been deleted. Only users with topic management privileges can see it.

How to add a check box in a table cell?

Clicking the check box in the header causes either to select or deselect all rows. 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.

How to work with the qtablewidget?

One of the first problems in working with The QTableWidget , which are trying to solve, start to work with this widget - setting QCheckBox in the middle of the cell. The fact that the standard check box in QTableWidget checkbox attached to the left edge of the cell, while the right side is given for explanatory text.


3 Answers

You cannot do it - Qt doesn't support check boxes 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 further information and its realization using custom QHeaderView

like image 178
Alex Telishev Avatar answered Oct 14 '22 12:10

Alex Telishev


Here is a bit modified/fixed (checkbox was displayed as disabled and redrawing didn't work) 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();
    }
}

usage

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);
like image 42
Alex P. Avatar answered Oct 14 '22 10:10

Alex P.


I am surprised such a hacky solution is recommend and used.

First: The check state should be stored in the model. All the tools are already there.

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 toggle checked state simply by handling 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 point we have fully functional checking behavior, only part missing is the visualization. The smartest way to go is to again use the model, taking advantage of the 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, one can draw a real checkbox there using the styled drawing.

Notice, this solution does not require sub-classing and custom widgets.

Also, the check state is decoupled from the view/UI. The only downside is fact the visuals are handled by the model, but this is optional - any way can be used to draw the check state, the one from the alternative answers included.

like image 37
user362515 Avatar answered Oct 14 '22 12:10

user362515