Qt QTableView how to have a checkbox only column

前端 未结 2 457
-上瘾入骨i
-上瘾入骨i 2020-12-08 03:35

We are using a QTableView with Qt 4.6.3, and need a column that only has a checkbox in each cell. We\'re using a custom subclass of QAbstractTableModel

相关标签:
2条回答
  • 2020-12-08 03:48

    Note: The answer of Dave works also for Python using PySide or PyQt4. I translated it, and it works great. Additionally, I added the functionality that the checkbox does not accept user input and is shown in a ReadOnly state if the cell is not editable. Thanks Dave for your code!

    class CheckBoxDelegate(QStyledItemDelegate):
    
        def createEditor(self, parent, option, index):
            '''
            Important, otherwise an editor is created if the user clicks in this cell.
            '''
            return None
    
        def paint(self, painter, option, index):
            '''
            Paint a checkbox without the label.
            '''
            checked = bool(index.model().data(index, Qt.DisplayRole))
            check_box_style_option = QStyleOptionButton()
    
            if (index.flags() & Qt.ItemIsEditable) > 0:
                check_box_style_option.state |= QStyle.State_Enabled
            else:
                check_box_style_option.state |= QStyle.State_ReadOnly
    
            if checked:
                check_box_style_option.state |= QStyle.State_On
            else:
                check_box_style_option.state |= QStyle.State_Off
    
            check_box_style_option.rect = self.getCheckBoxRect(option)
                if not index.model().hasFlag(index, Qt.ItemIsEditable):
                check_box_style_option.state |= QStyle.State_ReadOnly
    
            QApplication.style().drawControl(QStyle.CE_CheckBox, check_box_style_option, painter)
    
    
        def editorEvent(self, event, model, option, index):
            '''
            Change the data in the model and the state of the checkbox
            if the user presses the left mousebutton or presses
            Key_Space or Key_Select and this cell is editable. Otherwise do nothing.
            '''
            if not (index.flags() & Qt.ItemIsEditable) > 0:
                return False
    
            # Do not change the checkbox-state
            if event.type() == QEvent.MouseButtonRelease or event.type() == QEvent.MouseButtonDblClick:
                if event.button() != Qt.LeftButton or not self.getCheckBoxRect(option).contains(event.pos()):
                    return False
                if event.type() == QEvent.MouseButtonDblClick:
                    return True
            elif event.type() == QEvent.KeyPress:
                if event.key() != Qt.Key_Space and event.key() != Qt.Key_Select:
                    return False
            else:
                return False
    
            # Change the checkbox-state
            self.setModelData(None, model, index)
            return True
    
        def setModelData (self, editor, model, index):
            '''
            The user wanted to change the old state in the opposite.
            '''
            newValue = not bool(index.model().data(index, Qt.DisplayRole))
            model.setData(index, newValue, Qt.EditRole)
    
    
        def getCheckBoxRect(self, option):
            check_box_style_option = QStyleOptionButton()
            check_box_rect = QApplication.style().subElementRect(QStyle.SE_CheckBoxIndicator, check_box_style_option, None)
            check_box_point = QPoint (option.rect.x() +
                                 option.rect.width() / 2 -
                                 check_box_rect.width() / 2,
                                 option.rect.y() +
                                 option.rect.height() / 2 -
                                 check_box_rect.height() / 2)
            return QRect(check_box_point, check_box_rect.size())
    
    0 讨论(0)
  • 2020-12-08 03:54

    Here is a solution. For this to work properly, your column should not have the Qt::ItemIsEditable or Qt::ItemIsUserCheckable flags set. This reads the boolean values from Qt::DisplayRole and calls setData() with Qt::EditRole (i.e. not Qt::CheckStateRole.)

    #include "check_box_delegate.h"
    
    #include <QtGui/QApplication>
    #include <QtGui/QMouseEvent>
    
    static QRect CheckBoxRect(const QStyleOptionViewItem &view_item_style_options) {
      QStyleOptionButton check_box_style_option;
      QRect check_box_rect = QApplication::style()->subElementRect(
          QStyle::SE_CheckBoxIndicator,
          &check_box_style_option);
      QPoint check_box_point(view_item_style_options.rect.x() +
                             view_item_style_options.rect.width() / 2 -
                             check_box_rect.width() / 2,
                             view_item_style_options.rect.y() +
                             view_item_style_options.rect.height() / 2 -
                             check_box_rect.height() / 2);
      return QRect(check_box_point, check_box_rect.size());
    }
    
    CheckBoxDelegate::CheckBoxDelegate(QObject *parent)
      : QStyledItemDelegate(parent) {
    }
    
    void CheckBoxDelegate::paint(QPainter *painter,
                                 const QStyleOptionViewItem &option,
                                 const QModelIndex &index) const {
      bool checked = index.model()->data(index, Qt::DisplayRole).toBool();
    
      QStyleOptionButton check_box_style_option;
      check_box_style_option.state |= QStyle::State_Enabled;
      if (checked) {
        check_box_style_option.state |= QStyle::State_On;
      } else {
        check_box_style_option.state |= QStyle::State_Off;
      }
      check_box_style_option.rect = CheckBoxRect(option);
    
      QApplication::style()->drawControl(QStyle::CE_CheckBox,
                                         &check_box_style_option,
                                         painter);
    }
    
    // This is essentially copied from QStyledItemEditor, except that we
    // have to determine our own "hot zone" for the mouse click.
    bool CheckBoxDelegate::editorEvent(QEvent *event,
                                       QAbstractItemModel *model,
                                       const QStyleOptionViewItem &option,
                                       const QModelIndex &index) {
      if ((event->type() == QEvent::MouseButtonRelease) ||
          (event->type() == QEvent::MouseButtonDblClick)) {
        QMouseEvent *mouse_event = static_cast<QMouseEvent*>(event);
        if (mouse_event->button() != Qt::LeftButton ||
            !CheckBoxRect(option).contains(mouse_event->pos())) {
          return false;
        }
        if (event->type() == QEvent::MouseButtonDblClick) {
          return true;
        }
      } else if (event->type() == QEvent::KeyPress) {
        if (static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space &&
            static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select) {
          return false;
        }
      } else {
        return false;
      }
    
      bool checked = index.model()->data(index, Qt::DisplayRole).toBool();
      return model->setData(index, !checked, Qt::EditRole);
    }
    
    0 讨论(0)
提交回复
热议问题