c++ - 使用 QStyledItemDelegate 时 QPushButton 的绘制方法面临问题

标签 c++ qt widget qt5

我已经为 QTableView 中的列实现了 PushButtonDelegate,因为我们使用 QSortFilterProxyModel 来过滤 TableView 。 我将此委托(delegate)设置为如下所示的其中一列...

table_->setItemDelegateForColumn(11, new PushButtonDelegate(table_));

PushButtonDelegate 派生自 QStyledItemDelegate,我已经覆盖了 createEditor、setEditorData、setModelData、updateEditorGeometry 和 paint 方法,如下所示。 .

PushButtonDelegate::PushButtonDelegate(QObject* parent) :QStyledItemDelegate(parent)
{
    if (const auto table_view = qobject_cast<QTableView*>(parent))
    {
        my_widget_ = table_view;
        btn_ = new QPushButton(button_text_, my_widget_);
        btn_->hide();
        //my_widget_->setMouseTracking(true);
        connect(my_widget_, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(CellEntered(QModelIndex)));
        is_one_cell_in_edit_mode_ = false;
    }
}
QWidget* PushButtonDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    if (index.model()->headerData(index.column(), Qt::Horizontal, Qt::DisplayRole).toString() == constants::kUse)
    {
        const auto btn = new QPushButton(parent);
        const auto value = index.data().toString();
        btn->setCheckable(true);
        btn->setChecked(value == "Yes" ? true : false);
        btn->setFocusPolicy(Qt::NoFocus);
        connect(btn, SIGNAL(clicked(bool)), this, SLOT(UpdateUseButton(bool)));

        return btn;
    }
    else
    {
        return QStyledItemDelegate::createEditor(parent, option, index);
    }
}

void PushButtonDelegate::UpdateUseButton(bool checked) const
{
    if (const auto selection_button = dynamic_cast<QPushButton*>(sender()))
    {
        const QString value = selection_button->text();
        selection_button->setText(value == "Yes" ? "No" : "Yes");
        selection_button->setChecked(value == "Yes" ? false : true);
        selection_button->setCheckable(true);
    }
}

void PushButtonDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
    if (index.model()->headerData(index.column(), Qt::Horizontal, Qt::DisplayRole).toString() == constants::kUse)
    {
        const auto value = index.model()->data(index, Qt::EditRole).toString();
        const auto pb = dynamic_cast<QPushButton*>(editor);
        pb->setChecked(value == "Yes" ? true : false);
        pb->setText(value);
        pb->setCheckable(true);
    }
    else
    {
        QStyledItemDelegate::setEditorData(editor, index);
    }
}

void PushButtonDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
{
    if (index.model()->headerData(index.column(), Qt::Horizontal, Qt::DisplayRole).toString() == constants::kUse)
    {
        const auto pb = dynamic_cast<QPushButton*>(editor);
        const auto value = pb->text();
        pb->setChecked(value == "Yes" ? true : false);
        pb->setCheckable(true);
        const auto ptr = dynamic_cast<QSortFilterProxyModel*>(model);
        ptr->setData(index, value, Qt::EditRole);
    }
    else
    {
        QStyledItemDelegate::setModelData(editor, model, index);
    }
}

void PushButtonDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    if (index.model()->headerData(index.column(), Qt::Horizontal, Qt::DisplayRole).toString() == constants::kUse)
    {
        painter->save();
        btn_->setGeometry(option.rect);
        const auto value = index.data().toString();
        btn_->setCheckable(true);
        btn_->setText(value);
        btn_->setChecked(value == "Yes" ? true : false);

        if (value == "Yes")
            painter->fillRect(option.rect, option.palette.highlight());
        else
            painter->fillRect(option.rect, option.palette.shadow());

        const QPixmap map = btn_->grab();
        painter->drawPixmap(option.rect.x(), option.rect.y(), map);

        painter->restore();
    }
    else
    {
        QStyledItemDelegate::paint(painter, option, index);
    }
}

void PushButtonDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    editor->setGeometry(option.rect);
}

我面临以下问题...

  1. 我必须点击按钮 3 次才能将文本从“否”更改为"is"。
  2. 按钮的突出显示不正确(当文本为 yes 时按钮应突出显示)...

enter image description here

需要帮助...

最佳答案

必须单击一个单元格 3 次才能更改值的原因是您需要双击进行编辑(这会创建编辑器),然后再单击一次以调用 UpdateUseButton。您不需要这样做,您可以在创建编辑器时执行单击。事实上,您甚至不需要 UpdateUseButton,因为您可以在 setModelData 中进行必要的更改。将以下插槽添加到您的 PushButtonDelegate:

void commitAndCloseEditor();

然后像这样实现这个类:

QWidget* PushButtonDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    if (index.model()->headerData(index.column(), Qt::Horizontal, Qt::DisplayRole).toString() == constants::kUse)
    {
        const auto btn = new QPushButton(parent);
        const auto value = index.data().toString();
        btn->setCheckable(true);
        btn->setChecked(value == "Yes" ? true : false);
        btn->setFocusPolicy(Qt::NoFocus);
        
        // You don't need this, you can update your data in setModelData instead
        // connect(btn, SIGNAL(clicked(bool)), this, SLOT(UpdateUseButton(bool)));
        
        // the click will commit the data and close the editor
        // we connect it with a queued connection so that the slot is called after this function exits
        connect(btn, &QPushButton::clicked, this, &PushButtonDelegate::commitAndCloseEditor, Qt::QueuedConnection);
        emit btn->clicked(false); // perform a click programatically to that the commitAndCloseEditor will be called after we exit this function 
        return btn;
    }
    else
    {
        return QStyledItemDelegate::createEditor(parent, option, index);
    }
}

void PushButtonDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
    if (index.model()->headerData(index.column(), Qt::Horizontal, Qt::DisplayRole).toString() == constants::kUse)
    {
        //nothing to do when you are in your delegate column
    }
    else
    {
        QStyledItemDelegate::setEditorData(editor, index);
    }
}

void PushButtonDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
{
    if (index.model()->headerData(index.column(), Qt::Horizontal, Qt::DisplayRole).toString() == constants::kUse)
    {
        // set your model data here
        model->setData(index, (index.data().toString() == "Yes") ? "No" : "Yes");
    }
    else
    {
        QStyledItemDelegate::setModelData(editor, model, index);
    }
}

void PushButtonDelegate::commitAndCloseEditor()
{
    auto* editor = qobject_cast<QWidget*>(sender());
    emit commitData(editor);
    emit closeEditor(editor);
}

请注意,我使用了 Qt::QueuedConnnection 来连接 clicked 信号。这确保了 commitAndCloseEditor 插槽将在 createEditorsetEditorData 退出后被调用(通过在每个开始时使用打印语句很容易检查功能)。如果您删除排队的连接,那么 commitAndCloseEditor 将在 createEditor 返回实际编辑器之前被调用,因此插槽将不执行任何操作,因为编辑器尚未打开。

现在,对于 paint 方法... btn_->setChecked 似乎是无法在按钮中正确显示背景颜色的原因。我不确定为什么会这样,但解决方法是使用如下样式表:

void PushButtonDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    if (index.model()->headerData(index.column(), Qt::Horizontal, Qt::DisplayRole).toString() == constants::kUse)
    {
        painter->save();
        btn_->setGeometry(option.rect);
        const auto value = index.data().toString();
        
        btn_->setText(value);
        
        QString stylesheet = QString("background-color: %1").arg((value == "Yes") ? "green" : "red"  );
        btn_->setStyleSheet(stylesheet);

        const QPixmap map = btn_->grab();
        painter->drawPixmap(option.rect.x(), option.rect.y(), map);

        painter->restore();
    }
    else
    {
        QStyledItemDelegate::paint(painter, option, index);
    }
}

现在应该可以通过双击来切换值,结果(在我的 Windows 机器上)如下所示:

outcome

关于c++ - 使用 QStyledItemDelegate 时 QPushButton 的绘制方法面临问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73810341/

相关文章:

c++ - Qt 中的动画绑定(bind)更改

c++ - 如何使用 GCC 或 clang 导出函数名和变量名?

c++ - Qt 在可选列表中显示来自资源的图标

qt - 如何创建多窗口 Qt 应用程序

python - QProcess 将参数传递给 python 脚本

android - 如何在 Android 应用程序中使用 Geocoder 查找特定位置的大洲

c++ - 如何使用 deltaTime 产生一致的运动

javascript - 使用 React 创建可嵌入的小部件

android - 如何将 MediaBrowserService 与小部件一起使用?

c++ - 外部链接的缺点