QStyledItemDelegate:如何使复选框按钮在单击时更改其状态

vic*_*ann 3 qt qcheckbox qstyleditemdelegate

我有一个MyDelegate用于QListWidget. 委托派生自QStyledItemDelegate. 的目标之一MyDelegate是在ListWidget. 它是在以下paint()事件中完成的MyDelegate

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);
}
Run Code Online (Sandbox Code Playgroud)

起初我认为复选框会在单击时自动更改其状态,因为我指定了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);
    }
}  
Run Code Online (Sandbox Code Playgroud)

后端部分工作。但是,我无法弄清楚如何使复选框按钮将其视觉状态从选中更改为未选中,然后向后更改。

我意识到我可以通过以下方式手动更改复选框状态paint()

checkbox.state = someCondition? (QStyle::State_Enabled | QStyle::State_On) :
                           (QStyle::State_Enabled | QStyle::State_Off) ;
Run Code Online (Sandbox Code Playgroud)

QStyle::State_On/Off 手动复选框状态更改的技巧。

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

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

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

han*_*ank 5

您可以设置一些描述复选框状态的值,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);
}
Run Code Online (Sandbox Code Playgroud)

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

Kun*_*hoo 5

我添加了以下内容:

  • 数据(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);
    }
};
Run Code Online (Sandbox Code Playgroud)

为了完整起见,以下是应用它的方法:

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