2

painEvent如果小部件使用样式表,是否可以在小部件中使用剪辑?

我的问题的背景和原因是我想让小部件在它出现和消失时动画。(类似于调整大小的圆形或正方形,从中心的小区域开始变大)。

我对如何解决这个问题的第一个(也是唯一一个)想法是使用 a 的剪裁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);条线。

4

1 回答 1

1

您应用于小部件的样式表会覆盖默认配备的特定于操作系统的样式小部件。这甚至可能会导致问题,如果您想拥有 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,称为 from 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()或隐藏它们),然后手动render在您的处理程序中调用它们。paintEvent

于 2014-10-24T08:29:12.627 回答