为啥当 QPushButton::paintEvent 后面跟着 QPainter::fillRect 时应用程序会崩溃?

Posted

技术标签:

【中文标题】为啥当 QPushButton::paintEvent 后面跟着 QPainter::fillRect 时应用程序会崩溃?【英文标题】:Why the application crashes when QPushButton::paintEvent is followed by QPainter::fillRect?为什么当 QPushButton::paintEvent 后面跟着 QPainter::fillRect 时应用程序会崩溃? 【发布时间】:2018-10-28 03:46:21 【问题描述】:

我正在通过子类化QPushButton 并处理paintEvent 来实现一个圆形按钮。我想显示用户设置的文字,然后画一个圆圈。

应用程序在调用QPushButton::paintEvent 方法后在QPainter::fillRect 崩溃。如果不调用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
    
;

如何修复崩溃?

【问题讨论】:

【参考方案1】:

原因

你首先创建一个painter,将QPaintDevice *device传递给QPainter的构造函数,其中callsQPainter::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);
    

该示例产生以下结果:

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

【讨论】:

以上是关于为啥当 QPushButton::paintEvent 后面跟着 QPainter::fillRect 时应用程序会崩溃?的主要内容,如果未能解决你的问题,请参考以下文章

为啥当代码实际工作时 Vue 会抱怨

为啥当状态发生突变时 mapStateToProps 返回空对象?

封装——当设置器已经公开时,为啥我们需要它? [复制]

当元素存在于数组中时,为啥 index = -1? [复制]

为啥我收到这个错误,当角色完全加载时?

当我们有 OkHttp 时为啥要使用 Retrofit