如果小部件使用样式表,是否可以在小部件painEvent
中使用剪裁?
我提出这个问题的背景和原因是,我想让小部件在出现和消失时都具有动画效果。(类似于调整大小的圆形或正方形,从中心开始变大)。
关于如何解决这个问题,我的第一个(也是唯一一个)想法是使用QPainter
的剪裁,这样只绘制所需的区域。
如果我使小部件的背景透明,并使用QPainter
中的基本绘制函数,它就可以正常工作。但是,如果小部件应用了样式表,我该如何解决这个问题呢?这可能吗?
使用的Qt版本是Qt 4.8.6
我的问题是:
- 用上面提到的策略有可能实现我想要的吗
- 有没有可能把所有的孩子都剪掉
- 我的策略合适吗?还是这样解决是个坏主意
- 是否有其他想法、最佳实践、Qt课程。。。能给我想要的吗
附加信息
我没有太多的代码要显示,因为我一直在使用这个剪辑。但这里有一些东西可以了解我所尝试的:
这是有效的。
/* Shows a small red circle inside the widget as expected */
void MyAnimatingWidget::paintEvent(QPaintEvent *ev) {
QPainter painter(this);
QRect rect = this->geometry()
QStyleOption opt;
painter.setClipRegion(QRegion(rect.width()/2,
rect.height()/2,
150, 150,
QRegion::Ellipse));
painter.setPen(QColor(255, 0, 0));
painter.setBrush(QColor(255, 0, 0));
painter.setOpacity(1);
painter.drawRect(rect);
}
但以下内容并没有改变任何事情:
/* This shows the widget as usual */
void MyAnimatingWidget::paintEvent(QPaintEvent *ev) {
QPainter painter(this);
QRect rect = this->geometry();
QStyleOption opt;
painter.setClipRegion(QRegion(rect.width()/2,
rect.height()/2,
150, 150,
QRegion::Ellipse));
painter.setRenderHint(QPainter::Antialiasing);
painter.setOpacity(1);
opt.init(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
}
此外,我注意到,样式表也是绘制的,即使我删除了style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
行。
应用于窗口小部件的样式表覆盖窗口小部件默认配备的操作系统特定样式。如果你想有一个Windows外观,但仍然想使用样式表,这甚至会导致问题。无论如何,您可以在Qt源目录src/gui/styles
中检查每个样式的作用。对于style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
,代码为:
case PE_Widget:
if (w && !rule.hasDrawable()) {
QWidget *container = containerWidget(w);
if (styleSheetCaches->autoFillDisabledWidgets.contains(container)
&& (container == w || !renderRule(container, opt).hasBackground())) {
//we do not have a background, but we disabled the autofillbackground anyway. so fill the background now.
// (this may happen if we have rules like :focus)
p->fillRect(opt->rect, opt->palette.brush(w->backgroundRole()));
}
break;
}
正如您所看到的,剪辑在任何方面都不会受到干扰,所以您设置剪辑区域的想法应该可行。现在来谈谈绘画的奥秘。背景的绘制发生在void QWidgetPrivate::paintBackground(QPainter *painter, const QRegion &rgn, int flags) const
中,它是从void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QPoint &offset, int flags, QPainter *sharedPainter, QWidgetBackingStore *backingStore)
调用的。您可以在以下位置找到代码:/src/gui/kernel/qwidget.cpp
。相关代码显示:
if (q->testAttribute(Qt::WA_StyledBackground)) {
painter->setClipRegion(rgn);
QStyleOption opt;
opt.initFrom(q);
q->style()->drawPrimitive(QStyle::PE_Widget, &opt, painter, q);
}
也许关闭属性会有所帮助?你应该从我的回答中得到的基本教训是要习惯源潜水。Qt背后的想法很好(实例化控件,而不关心实现细节),但它在实践中很少起作用,即您经常需要源代码挖掘。
要将小部件的子对象剪辑到任意剪辑区域,您可以将它们捕获到像素图中,例如:
QPixmap pixmap(widget->size());
widget->render(&pixmap);
然后手动绘制像素图。您还可以防止它们自动重新绘制(通过setUpdatesEnabled()
或隐藏它们),然后在paintEvent
处理程序中手动调用它们的render
。