【问题标题】:QStyledItemDelegate: how to make checkbox button to change its state on clickQStyledItemDelegate:如何使复选框按钮在单击时更改其状态
【发布时间】:2016-04-21 19:17:17
【问题描述】:

我有一个代表MyDelegate,用于QListWidget。委托派生自QStyledItemDelegateMyDelegate 的目标之一是在ListWidget 的每一行上放置一个复选框按钮。它在MyDelegatepaint() 事件中完成:

void MyDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QStyledItemDelegate::paint(painter, option, index);
    // ... drawing other delegate elements

    QStyleOptionButton checkbox;
    // setting up checkbox's size and position

    // now draw the checkbox
    QApplication::style()->drawControl(QStyle::CE_CheckBox, &checkbox, painter);
}

起初我以为复选框会在单击时自动更改其状态,因为我指定了QStyle::CE_CheckBox。但事实并非如此。看来我必须手动指定复选框的视觉行为。

数据方面,当用户点击该复选框时,会发出某些信号并更改场景数据。我在editorEvent()执行这个动作:

bool MyDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)  
{
    if (event->type() == QEvent::MouseButtonRelease) {
        if (/* within checkbox area */)
            emit this->clickedCheckbox(index);
    }
}  

后端部分有效。但是,我不知道如何使复选框按钮将其视觉状态从选中更改为未选中,然后向后更改。

我意识到我可以通过在paint() 中执行类似的操作来手动更改复选框状态:

checkbox.state = someCondition? (QStyle::State_Enabled | QStyle::State_On) :
                           (QStyle::State_Enabled | QStyle::State_Off) ;

QStyle::State_On/Off 可以手动更改复选框状态。

但我不知道如何设置 someCondition 以及我应该在哪里设置它。我尝试将它作为私有 bool 变量引入,当复选框区域被点击时,它将在 editorEvent() 中设置,但是,它不会产生所需的行为:它将 ListWidget 的所有其他复选框设置为相同的视觉状态。因此,它的行为类似于所有复选框的某种全局条件。

我觉得,要完成我的任务,我必须重新实现按钮并使其在单击时更改复选框状态。但是我迷路了,不知道如何解决这个问题。从QStyleOptionButton API 我看不到clicked() 或我可以使用的任何其他方法。

所以,问题是:如何使复选框在视觉上表现得像一个复选框?如果我需要重新实现一个复选框,那么我应该继承什么类?

【问题讨论】:

    标签: qt qcheckbox qstyleditemdelegate


    【解决方案1】:

    您可以在MyDelegate::editorEvent 中设置一些描述您的复选框状态的值,然后使用它来绘制适当的复选框:

    const int CHECK_ROLE = Qt::UserRole + 1;
    
    bool MyDelegate::editorEvent(QEvent *event,
                                QAbstractItemModel *model,
                                const QStyleOptionViewItem &option,
                                const QModelIndex &index)
    {
        if (event->type() == QEvent::MouseButtonRelease)
        {
            bool value = index.data(CHECK_ROLE).toBool();
    
            // invert checkbox state
            model->setData(index, !value, CHECK_ROLE);
    
            return true;
        }
    
        return QStyledItemDelegate::editorEvent(event, model, option, index);
    }
    
    void MyDelegate::paint(QPainter *painter, 
                          const QStyleOptionViewItem &option, 
                          const QModelIndex &index) const
    {
        QStyleOptionButton cbOpt;
        cbOpt.rect = option.rect;
    
        bool isChecked = index.data(CHECK_ROLE).toBool();
        if (isChecked)
        {
            cbOpt.state |= QStyle::State_On;
        }
        else
        {
            cbOpt.state |= QStyle::State_Off;
        }
    
        QApplication::style()->drawControl(QStyle::CE_CheckBox, &cbOpt, painter);
    }
    

    【讨论】:

    • 完美,有效。澄清一下:你是怎么想出CHECK_ROLE变量的,为什么你把它完全分配给Qt::UserRole + 1。这是一些常见的做法吗?
    • QAbstractItemModel派生模型可以存储不同角色的数据。其中一些角色是预定义的,例如Qt::DisplayRoleQt::UserRole 是您可以用于特殊目的的第一个角色。所以,实际上CHECK_ROLE 可能等于那个值。见doc.qt.io/qt-4.8/qt.html#ItemDataRole-enum
    【解决方案2】:

    我添加了以下内容:

    • data(Qt:CheckStateRole) 已切换。
    • 启用/禁用项目。
    • 在单元格中将框居中。
    • 仅使用鼠标左键切换复选标记,以及单击复选标记时(不是整个单元格)。

    代码如下:

    // -------------------------------- //
    class GFQtCheckboxItemDelegate : public QStyledItemDelegate
    {
    Q_OBJECT
    public:
        const int CHECK_ROLE = Qt::CheckStateRole;
    
        // dont't let the default QStyledItemDelegate create the true/false combobox
        QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE
        {
            (void)parent;
            (void)option;
            (void)index;
            return nullptr;
        }
    
    
        QRect GetCheckboxRect(const QStyleOptionViewItem &option)const
        {
            QStyleOptionButton opt_button;
            opt_button.QStyleOption::operator=(option);
            QRect sz = QApplication::style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &opt_button);
            QRect r = option.rect;
            // center 'sz' within 'r'
            int dx = (r.width() - sz.width())/2;
            int dy = (r.height()- sz.height())/2;
            r.setTopLeft(r.topLeft() + QPoint(dx,dy));
            r.setWidth(sz.width());
            r.setHeight(sz.height());
    
            return r;
        }
    
    
        // click event
        bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
        {
            if (event->type() == QEvent::MouseButtonRelease)
            {
                QMouseEvent* pME = static_cast<QMouseEvent*>(event);
                if(pME->button() == Qt::LeftButton)
                {
                    QRect ro = GetCheckboxRect(option);
                    QPoint pte = pME->pos();
                    if(ro.contains(pte) )
                    {
                        bool value = index.data( CHECK_ROLE).toBool();
    
                        // invert checkbox state
                        model->setData(index, !value, CHECK_ROLE);
    
                        return true;
                    }
                }
            }
    
            return QStyledItemDelegate::editorEvent(event, model, option, index);
        }
    
        void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
        {
            QStyleOptionButton cbOpt;
            cbOpt.rect = GetCheckboxRect(option);
    
            bool isChecked = index.data(CHECK_ROLE ).toBool();
            if (isChecked)
            {
                cbOpt.state |= QStyle::State_On;
            }
            else
            {
                cbOpt.state |= QStyle::State_Off;
            }
    
            QVariant enabled = index.data(Qt::ItemIsEnabled);
            if(enabled.isNull() || enabled.toBool() )
            {
                cbOpt.state |= QStyle::State_Enabled;
            }
    
            QApplication::style()->drawControl(QStyle::CE_CheckBox, &cbOpt, painter);
        }
    };
    

    为了完整起见,以下是如何应用它:

    m_p_QTableView->setItemDelegateForColumn(iTableColumnIndex, new GFQtCheckboxItemDelegate() );
    ...
    bool yesno ...;
    pGrid->setData(index, yesno, Qt::CheckStateRole);
    

    【讨论】:

      【解决方案3】:

      如果将 QListWidget 更改为 QListModel 和 QListView,这会容易得多。

      Qt::ItemFlags ListModel::flags(const QModelIndex &index) const
      {
          return QAbstractListModel::flags(index) | Qt::ItemIsUserCheckable;
      }
      
      QVariant ListModel::data(const QModelIndex &index, int role) const
      {
          if (role == Qt::EditRole) {
              return m_checkStateForRow[index.row()];
          }
      }
      
      bool ListModel::setData(const QModelIndex &index, const QVariant &value, int role)
      {
          if (role == Qt::CheckStateRole) {
              m_checkStateForRow[index.row()] = value.toInt();
              emit dataChanged(index, index, {role});
              return true;
          }
          return false;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-04
        • 1970-01-01
        • 2015-02-16
        • 2016-12-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多