表格整行的 selected、hover 高亮需求很常见,但使用 Qt 提供的开箱即用的方法根本无法实现这个需求(至少在当前的时间节点是不行的);想要实现这个效果必须要费一点点力气,我们尽量选择较为简单的方法。
话不多说,先看效果图:
实现的原理
经常用 QWidgets 的朋友应该都知道,框架中的 QTableWidget、QTreeWidget、QListWidget 等控件以及它们的基类 xxView 都是 MVD(model、view、delegate) 的结构,实现整行选中我们选择子类化 QStyledItemDelegate 的方式,这个方法较为简单,不会增加太多的心智负担,只需要以下几个步骤:
- 创建一个类继承于 QStyledItemDelegate
- 将外部将要使用这个 delegate 的 QTableWidget 传入,这里我选择了作为构造函数的参数,形式无所谓,只要确保在使用的时候此对象有效即可
- 重写基类的 paint 函数,客制化逻辑
- 创建对象,设置给 QTableWidget
头文件
#include <QStyledItemDelegate>
#include <QTableWidget>
class HoveredRowItemDelegate : public QStyledItemDelegate
{
public:
explicit HoveredRowItemDelegate(QTableWidget *parent = nullptr);
protected:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
private:
const QTableWidget *tableWidget{};
};
源文件
#include "hoveredrowitemdelegate.h"
#include <QPainter>
HoveredRowItemDelegate::HoveredRowItemDelegate(QTableWidget *parent)
: QStyledItemDelegate{parent}
{
tableWidget = parent;
}
void HoveredRowItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
//选中状态或是在鼠标下方的状态
if(option.state.testFlag(QStyle::State_Selected) || option.state.testFlag(QStyle::State_MouseOver))
{
QTableWidgetItem *hoveredItem = tableWidget->item(index.row(), index.column());
if(hoveredItem)
{
const int row = hoveredItem->row();
//遍历列,绘制此行的所有列
for(int column = 0; column < tableWidget->columnCount(); column++)
{
QTableWidgetItem *item = tableWidget->item(row, column);
if(item)
{
QModelIndex index = tableWidget->model()->index(row, column);
QStyleOptionViewItem itemOption = option;
itemOption.index = index;
itemOption.rect = tableWidget->visualItemRect(item);
QStyledItemDelegate::paint(painter, itemOption, index);
}
}
}
}
else
{
return QStyledItemDelegate::paint(painter, option, index);
}
}
使用
auto delegate = new HoveredRowItemDelegate(ui->tableWidget);
ui->tableWidget->setItemDelegate(delegate);
QTableWidget 设置下样式表:
QTableView::item:hover
{
background-color: red;
}
QTableView::item:selected
{
background-color: blue;
}