c++ - 为什么当 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
}
};
如何修复崩溃?
解决方案
原因
您首先创建一个画家,将 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);
}
该示例产生以下结果:
如您所见,文本与您的自定义绘图一起显示。
推荐阅读
- javascript - Vue.js:阻止后续请求直到前一个请求完成
- r - r 从 txt 文件中读取带引号的行
- python - Use finditem() only on one Column
- unit-testing - 使用 Codeception 和 Laravel 5 模块进行单元测试的模拟类
- optimization - 如何知道需要预加载哪些资源(Image /js/css)
- rest - 使用 java REST API 将弹性搜索聚合转换为代码会出错
- c++ - 将 Python 转换为 Rcpp
- powerquery - 使用 excel power query 公开 SQL Server 数据,限制对用户数据的访问
- java - 如何在 pact jvm 中迭代地创建 Pact 文件
- html - 在反应中渲染大字符串