PyQt 中 QGridLayout 中的动画几何
Animate Geometry inside QGridLayout in PyQt
如何为 QGridLayout 中的几何变化设置动画。我有 QLabel,它将放置在 QGridlayout 中。当鼠标在 QLabel 内部时它应该展开,在外部时收缩回正常状态。我设法制作了动画,但它并没有从所有四个方面展开。它反而远离网格。
MRE:
import sys
from PyQt5 import QtWidgets, QtCore
class Tile(QtWidgets.QLabel):
def __init__(self, *args, **kwargs):
super(Tile, self).__init__(*args, **kwargs)
# p = self.palette()
# p.setColor(self.backgroundRole(), QtCore.Qt.red)
# self.setPalette(p)
self.setText("hello")
self.setMinimumSize(100, 100)
self.setMaximumSize(125, 125)
def enterEvent(self, a0: QtCore.QEvent) -> None:
super(Tile, self).enterEvent(a0)
self.animation = QtCore.QPropertyAnimation(self, b"geometry")
self.animation.setStartValue(QtCore.QRect(self.geometry()))
self.animation.setEndValue(QtCore.QRect(self.geometry().adjusted(-25, -25, 25, 25)))
self.animation.setDuration(150)
self.animation.start(QtCore.QPropertyAnimation.DeleteWhenStopped)
def leaveEvent(self, a0: QtCore.QEvent) -> None:
super(Tile, self).leaveEvent(a0)
self.animation = QtCore.QPropertyAnimation(self, b"geometry")
self.animation.setStartValue(QtCore.QRect(self.geometry()))
self.animation.setEndValue(QtCore.QRect(self.geometry().adjusted(25, 25, -25, -25)))
self.animation.setDuration(150)
self.animation.start(QtCore.QPropertyAnimation.DeleteWhenStopped)
class ScrollView(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super(ScrollView, self).__init__(*args, **kwargs)
self.setStyleSheet('border: 1px solid black')
self.setLayout(QtWidgets.QVBoxLayout())
widget = QtWidgets.QWidget()
self.grid_layout = QtWidgets.QGridLayout(widget)
self.scrollArea = QtWidgets.QScrollArea()
self.scrollArea.setWidget(widget)
self.scrollArea.setWidgetResizable(True)
self.grid_layout.setSpacing(50)
self.row_width = 4
self._row = 0
self._column = 0
self.layout().addWidget(self.scrollArea)
def addTile(self):
self.grid_layout.addWidget(Tile(), self._row, self._column)
if self._column == 3:
self._row += 1
self._column = 0
else:
self._column += 1
def main():
app = QtWidgets.QApplication(sys.argv)
win = ScrollView()
for x in range(30):
win.addTile()
win.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()
主要问题是您设置的最大尺寸比最终值小:如果您有一个尺寸为 100x100 的起始矩形并将其扩展 25 像素在每一侧,它将变为 150x150,而不是 125x125。由于您已经设置了最大值,一旦几何体达到 125x125,它只会更改坐标,同时保持最大尺寸。
但是还有另外三个问题。
- 您总是使用 current 几何作为起始值,这可能会成为一个问题:如果您进入或离开 while其他动画正在进行中,您得到了错误的目标几何体参考;
- 如果你 enter/leave 小部件非常快,你最终会同时发生两个动画;每次都连续创建动画实际上没有任何好处;
- 小部件未考虑调整滚动区域的大小,这会改变纵横比并为新定位带来问题;
为了避免这一切,你必须只使用一个动画,根据enter/leave事件改变它的方向,适当修改start/end 值根据实际几何形状变化,并在发生 external 调整大小时适当调整为“默认”大小;最后两点仅完成 if 动画 not 活动(因为 moveEvent 和 resizeEvent 由几何变化调用)。
class Tile(QtWidgets.QLabel):
def __init__(self, *args, **kwargs):
super(Tile, self).__init__(*args, **kwargs)
self.setText("hello")
self.setMinimumSize(100, 100)
self.setMaximumSize(150, 150)
self.animation = QtCore.QPropertyAnimation(self, b"geometry")
self.animation.setDuration(150)
def animate(self, expand):
if expand:
self.animation.setDirection(self.animation.Forward)
else:
self.animation.setDirection(self.animation.Backward)
self.animation.start()
def enterEvent(self, a0: QtCore.QEvent) -> None:
super(Tile, self).enterEvent(a0)
self.animate(True)
def leaveEvent(self, a0: QtCore.QEvent) -> None:
super(Tile, self).leaveEvent(a0)
self.animate(False)
def updateAnimation(self):
if not self.animation.state():
center = self.geometry().center()
start = QtCore.QRect(QtCore.QPoint(), self.minimumSize())
start.moveCenter(center)
self.animation.setStartValue(start)
end = QtCore.QRect(QtCore.QPoint(), self.maximumSize())
end.moveCenter(center)
self.animation.setEndValue(end)
def moveEvent(self, event):
self.updateAnimation()
def resizeEvent(self, event):
self.updateAnimation()
if not self.animation.state():
rect = QtCore.QRect(QtCore.QPoint(),
self.maximumSize() if self.underMouse() else self.minimumSize())
rect.moveCenter(self.geometry().center())
self.setGeometry(rect)
如何为 QGridLayout 中的几何变化设置动画。我有 QLabel,它将放置在 QGridlayout 中。当鼠标在 QLabel 内部时它应该展开,在外部时收缩回正常状态。我设法制作了动画,但它并没有从所有四个方面展开。它反而远离网格。
MRE:
import sys
from PyQt5 import QtWidgets, QtCore
class Tile(QtWidgets.QLabel):
def __init__(self, *args, **kwargs):
super(Tile, self).__init__(*args, **kwargs)
# p = self.palette()
# p.setColor(self.backgroundRole(), QtCore.Qt.red)
# self.setPalette(p)
self.setText("hello")
self.setMinimumSize(100, 100)
self.setMaximumSize(125, 125)
def enterEvent(self, a0: QtCore.QEvent) -> None:
super(Tile, self).enterEvent(a0)
self.animation = QtCore.QPropertyAnimation(self, b"geometry")
self.animation.setStartValue(QtCore.QRect(self.geometry()))
self.animation.setEndValue(QtCore.QRect(self.geometry().adjusted(-25, -25, 25, 25)))
self.animation.setDuration(150)
self.animation.start(QtCore.QPropertyAnimation.DeleteWhenStopped)
def leaveEvent(self, a0: QtCore.QEvent) -> None:
super(Tile, self).leaveEvent(a0)
self.animation = QtCore.QPropertyAnimation(self, b"geometry")
self.animation.setStartValue(QtCore.QRect(self.geometry()))
self.animation.setEndValue(QtCore.QRect(self.geometry().adjusted(25, 25, -25, -25)))
self.animation.setDuration(150)
self.animation.start(QtCore.QPropertyAnimation.DeleteWhenStopped)
class ScrollView(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super(ScrollView, self).__init__(*args, **kwargs)
self.setStyleSheet('border: 1px solid black')
self.setLayout(QtWidgets.QVBoxLayout())
widget = QtWidgets.QWidget()
self.grid_layout = QtWidgets.QGridLayout(widget)
self.scrollArea = QtWidgets.QScrollArea()
self.scrollArea.setWidget(widget)
self.scrollArea.setWidgetResizable(True)
self.grid_layout.setSpacing(50)
self.row_width = 4
self._row = 0
self._column = 0
self.layout().addWidget(self.scrollArea)
def addTile(self):
self.grid_layout.addWidget(Tile(), self._row, self._column)
if self._column == 3:
self._row += 1
self._column = 0
else:
self._column += 1
def main():
app = QtWidgets.QApplication(sys.argv)
win = ScrollView()
for x in range(30):
win.addTile()
win.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()
主要问题是您设置的最大尺寸比最终值小:如果您有一个尺寸为 100x100 的起始矩形并将其扩展 25 像素在每一侧,它将变为 150x150,而不是 125x125。由于您已经设置了最大值,一旦几何体达到 125x125,它只会更改坐标,同时保持最大尺寸。
但是还有另外三个问题。
- 您总是使用 current 几何作为起始值,这可能会成为一个问题:如果您进入或离开 while其他动画正在进行中,您得到了错误的目标几何体参考;
- 如果你 enter/leave 小部件非常快,你最终会同时发生两个动画;每次都连续创建动画实际上没有任何好处;
- 小部件未考虑调整滚动区域的大小,这会改变纵横比并为新定位带来问题;
为了避免这一切,你必须只使用一个动画,根据enter/leave事件改变它的方向,适当修改start/end 值根据实际几何形状变化,并在发生 external 调整大小时适当调整为“默认”大小;最后两点仅完成 if 动画 not 活动(因为 moveEvent 和 resizeEvent 由几何变化调用)。
class Tile(QtWidgets.QLabel):
def __init__(self, *args, **kwargs):
super(Tile, self).__init__(*args, **kwargs)
self.setText("hello")
self.setMinimumSize(100, 100)
self.setMaximumSize(150, 150)
self.animation = QtCore.QPropertyAnimation(self, b"geometry")
self.animation.setDuration(150)
def animate(self, expand):
if expand:
self.animation.setDirection(self.animation.Forward)
else:
self.animation.setDirection(self.animation.Backward)
self.animation.start()
def enterEvent(self, a0: QtCore.QEvent) -> None:
super(Tile, self).enterEvent(a0)
self.animate(True)
def leaveEvent(self, a0: QtCore.QEvent) -> None:
super(Tile, self).leaveEvent(a0)
self.animate(False)
def updateAnimation(self):
if not self.animation.state():
center = self.geometry().center()
start = QtCore.QRect(QtCore.QPoint(), self.minimumSize())
start.moveCenter(center)
self.animation.setStartValue(start)
end = QtCore.QRect(QtCore.QPoint(), self.maximumSize())
end.moveCenter(center)
self.animation.setEndValue(end)
def moveEvent(self, event):
self.updateAnimation()
def resizeEvent(self, event):
self.updateAnimation()
if not self.animation.state():
rect = QtCore.QRect(QtCore.QPoint(),
self.maximumSize() if self.underMouse() else self.minimumSize())
rect.moveCenter(self.geometry().center())
self.setGeometry(rect)