首页 > 解决方案 > 为什么当 QPushButton::paintEvent 后面跟着 QPainter::fillRect 时应用程序会崩溃?

问题描述

我正在通过子类QPushButton化和处理paintEvent. 我想显示用户设置的文本,然后画一个圆圈。

QPainter::fillRect应用程序在调用该QPushButton::paintEvent方法后崩溃。如果QPushButton::paintEvent未调用它不会崩溃,但不会显示按钮文本。

这是我的代码:

class CRoundAnimatingBtn : public QPushButton
{
    Q_OBJECT
public:
    explicit CRoundAnimatingBtn(QWidget *parent = nullptr)  : QPushButton(parent) {}

protected:
    void resizeEvent(QResizeEvent *) { setMask(QRegion(rect(), QRegion::Ellipse)); }
    void paintEvent(QPaintEvent * e) {
        QPainter painter(this);
        QPointF center(width()/2, height()/2);
        QRadialGradient radialGradient(center, qMin(width(), height())/2, center);

        QPushButton::paintEvent(e); // Application crashes if this is called

        if (isDown()) {
            radialGradient.setColorAt(0.0,Qt::transparent);
            radialGradient.setColorAt(0.79, Qt::transparent);
            radialGradient.setColorAt(0.80, Qt::gray);
            radialGradient.setColorAt(0.95, Qt::black);
            radialGradient.setColorAt(0.90, Qt::gray);
            radialGradient.setColorAt(0.91, Qt::transparent);
        } else {
            radialGradient.setColorAt(0.0,Qt::transparent);
            radialGradient.setColorAt(0.84, Qt::transparent);
            radialGradient.setColorAt(0.85, Qt::gray);
            radialGradient.setColorAt(0.90, Qt::black);
            radialGradient.setColorAt(0.95, Qt::gray);
            radialGradient.setColorAt(0.96, Qt::transparent);
        }

        painter.fillRect(rect(), radialGradient); // Application crashes here
    }
};

如何修复崩溃?

标签: c++qtqt5

解决方案


原因

您首先创建一个画家,将 a 传递QPaintDevice *device给 的构造函数QPainter,该构造函数调用 QPainter::begin

QPainter painter(this);

然后调用基类实现 paintEvent

QPushButton::paintEvent(e);

在完成第一个绘制之前,它会在同一QStylePainter p绘制设备上创建一个新绘制器:

void QPushButton::paintEvent(QPaintEvent *)
{
    QStylePainter p(this);
    QStyleOptionButton option;
    initStyleOption(&option);
    p.drawControl(QStyle::CE_PushButton, option);
}

最后,您尝试使用第一个画家QPainter painter进行绘制:

painter.fillRect(rectangle, radialGradient);

重要提示:这种方法是不允许的,因为文档QPainter::begin清楚地表明:

警告:油漆设备一次只能由一名油漆工进行油漆。

解决方案

考虑到这一点,我建议您通过移动到(在此事件处理程序中的所有其他内容之前)QPushButton::paintEvent(e);的最开始来避免同时拥有两个活跃的画家。CRoundAnimatingBtn::paintEvent

注意:如果您将 放在QPushButton::paintEvent(e);最后CRoundAnimatingBtn::paintEvent,默认实现将覆盖您的自定义绘图并且它不可见。

例子

这是CRoundAnimatingBtn::paintEvent可能的样子:

void paintEvent(QPaintEvent * e) {
        QPushButton::paintEvent(e);

        QPainter painter(this);
        QPointF center(width()/2, height()/2);
        QRadialGradient radialGradient(center, qMin(width(), height())/2, center);

        ...

        painter.fillRect(rect(), radialGradient);
    }

该示例产生以下结果:

带有圆形突出显示按钮的窗口

如您所见,文本与您的自定义绘图一起显示。


推荐阅读