Qt自定义Delegate实现QTableWidget整行选中圆角矩形高亮效果

问题背景

参照一个现有的Linux桌面应用,尽可能的模仿它的UI,其中有一个UI效果就是列表整行选中后是一个圆角矩形高亮效果,如下图所示。
Qt自定义Delegate实现QTableWidget整行选中圆角矩形高亮效果_第1张图片

参考代码

先放代码,实现的思路就是用代理来重绘我们想要的效果。

#include 
class MyDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    MyDelegate(QWidget *parent = 0) : QStyledItemDelegate(parent) {}
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;

private:
    void paintBackground(QPainter *painter, const QRect &rect, const int &column, const QBrush &brush) const;
    void paintBackgroundBase(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
    void paintBackgroundAlternateBase(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
    void paintBackgroundHighLighted(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;

    void paintForegroundText(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
void MyDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
                         const QModelIndex &index) const
{
    if (option.state & QStyle::State_Selected)
    {
        paintBackgroundHighLighted(painter, option, index);
    }
    else if ( index.row() % 2 == 0)
    {
        paintBackgroundBase(painter, option, index);
    }
    else
    {
        paintBackgroundAlternateBase(painter, option, index);
    }

    paintForegroundText(painter, option, index);
}

void MyDelegate::paintBackgroundHighLighted(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    paintBackground(painter, option.rect, index.column(), option.palette.highlight());
}

void MyDelegate::paintBackgroundBase(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    paintBackground(painter, option.rect, index.column(), option.palette.base());
}

void MyDelegate::paintBackgroundAlternateBase(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    paintBackground(painter, option.rect, index.column(), option.palette.alternateBase());
}

void MyDelegate::paintBackground(QPainter *painter, const QRect &rect, const int &column, const QBrush &brush) const
{
    int width = rect.width();
    if ( column == 0 /*option.viewItemPosition == QStyleOptionViewItem::Beginning*/)
    {
        QPainterPath path;
        path.setFillRule(Qt::WindingFill);
        path.addRoundedRect(rect, 8, 8);
        path.addRect(QRect(rect.x() + width / 2, rect.y(), width / 2, rect.height()));
        painter->fillPath(path, brush);
    }
    else if ( column == 2/*option.viewItemPosition == QStyleOptionViewItem::End*/)
    {
        QPainterPath path;
        path.setFillRule(Qt::WindingFill);
        path.addRoundedRect(rect, 8, 8);
        path.addRect(QRect(rect.x(), rect.y(), width / 2, rect.height()));
        painter->fillPath(path, brush);
    }
    else
    {
         painter->fillRect(rect, brush);
    }
}

void MyDelegate::paintForegroundText(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QFontMetrics fontMetrics(painter->font());
    QString msg = fontMetrics.elidedText(index.data().toString(), Qt::ElideRight, option.rect.width());
    QApplication::style()->drawItemText(painter, option.rect, option.displayAlignment, QApplication::palette(), true, msg);
}

代码讲解

推荐继承自QStyledItemDelegate,因为它可以使用当前的Sytle绘制Items,而继承QItemDelegate需要自定义Sytle。具体可以参考Qt的手册 QStyledItemDelegate vs. QItemDelegate,讲解的比较详细。

QStyledItemDelegate vs. QItemDelegate
Since Qt 4.4, there are two delegate classes: QItemDelegate and QStyledItemDelegate. However, the default delegate is QStyledItemDelegate. These two classes are independent alternatives to painting and providing editors for items in views. The difference between them is that QStyledItemDelegate uses the current style to paint its items. We therefore recommend using QStyledItemDelegate as the base class when implementing custom delegates or when working with Qt style sheets. The code required for either class should be equal unless the custom delegate needs to use the style for drawing. If you wish to customize the painting of item views, you should implement a custom style. Please see the QStyle class documentation for details.

Qt没有部分绘制圆角,部分绘制直角矩形的接口,所以实现上述效果有两个思路:

  1. 从点线开始,完全重绘一个满足我们要求的矩形;
  2. 将圆角矩形和直角矩形部分重叠,从而实现一半是圆角,一半是直角的矩形。

采用第二种办法,代码如下,需要注意的是填充效果需要是Qt::WindingFill

QPainterPath path;
path.setFillRule(Qt::WindingFill); /* 填充效果 */
path.addRoundedRect(rect, 8, 8);
path.addRect(QRect(rect.x() + width / 2, rect.y(), width / 2, rect.height()));
painter->fillPath(path, brush);

如果表格中的文字太长的话,人性化的效果是被遮挡的部分用省略号表示出来,代码如下所示。继承自QStyledItemDelegate后,我们就不用关心这行文字的效果是高亮选中的,还是普通效果,直接丢给接口即可。

void MyDelegate::paintForegroundText(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QFontMetrics fontMetrics(painter->font());
    QString msg = fontMetrics.elidedText(index.data().toString(), Qt::ElideRight, option.rect.width());
    QApplication::style()->drawItemText(painter, option.rect, option.displayAlignment, QApplication::palette(), true, msg);
}

推荐使用表格背景交替的显示效果,并且未选中高亮的背景也是圆角矩形显示。提取出两个接口

    void paintBackgroundBase(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
    void paintBackgroundAlternateBase(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;

你可能感兴趣的:(Qt,Widget)