如何使Qt小部件淡入或淡出?

28

我正在尝试淡入淡出一个QLabel或者其他任何QWidget的子类。 我已经尝试使用QGraphicsEffect,但不幸的是它只能在Windows上运行良好,在Mac上不行。

唯一可以在Mac和Windows上都可用的解决方案似乎是拥有我的自定义paintEvent,在其中设置QPainter的不透明度,并在我的派生QLabel中定义“opacity”的Q_PROPERTY,并通过QPropertyAnimation更改不透明度。

以下是相关的代码片段供您参考。 我仍然看到一个问题 - 重用QLabel :: paintEvent似乎不起作用,只有在完全自定义绘制使用QPainter时才起作用,但这似乎不是一个简单的方式,如果我需要为每个要淡出的QWidget子类执行此操作,那将是一场噩梦。请说明我是否犯了任何明显的错误。

Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity)

void MyLabel::setOpacity(qreal value) {
    m_Opacity = value;
    repaint();
}

void MyLabel::paintEvent((QPaintEvent *pe) {
    QPainter p;
    p.begin(this);
    p.setOpacity();
    QLabel::paintEvent(pe);
    p.end();
}

void MyLabel::startFadeOutAnimation() {
    QPropertyAnimation *anim = new QPropertyAnimation(this, "opacity");
    anim->setDuration(800);
    anim->setStartValue(1.0);
    anim->setEndValue(0.0);
    anim->setEasingCurve(QEasingCurve::OutQuad);
    anim->start(QAbstractAnimation::DeleteWhenStopped);
}

Qt API告诉你正在以不应使用的方式使用它。当您想要像那样动画显示小部件的内容时,小部件并不是真正的高性能工具。这可能适用于概念验证,但从长远来看,您应该使用Qt Quick。无论您使用Qt Quick 1还是Qt Quick 2都取决于您,即使Qt Quick 1(声明性模块)也比您当前的做法更好。 - Kuba hasn't forgotten Monica
4个回答

49

实际上,有一种超级简单的方法可以做到这一点,而不需要混乱的QPaintEvent拦截以及QGraphicsProxyWidget的严格要求,后者不适用于推广的小部件子类。下面的技巧甚至可以用于推广的小部件及其子部件。

给你的小部件渐显效果

// w is your widget
QGraphicsOpacityEffect *eff = new QGraphicsOpacityEffect(this);
w->setGraphicsEffect(eff);
QPropertyAnimation *a = new QPropertyAnimation(eff,"opacity");
a->setDuration(350);
a->setStartValue(0);
a->setEndValue(1);
a->setEasingCurve(QEasingCurve::InBack);
a->start(QPropertyAnimation::DeleteWhenStopped);

淡出你的小部件

// w is your widget
QGraphicsOpacityEffect *eff = new QGraphicsOpacityEffect(this);
w->setGraphicsEffect(eff);
QPropertyAnimation *a = new QPropertyAnimation(eff,"opacity");
a->setDuration(350);
a->setStartValue(1);
a->setEndValue(0);
a->setEasingCurve(QEasingCurve::OutBack);
a->start(QPropertyAnimation::DeleteWhenStopped);
connect(a,SIGNAL(finished()),this,SLOT(hideThisWidget()));
// now implement a slot called hideThisWidget() to do
// things like hide any background dimmer, etc.

我真的很喜欢这个答案!不过,有没有一种简单的方法来淡入/淡出多个小部件? - philm
5
重要提示:如果您在Python中运行此代码,需要保存aeff变量(self.a=),以免被垃圾回收。 - Jonathan

2
尝试将调色板的一部分作为标签属性暴露出来,然后进行动画处理:
Q_PROPERTY(QColor color READ color WRITE setColor)

void MyLabel::setColor(const QColor &value) {
    QPalette palette;
    palette.setBrush(QPalette::WindowText, value);
    setPalette(palette);
}

QColor MyLabel::color() {
    return palette(QPalette::Normal, QPalette::Window).
}

void MyLabel::startFadeOutAnimation() {
    QPropertyAnimation *animation = new QPropertyAnimation(label, "color", this);
    QColor c = label->color();
    animation->setKeyValueAt(0, c);
    c.setAlpha(0);
    animation->setKeyValueAt(1, c);
    animation->setEasingCurve(QEasingCurve::OutQuad);
    animation->setDuration(1000);
    animation->start(QAbstractAnimation::DeleteWhenStopped);
}

您可以尝试通过定义和注册新的插值器来处理QPalette,以避免子类化qRegisterAnimationInterpolator,但这有点复杂。

1
你可以将小部件放入 QGraphicsScene 中。它支持不透明度变化和动画效果。
请参阅 QGraphicsProxyWidget 文档以获取示例。

1

很抱歉我不能用C++编写它,但在Python3和C++中的实现是相同的:

您可以创建一个QVariantAnimation来动画化表示不透明度的浮点值,将valueChanged信号连接到自定义函数:

anim = QVariantAnimation()
anim.setStartValue(.3)
anim.setEndValue(.6)
anim.setDuration(900)
anim.setEasingCurve(QEasingCurve.InOutQuad) # Not strictly required, just for the aesthetics
anim.valueChanged.connect(lambda opacity: mySpecialFun(opacity))
anim.start()

然后,定义自定义函数来更改不透明度(或您想要动画的属性):

def mySpecialFun(opacity: float) -> None:
    mywidget.setStyleSheet(f"background-color: rgba(0, 0, 0, {color});")

这个方法之所以有效是因为我们不是在动画化一个属性,而是在动画化一个浮点数值,关键在于每当浮点数值发生变化时,我们获取其值并将其传递给一个自定义函数来执行我们的定制作业(在我们的情况下是通过样式表设置不透明度)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接