Аналогично Как создать виджет расширяемого / складываемого раздела в Qt Мне нужны расширяемые разделы в моем приложении Qt. Поэтому я перевел пример на PySide2 (Qt5).
Все выглядит хорошо, пока я не вложил расширители:
Насколько мне известно, кажется, что MaximumHeight рассчитывается только один раз (когда вызывается setContentLayout).
Полагаю, мне нужно будет обновлять максимальный вес родителей при расширении с помощью внутренней анимации, но я не знаю, с чего начать.
Пример вложенного воспроизведения
from PySide2 import QtCore, QtGui, QtWidgets
class Expander(QtWidgets.QWidget):
def __init__(self, parent=None, title='', animationDuration=300):
"""
References:
# Adapted from PyQt4 version
https://stackoverflow.com/a/37927256/386398
# Adapted from c++ version
https://stackoverflow.com/a/37119983/386398
"""
super(Expander, self).__init__(parent=parent)
self.animationDuration = animationDuration
self.toggleAnimation = QtCore.QParallelAnimationGroup()
self.contentArea = QtWidgets.QScrollArea()
self.headerLine = QtWidgets.QFrame()
self.toggleButton = QtWidgets.QToolButton()
self.mainLayout = QtWidgets.QGridLayout()
toggleButton = self.toggleButton
toggleButton.setStyleSheet("QToolButton { border: none; }")
toggleButton.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
toggleButton.setArrowType(QtCore.Qt.RightArrow)
toggleButton.setText(str(title))
toggleButton.setCheckable(True)
toggleButton.setChecked(False)
headerLine = self.headerLine
headerLine.setFrameShape(QtWidgets.QFrame.HLine)
headerLine.setFrameShadow(QtWidgets.QFrame.Sunken)
headerLine.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Maximum)
self.contentArea.setStyleSheet("QScrollArea { background-color: white; border: none; }")
self.contentArea.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
# start out collapsed
self.contentArea.setMaximumHeight(0)
self.contentArea.setMinimumHeight(0)
# let the entire widget grow and shrink with its content
toggleAnimation = self.toggleAnimation
toggleAnimation.addAnimation(QtCore.QPropertyAnimation(self, b"minimumHeight"))
toggleAnimation.addAnimation(QtCore.QPropertyAnimation(self, b"maximumHeight"))
toggleAnimation.addAnimation(QtCore.QPropertyAnimation(self.contentArea, b"maximumHeight"))
# don't waste space
mainLayout = self.mainLayout
mainLayout.setVerticalSpacing(0)
mainLayout.setContentsMargins(0, 0, 0, 0)
row = 0
mainLayout.addWidget(self.toggleButton, row, 0, 1, 1, QtCore.Qt.AlignLeft)
mainLayout.addWidget(self.headerLine, row, 2, 1, 1)
row += 1
mainLayout.addWidget(self.contentArea, row, 0, 1, 3)
self.setLayout(self.mainLayout)
def start_animation(checked):
arrow_type = QtCore.Qt.DownArrow if checked else QtCore.Qt.RightArrow
direction = QtCore.QAbstractAnimation.Forward if checked else QtCore.QAbstractAnimation.Backward
toggleButton.setArrowType(arrow_type)
self.toggleAnimation.setDirection(direction)
self.toggleAnimation.start()
self.toggleButton.clicked.connect(start_animation)
def setContentLayout(self, contentLayout):
# Not sure if this is equivalent to self.contentArea.destroy()
self.contentArea.destroy()
self.contentArea.setLayout(contentLayout)
collapsedHeight = self.sizeHint().height() - self.contentArea.maximumHeight()
contentHeight = contentLayout.sizeHint().height()
for i in range(self.toggleAnimation.animationCount()-1):
expandAnimation = self.toggleAnimation.animationAt(i)
expandAnimation.setDuration(self.animationDuration)
expandAnimation.setStartValue(collapsedHeight)
expandAnimation.setEndValue(collapsedHeight + contentHeight)
contentAnimation = self.toggleAnimation.animationAt(self.toggleAnimation.animationCount() - 1)
contentAnimation.setDuration(self.animationDuration)
contentAnimation.setStartValue(0)
contentAnimation.setEndValue(contentHeight)
from PySide2.QtWidgets import *
app = QApplication()
window = QWidget()
layout = QVBoxLayout()
window.setLayout(layout)
layout.addWidget(QLabel('above'))
inner = QVBoxLayout()
inner.addWidget(QLabel('innertop'))
inex = Expander(title='inner')
innest = QVBoxLayout()
innest.addWidget(QLabel('content'))
inex.setContentLayout(innest)
inner.addWidget(inex)
inner.addWidget(QLabel('innerbottom'))
ex = Expander(title='outer')
ex.setContentLayout(inner)
layout.addWidget(ex)
layout.addWidget(QLabel('below'))
window.show()
app.exec_()