Qt绘制圆角矩形的内发光或外发光效果

发布时间 2023-11-04 19:40:49作者: 兜尼完

Qt没有内置的发光效果,只有一个QGraphicsDropShadowEffect类可以对整个控件产生阴影(可近似为外发光)效果。此处作者整理了如何用QPainter手工绘制形状的内发光或外发光效果。本文主要涉及到QPainter类中的图像混合模式技巧。下面允许我把Qt帮助中的内容复制过来供参考。

调用QPainter::setCompositionMode(...)方法可以对两个图像应用不同的混合方法。Qt默认的是Blend混合。混合过程中完全透明的像素作为“无”处理。请特别注意,CompositionMode是针对两个图像的混合,因此它的Destination和Source都应该是图像,尽量不要是QWidget。否则可能出现一些意外的效果。根据作者的理解应用混合模式的典型代码如下:

void main()
{
    QPixmap background(w, h); /* Destination图片 */
    background.fill(Qt::transparent);
    QPainter pq(&background);
    ...

    QPixmap pixmap(w, h); /* Source图片 */
    pixmap.fill(Qt::transparent);
    QPainter pp(&pixmap);
    ...

    pq.setCompositionMode(QPainter::CompositionMode_DestinationOver); /* 设置混合模式 */
    pq.drawPixmap(0, 0, pixmap);
}

一、内发光效果

这里实现的是一个控件里绘制一个圆角矩形,然后对圆角矩形绘制内发光的效果。整体上是绿色背景,粉色的光影。测试环境是VS2017和Qt5.9。

头文件:

class MLighting : public QWidget
{
    Q_OBJECT

public:
    MLighting(QWidget* parent = 0);

private:
    void paintEvent(QPaintEvent *event) override;
};

CPP文件:

MLighting::MLighting(QWidget* parent) : 
    QWidget(parent)
{
}

void MLighting::paintEvent(QPaintEvent *event)
{
    int w = width();
    int h = height();

    QPixmap background(w, h);
    background.fill(Qt::transparent);
    QPainter pq(&background);
    pq.setRenderHint(QPainter::Antialiasing);
    pq.setPen(Qt::NoPen);
    pq.setBrush(Qt::darkGreen);
    pq.drawRoundedRect(4, 4, w - 8, h - 8, 8, 8);

    QPixmap pixmap(w, h);
    pixmap.fill(Qt::transparent);
    QPainter pp(&pixmap);
    pp.setRenderHint(QPainter::Antialiasing);
    pp.setBrush(Qt::NoBrush);
    for (int p = 8; p >= 1; p -= 2)
    {
        pp.setPen(QPen(QColor(243, 163, 253, 64), p));
        pp.drawRoundedRect(4, 4, w - 8, h - 8, 8, 8);
    }

    pq.setCompositionMode(QPainter::CompositionMode_SourceAtop);
    pq.drawPixmap(0, 0, pixmap);

    QPainter painter(this);
    painter.drawPixmap(0, 0, background);
}

这里采用的混合模式是CompositionMode_SourceAtop,此模式将Source图像放在最上方并且超出Destination图像的范围外的部分会被裁剪掉。

二、外发光效果

整个代码与内发光差不多。效果图如下:

头文件:

class MLighting : public QWidget
{
    Q_OBJECT

public:
    MLighting(QWidget* parent = 0);

private:
    void paintEvent(QPaintEvent *event) override;
};

CPP文件:

MLighting::MLighting(QWidget* parent) : 
    QWidget(parent)
{
}

void MLighting::paintEvent(QPaintEvent *event)
{
    int w = width();
    int h = height();

    QPixmap background(w, h);
    background.fill(Qt::transparent);
    QPainter pq(&background);
    pq.setRenderHint(QPainter::Antialiasing);
    pq.setPen(Qt::NoPen);
    pq.setBrush(Qt::darkGreen);
    pq.drawRoundedRect(4, 4, w - 8, h - 8, 8, 8);

    QPixmap pixmap(w, h);
    pixmap.fill(Qt::transparent);
    QPainter pp(&pixmap);
    pp.setRenderHint(QPainter::Antialiasing);
    pp.setBrush(Qt::NoBrush);
    for (int p = 8; p >= 1; p -= 2)
    {
        pp.setPen(QPen(QColor(243, 163, 253, 64), p));
        pp.drawRoundedRect(4, 4, w - 8, h - 8, 8, 8);
    }

    pq.setCompositionMode(QPainter::CompositionMode_DestinationOver);
    pq.drawPixmap(0, 0, pixmap);

    QPainter painter(this);
    painter.drawPixmap(0, 0, background);
}

这里采用的是CompositionMode_DestinationOver模式。此模式会将Destination图像放在上方,而Source图像放在下面混合。