【问题标题】:Qt QTableView how to have a checkbox only columnQt QTableView如何有一个只有复选框的列
【发布时间】:2011-03-22 17:57:57
【问题描述】:

我们在 Qt 4.6.3 中使用QTableView,并且需要一个在每个单元格中只有一个复选框的列。我们使用QAbstractTableModel 的自定义子类作为QTableView 的模型。现在,我们有一个通过设置Qt::ItemIsUserCheckable 标志的复选框。但是我们不知道如何去掉复选框旁边的空白文本框!

我们怎样才能让列只有有一个复选框,没有别的?

【问题讨论】:

    标签: qt qt4 qtableview qabstracttablemodel


    【解决方案1】:

    这里有一个解决方案。为使其正常工作,您的列应该设置Qt::ItemIsEditableQt::ItemIsUserCheckable 标志。这会从Qt::DisplayRole 中读取布尔值并使用Qt::EditRole 调用setData()(即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);
    }
    

    【讨论】:

      【解决方案2】:

      注意:Dave 的答案也适用于使用 PySide 或 PyQt4 的 Python。我翻译了它,效果很好。此外,我添加了复选框不接受用户输入并在单元格不可编辑时显示为只读状态的功能。 感谢 Dave 的代码!

      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())
      

      【讨论】:

      • raorao 和@Niklas,感谢这个音译,但我无法让代码工作。我已经发布了一个跟进here.,其中包含我的实现示例。
      • @Dave Mateer 真的令人印象深刻....我对if not (index.flags() &amp; Qt.ItemIsEditable) &gt; 0: 有点困惑。为什么要检查索引上是否存在标志?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-09-10
      • 2016-01-10
      • 2014-02-19
      • 2013-07-18
      • 1970-01-01
      • 2012-07-26
      • 1970-01-01
      相关资源
      最近更新 更多