Checkbox in a header cell in QTableView

后端 未结 3 793
礼貌的吻别
礼貌的吻别 2020-12-16 20:57

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 desel

相关标签:
3条回答
  • 2020-12-16 21:05

    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);
    
    0 讨论(0)
  • 2020-12-16 21:06

    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.

    0 讨论(0)
  • 2020-12-16 21:26

    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

    0 讨论(0)
提交回复
热议问题