【问题标题】:Why my QToolButton which I try to make collapsible doesn't collapse properly?为什么我尝试使其可折叠的 QToolButton 不能正确折叠?
【发布时间】:2019-12-02 12:49:29
【问题描述】:

伙计们。

请求您帮助解决我的测试脚本问题。 我正在练习制作带有小部件的可折叠按钮。

脚本主要取自stackoverflow中关于可折叠按钮的another question

所以我想把QTabWidget 我的class CollpsibleBox(QWidget) 放在下面。问题是我的 CollapsibleBox 表现得很奇怪 - 按钮在跳跃,有时它不能正确打开/关闭。

我想知道将我的小部件正确放置在QTabWidget 下是不是有些错误,还是动画有问题?

import random
from PySide2.QtGui import QPixmap, QBrush, QColor, QIcon, QPainterPath, QPolygonF, QPen, QTransform
from PySide2.QtCore import QSize, Qt, Signal, QPointF, QRect, QPoint, QParallelAnimationGroup, QPropertyAnimation, QAbstractAnimation
from PySide2.QtWidgets import QMainWindow, QDialog, QVBoxLayout, QHBoxLayout, QGraphicsView, QGraphicsScene, QFrame, \
    QSizePolicy, QGraphicsPixmapItem, QApplication, QRubberBand, QMenu, QMenuBar, QTabWidget, QWidget, QPushButton, \
    QSlider, QGraphicsPolygonItem, QToolButton, QScrollArea, QLabel

extraDict = {'buttonSetA': ['test'], 'buttonSetB': ['test']}
tabList = ['Main', 'Extra']
_ui = dict()

class MainWindow(QDialog):
    def __init__(self, parent=None):
        QDialog.__init__(self, parent=parent)
        self.create()

    def create(self, **kwargs):
        _ui['mainLayout'] = QVBoxLayout()
        _ui['tabWidget'] = QTabWidget()
        _ui['mainLayout'].addWidget(_ui['tabWidget'])
        for tab in tabList:
            _ui['tab' + tab] = QWidget()
            _ui['tabWidget'].addTab(_ui['tab' + tab], tab)

        _ui['tabExtra'].layout = QVBoxLayout()
        _ui['tabExtra'].setLayout(_ui['tabExtra'].layout)

        _ui['content'] = QWidget()
        _ui['tabExtra'].layout.addWidget(_ui['content'])

        vlay = QVBoxLayout(_ui['content'])
        for name in extraDict.keys():
            box = CollapsibleBox(name)
            vlay.addWidget(box)
            lay = QVBoxLayout()
            for j in range(8):
                label = QLabel("{}".format(j))
                color = QColor(*[random.randint(0, 255) for _ in range(3)])
                label.setStyleSheet(
                    "background-color: {}; color : white;".format(color.name())
                )
                label.setAlignment(Qt.AlignCenter)
                lay.addWidget(label)

            box.setContentLayout(lay)
        self.setLayout(_ui['mainLayout'])

class CollapsibleBox(QWidget):
    def __init__(self, name):
        super(CollapsibleBox, self).__init__()
        self.toggle_button = QToolButton(text=name, checkable=True, checked=False)
        self.toggle_button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.toggle_button.setArrowType(Qt.RightArrow)
        self.toggle_button.pressed.connect(self.on_pressed)
        self.toggle_animation = QParallelAnimationGroup(self)
        self.content_area = QScrollArea(maximumHeight=0, minimumHeight=0)
        self.content_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.content_area.setFrameShape(QFrame.NoFrame)

        lay = QVBoxLayout(self)
        lay.setSpacing(0)
        lay.setContentsMargins(0, 0, 0, 0)
        lay.addWidget(self.toggle_button)
        lay.addWidget(self.content_area)

        self.toggle_animation.addAnimation(QPropertyAnimation(self, b"minimumHeight"))
        self.toggle_animation.addAnimation(QPropertyAnimation(self, b"maximumHeight"))
        self.toggle_animation.addAnimation(QPropertyAnimation(self.content_area, b"maximumHeight"))

    def on_pressed(self):
        checked = self.toggle_button.isChecked()
        self.toggle_button.setArrowType(Qt.DownArrow if not checked else Qt.RightArrow)
        self.toggle_animation.setDirection(QAbstractAnimation.Forward
            if not checked
            else QAbstractAnimation.Backward
                                           )
        self.toggle_animation.start()

    def setContentLayout(self, layout):
        lay = self.content_area.layout()
        del lay
        self.content_area.setLayout(layout)
        collapsed_height = (self.sizeHint().height() - self.content_area.maximumHeight())
        content_height = layout.sizeHint().height()
        for i in range(self.toggle_animation.animationCount()):
            animation = self.toggle_animation.animationAt(i)
            animation.setDuration(500)
            animation.setStartValue(collapsed_height)
            animation.setEndValue(collapsed_height + content_height)
        content_animation = self.toggle_animation.animationAt(self.toggle_animation.animationCount() - 1)
        content_animation.setDuration(500)
        content_animation.setStartValue(0)
        content_animation.setEndValue(content_height)

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    window = MainWindow()
    window.setGeometry(500, 100, 500, 500)
    window.show()
    sys.exit(app.exec_())

【问题讨论】:

    标签: python pyqt


    【解决方案1】:

    问题在于您只是将两个小部件添加到完整布局中,并且布局将尝试将它们放置得尽可能好(通常在每个小部件可用区域的中心,基于其大小提示)。

    您可以为布局设置小部件的对齐方式(将按钮放在其可用空间的顶部):

            vlay = QVBoxLayout(_ui['content'])
            for name in extraDict.keys():
                box = CollapsibleBox(name)
                vlay.addWidget(box, alignment=Qt.AlignTop)
    

    或者在布局底部添加一个拉伸:

            vlay = QVBoxLayout(_ui['content'])
            for name in extraDict.keys():
                # ...
            vlay.addStretch(1)
    

    这会将所有按钮放置在布局的顶部。

    作为旁注,我建议您避免使用 ui 的字典逻辑,因为它可能会变得非常混乱并且容易出错。如果您出于某些原因(我希望,非常好)确实需要这样做,那没关系,但在提问时请避免这样做:这会使 真的难以阅读您的代码,而人们最终可能会完全忽略您的问题。

    【讨论】:

    • 感谢您的帮助,真的很有帮助。还要感谢有关字典的说明,我是编码方面的业余爱好者,但仍然不知道如何制作正确的代码。你能建议我如何避免字典吗?我正在制作字典,因为有时在我的脚本中我发现我需要在其他类中使用 mainWindow 类中的一些信息,而无需制作全局变量。
    • 不客气!您不需要为此使用字典,只需创建类实例属性(例如self.tabWidget = QTabWidget()),这正是Qt 在使用setupUiuic.loadUi 与从Designer 创建的.ui 文件时所做的。您只需要确保属性名称是唯一的(但对于字典来说是相同的)并且不要覆盖基本的 QWidget/QObject 方法。由于您是从代码创建 ui,因此您可以避免为不需要跟踪的对象创建持久实例属性(通常是小部件布局,因为您可以使用 widget.layout() 访问它们)
    • 哦...我明白了,确实如此。那么我能问关于这个话题的最后一个问题吗?那么你如何处理'for循环'呢?如果您需要制作许多相同的按钮,但具有唯一的命名,那么除了字典还有其他选择吗?
    • 通常人们会使用一个列表来对这些按钮进行分组(记住有QButtonGroup),或者最终通过setattr(例如setattr(self, 'button_{:02}'.format(b), QPushButton(str(b))))。环顾四周获取有关为类创建动态属性名称的答案。
    猜你喜欢
    • 1970-01-01
    • 2014-02-28
    • 2015-11-25
    • 1970-01-01
    • 1970-01-01
    • 2014-03-16
    • 2020-10-28
    • 1970-01-01
    • 2014-05-22
    相关资源
    最近更新 更多