在不缩小其他元素高度的情况下,将框架动画设置为从应用程序底部弹出 [英] Animating a frame to pop out of the bottom of the app without shrinking the height of other elements

查看:26
本文介绍了在不缩小其他元素高度的情况下,将框架动画设置为从应用程序底部弹出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用PyQt和Qt Creator为Windows开发桌面应用程序。

我想要的

我希望仅在用户输入时才向用户显示消息。我也想让信息吸引眼球,所以我选择了以下动画解决方案:

在不需要时隐藏的框架(高度=0,宽度=应用的宽度),在需要时从应用的底部"增长",保持可见5-6秒,然后缩回底部。

应用程序看起来有点像这样,没有消息:

显示消息时类似于此(请注意消息如何"覆盖"底部灰色元素):

我尝试的内容

因此,我这样做的方法是创建我称为&footer Frame";的内容,其中包含另一个我称为&message Frame";的框架。消息帧包含将及时为用户保存消息的标签。所有内容都有预先确定的高度,因此为了隐藏整个内容,我将消息框架的最大高度设置为0。

因此,对于"增长"动画,我为消息帧的maxumHeight属性设置了动画。

当前问题

事情是这样的-因为我想让应用程序响应,所以我把所有东西都放在布局里了……正因为如此,每当显示消息时,组件的睡觉高度都会被"压缩"。 有点像这样(注意消息中没有覆盖底部的灰色元素,但所有元素的高度都略有缩小):

相反,我希望消息"覆盖"位于消息坐标下的任何内容。

我尝试为消息框的几何体设置动画,但实际上什么也没有发生-可能是因为最小高度仍然是0。因此,我试图在动画开始前更改最小高度;但这再次导致了压缩。 尝试对页脚框架执行相同的操作,结果相同。

我的问题是:使用Qt实现预期效果的最佳/首选方式是什么?

推荐答案

布局管理器始终尝试显示其管理的所有小部件。如果希望某个小部件与其他小部件重叠,则不能将其放在布局内,只需创建具有父级的小部件,该父级可能就是包含位于顶层窗口上方布局的小部件。

在Designer/Creator中不能做到这一点,因为假设一旦为父小部件设置了布局,所有子小部件都将由该布局管理。唯一的解决方案是以编程方式完成此操作。

在下面的示例中,我假设使用的是QMainWindow,因此引用父小部件实际上是中心小部件,而不是QMainWindow:这是因为警报不应该覆盖主窗口布局中的其他小部件,如状态栏、底部放置的工具栏或停靠)。

动画实际上是一个QSequentialAnimationGroup,它显示矩形,等待几秒钟,然后再次隐藏它。由于可以在动画运行时调整窗口大小,因此使用帮助器函数来正确更新警告的起始值和结束值,并最终在处于暂停状态(实际上为QPauseAnimation)时更新几何体。为此,中央小工具上安装了事件过滤。

from random import randrange
from PyQt5 import QtCore, QtWidgets, uic

class MyWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        uic.loadUi('overlay.ui', self)

        self.alerts = []

        self.centralWidget().installEventFilter(self)

        self.pushButton.clicked.connect(self.showAlert)
        QtCore.QTimer.singleShot(2000, self.showAlert)

    def showAlert(self, message=None, timeout=250):
        # create an alert that is a child of the central widget
        alert = QtWidgets.QLabel(message or 'Some message to the user', 
            self.centralWidget(), wordWrap=True, 
            alignment=QtCore.Qt.AlignCenter, 
            styleSheet='background: rgb({}, {}, {});'.format(
                randrange(192, 255), randrange(192, 255), randrange(192, 255)))
        self.alerts.append(alert)
        alert.animation = QtCore.QSequentialAnimationGroup(alert)
        alert.animation.addAnimation(QtCore.QPropertyAnimation(
            alert, b'geometry', duration=timeout))
        alert.animation.addAnimation(QtCore.QPauseAnimation(3000))
        alert.animation.addAnimation(QtCore.QPropertyAnimation(
            alert, b'geometry', duration=timeout))

        # delete the alert when the animation finishes
        def deleteLater():
            self.alerts.remove(alert)
            alert.deleteLater()
        alert.animation.finished.connect(deleteLater)

        # update all animations, including the new one; this is not very
        # performant, as it also updates all existing alerts; it is 
        # just done for simplicity;
        self.updateAnimations()
        # set the start geometry of the alert, show it, and start 
        # the new animation
        alert.setGeometry(alert.animation.animationAt(0).startValue())
        alert.show()
        alert.animation.start()

    def updateAnimations(self):
        width = self.centralWidget().width() - 20
        y = self.centralWidget().height()
        margin = self.fontMetrics().height() * 2
        for alert in self.alerts:
            height = alert.heightForWidth(width) + margin
            startRect = QtCore.QRect(10, y, width, height)
            endRect = startRect.translated(0, -height)
            alert.animation.animationAt(0).setStartValue(startRect)
            alert.animation.animationAt(0).setEndValue(endRect)
            alert.animation.animationAt(2).setStartValue(endRect)
            alert.animation.animationAt(2).setEndValue(startRect)

    def eventFilter(self, obj, event):
        if obj == self.centralWidget() and event.type() == event.Resize and self.alerts:
            self.updateAnimations()
            for alert in self.alerts:
                ani = alert.animation
                # if the animation is "paused", update the geometry
                if isinstance(ani.currentAnimation(), QtCore.QPauseAnimation):
                    alert.setGeometry(ani.animationAt(0).endValue())
        return super().eventFilter(obj, event)


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = MyWindow()
    w.show()
    sys.exit(app.exec())

这篇关于在不缩小其他元素高度的情况下,将框架动画设置为从应用程序底部弹出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆