我已经为 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);
}
我面临以下问题...
- 我必须点击按钮 3 次才能将文本从“否”更改为"is"。
- 按钮的突出显示不正确(当文本为
yes
时按钮应突出显示)...
需要帮助...
最佳答案
必须单击一个单元格 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
插槽将在 createEditor
和 setEditorData
退出后被调用(通过在每个开始时使用打印语句很容易检查功能)。如果您删除排队的连接,那么 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 机器上)如下所示:
关于c++ - 使用 QStyledItemDelegate 时 QPushButton 的绘制方法面临问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73810341/