很常见的一种需求,但是很可惜Qt没有一种比较简单的实现方式。在网上找了很多资料也没有找到,实现方法比较复杂,但是经过 我的实验,下面这种方法是可行的,而且有很多意想不到的好处。
定义接口类
存储当前hovered行,split行是实现其他需求的可以不考虑
class IView
{
public:
virtual void setHoveredRow(int row) { row_ = row; }
virtual void setSplitRow(int row) { splitRow_ = row; }
int hoveredRow() const { return row_; }
int splitRow() const { return splitRow_; }
private:
int row_ = -1;
int splitRow_ = -1;
};
表格绘制代理类delegate
这个类里面可以实现对每个item的定制化绘制,下面split相关的实现可以不考虑
class Delegate : public QStyledItemDelegate
{
public:
Delegate(QObject *parent = 0);
~Delegate();
void setView(IView *view) { view_ = view; }
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
private:
IView *view_ = nullptr;
};
Delegate::Delegate(QObject *parent /* = 0 */)
: QStyledItemDelegate(parent)
{
}
Delegate::~Delegate()
{
}
void Delegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyleOptionViewItem opt = option;
// 去掉焦点虚线框
if (opt.state & QStyle::State_HasFocus) {
opt.state = opt.state & ~QStyle::State_HasFocus;
}
// hover时整行高亮
if (index.row() == view_->hoveredRow()) {
opt.state |= QStyle::State_MouseOver;
} else {
opt.state &= ~QStyle::State_MouseOver;
}
// 表格上部分样式
int splitRow = view_->splitRow();
bool isFollower = index.row() <= splitRow;
painter->fillRect(opt.rect, isFollower ? QColor("#0C1E1E") : QColor("#000000"));
// 上下两部分分隔线
if (index.row() == splitRow) {
QRect rect = opt.rect;
rect.setTop(rect.top() + rect.height() - 2);
painter->fillRect(rect, QColor("#273F3D"));
}
QStyledItemDelegate::paint(painter, opt, index);
}
table的具体应用
结合上面两个类来实现我们的需求,由于去掉了一些业务代码下面的类并不完整,不过不影响我们要实现的需求。
class TableWidget : public QTableWidget, public IView
{
Q_OBJECT
public:
TableWidget(QWidget *parent = 0);
protected:
void mouseMoveEvent(QMouseEvent *event) override;
void leaveEvent(QEvent *event) override;
}
TableWidget::TableWidget(QWidget *parent)
: QTableWidget(parent)
{
// 隐藏水平垂直头
this->verticalHeader()->hide();
this->horizontalHeader()->hide();
// 去掉虚线框
this->setShowGrid(false);
// 单行选择
this->setSelectionBehavior(QAbstractItemView::SelectRows);
this->setSelectionMode(QAbstractItemView::SingleSelection);
// 禁止编辑
this->setEditTriggers(QTableWidget::NoEditTriggers);
// 关闭水平垂直滚动条,使用自定义的悬浮滚动条(为了满足样式需求)
this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
// 鼠标跟踪
this->setMouseTracking(true);
// 扩展最后一列
this->horizontalHeader()->setStretchLastSection(true);
// 支持拖拽
this->setDragDropMode(QAbstractItemView::DragDrop);
this->setDragEnabled(true);
this->setAcceptDrops(true);
// 应用基本qss样式
this->setProperty("qssname", "BindingTraderTable");
Delegate *delegate = new Delegate();
delegate->setView(this);
this->setItemDelegate(delegate);
}
void TableWidget::mouseMoveEvent(QMouseEvent *event)
{
QModelIndex index = this->indexAt(event->pos());
if (index.isValid()) {
this->setHoveredRow(index.row());
}
QTableWidget::mouseMoveEvent(event);
}
void TableWidget::leaveEvent(QEvent *event)
{
this->setHoveredRow(-1);
QTableWidget::leaveEvent(event);
}
主要是通过mouseMoveEvent来设置当前hovered的行,然后delegate就可以根据当前row来绘制样式。 虽然上面实现看起来有些复杂,但是这种分层方式是很灵活的在业务中很多对于table的需求都是 很有必要的,也容易扩充一些新的功能。