在不缩小其他元素高度的情况下,将框架动画设置为从应用程序底部弹出 [英] Animating a frame to pop out of the bottom of the app without shrinking the height of other elements
问题描述
我正在使用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屋!