类似于How to make an expandable/collapsable section widget in Qt,我想在我的Qt应用程序中扩展部分。因此,我已将示例转换为PySide2(Qt5)。 在嵌套扩展器之前,一切看起来都很好:
据我目前的理解,maximumHeight似乎只计算一次(调用setContentLayout时)。
我想通过内部动画扩展时需要更新父对象的maximumHeight,但是我不知道从哪里开始。
嵌套复制示例
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_()
答案 0 :(得分:0)
我最终使用了容器resizeEvent来更新最小/最大约束。
这是工作示例
from PySide2 import QtCore, QtGui, QtWidgets
class ScrollArea(QtWidgets.QScrollArea):
resized = QtCore.Signal()
def resizeEvent(self, e):
self.resized.emit()
return super(ScrollArea, self).resizeEvent(e)
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 = ScrollArea()
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)
self.contentArea.resized.connect(self.updateContentLayout)
# 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"))
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 updateContentLayout(self):
if self.toggleButton.isChecked() and self.toggleAnimation.state() != self.toggleAnimation.Running:
collapsedHeight = self.sizeHint().height() - self.contentArea.maximumHeight()
contentHeight = self.contentArea.layout().sizeHint().height()
self.setMinimumHeight(collapsedHeight + contentHeight)
self.setMaximumHeight(collapsedHeight + contentHeight)
self.contentArea.setMaximumHeight(contentHeight)
self.updateGeometry()
p = self.parent()
if isinstance(p, ScrollArea):
p.resized.emit()
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 = QFormLayout()
window.setLayout(layout)
layout.addRow(QLabel('above'))
inner = QFormLayout()
inner.addRow(QLabel('innertop'))
inex = Expander(title='inner')
innest = QFormLayout()
innest.addRow(QLabel('content'))
inex.setContentLayout(innest)
inner.addRow(inex)
inner.addRow(QLabel('innerbottom'))
ex = Expander(title='outer')
ex.setContentLayout(inner)
layout.addRow(ex)
layout.addRow(QLabel('below'))
window.show()
app.exec_()