QTreeWidget:如何动态更改 sizeHint?

Posted

技术标签:

【中文标题】QTreeWidget:如何动态更改 sizeHint?【英文标题】:QTreeWidget: How to change sizeHint dynamically? 【发布时间】:2021-05-27 07:38:23 【问题描述】:

我有一个 QTreeWidget,其中 TopLevelIteps 被自定义小部件替换。所述小部件通过 QStateMachine 对其最大和最小高度进行动画处理。

QTreeWidget (GIF):

自定义小部件动画 (GIF):

问题是行在展开时不会调整其高度以适应自定义小部件:

行高固定大小(GIF):

在小部件项目之间造成重叠,而不是像这样相互推开:

我追求的结果(GIF):

我尝试在***项目上使用 setSizeHint(),但它在项目/小部件之间创建了一个很大的空白空间:

使用 setSizeHint()(GIF):

我在想也许我必须实现 sizeHint() 但我不确定该放什么。或者有没有更好的方法来解决这个问题?

我真的很感激一些提示。

示例代码:

# Custom Widget
class ExpandableFrame(QFrame):

    def __init__(self):
        QFrame.__init__(self)

        # Default Properties
        self.setFixedSize(200,50)
        self.setStyleSheet('background-color: #2e4076; border: 2px solid black; border-radius: 10px;')
     
        # Setup expand button
        self.expandToggle = QToolButton()
        self.expandToggle.setText('[]')
        self.expandToggle.setMaximumSize(10,20)
        self.expandToggle.setCursor(Qt.PointingHandCursor)

        # Setup layout
        layout = QHBoxLayout()
        layout.setAlignment(Qt.AlignRight)
        layout.addWidget(self.expandToggle)
        self.setLayout(layout)

        self.expandArea()

    # Animates minimumHeight and maximumHeight
    def expandArea(self):

        heigth = self.height()
        newHeigth = 100

        machine = QStateMachine(self)

        state1 = QState()
        state1.assignProperty(self, b'minimumHeight', heigth)
        state1.assignProperty(self, b'maximumHeight', heigth)

        state2 = QState()
        state2.assignProperty(self, b'minimumHeight', newHeigth)
        state2.assignProperty(self, b'maximumHeight', newHeigth)

        # Create animations
        expandAnim = QPropertyAnimation(self, 'minimumHeight')
        closeAnim = QPropertyAnimation(self, 'maximumHeight')
        expandAnim.setDuration(125)
        closeAnim.setDuration(125)
        expandAnim.setEasingCurve(QEasingCurve.Linear)  

        # Create event transitions
        eventTransition1 = QEventTransition(self.expandToggle, QEvent.MouseButtonPress)
        eventTransition1.setTargetState(state2)
        eventTransition1.addAnimation(expandAnim)
        state1.addTransition(eventTransition1)

        eventTransition2 = QEventTransition(self.expandToggle, QEvent.MouseButtonPress)
        eventTransition2.setTargetState(state1)
        eventTransition2.addAnimation(closeAnim)
        state2.addTransition(eventTransition2)
        
        # Add the states to the machine
        machine.addState(state1)
        machine.addState(state2)
        machine.setInitialState(state1)
        machine.start()
    
# Tree Widget
class CustomTree(QTreeWidget):

    customWidget = []

    def __init__(self, parent=None):
        QTreeWidget.__init__(self, parent)

        # Create top level items
        itemCount = 3
        for element in range(itemCount):
            topLevelItem = QTreeWidgetItem()
            self.addTopLevelItem(topLevelItem)

            # Replace the top level items with the expandable custom widget:
            # Get the Model Index to each item
            modelIndex = self.indexFromItem(topLevelItem, 0)
            
            # Create the ExpandableFrame widgets for all the top level items
            self.customWidget.append(ExpandableFrame())

            # Set the widget to each top level item based on its index
            self.setIndexWidget(modelIndex, self.customWidget[element])
            
            # Create more items and add them as children of the top level items
            for x in range(itemCount):
                child = QTreeWidgetItem()
                child.setText(0,'Child')
                topLevelItem.addChild(child)

示例如下所示:

运行的最小代码示例:

【问题讨论】:

请提供minimal, reproducible example。 当然@musicamante。我编辑了帖子并添加了一个非常简化的示例。让我知道你的想法。 我发现我可以返回实现一个python QTreeWidgetItem 类并覆盖python data() 以返回一个新的sizeHint。这打开了一些门。如何使用按钮为树中的特定项目触发此操作? 【参考方案1】:

一种解决方案可能是在每次更改任何小部件的大小时发出视图的项目委托的sizeHintChanged 信号。这告诉视图应该更新项目的位置。为此,您可以覆盖 ExpandableFrameresizeEvent 并发出自定义信号,例如

class ExpandableFrame(QFrame):
    resized = pyqtSignal(QVariant)

    def resizeEvent(self, event):
        self.resized.emit(self.height())
        super().resizeEvent(event)

CustomTree 中,您可以通过覆盖setIndexWidgetExpandableFrame.resized 连接到自定义树的委托的sizeHintChanged 信号,例如

class CustomTree(QTreeWidget):
    ...

    def setIndexWidget(self, index, widget):
        super().setIndexWidget(index, widget)
        if isinstance(widget, ExpandableFrame):
            widget.resized.connect(lambda: self.itemDelegate().sizeHintChanged.emit(index))

屏幕截图:

这种方法的缺点是,如果小部件被移动或替换,您将需要更新连接。

【讨论】:

太棒了!谢谢。不过我有一些问题: 1. 由于我的 Python 版本(我在 Maya 2019 中仍然使用 2.7),我不得不将其更改为:super(QTreeWidget, self).setIndexWidget(index, widget)。小部件出现,但是当resized 信号被触发时我收到此错误:``` TypeError: resized() 只接受 0 个参数,1 个给定!. This is how I defined the signal: resized = Signal()```。 2. 如果小部件被移动或替换,您能否举例说明如何更新连接? 在阅读了信号之后,我意识到如果信号携带数据,它必须在声明中。你使用了QVariant,但我可以use int,这是height()返回的类型,所以resized = Signal(int) 它似乎工作得很好。我仍然想知道更新部分。再次感谢@Heike 的洞察力。

以上是关于QTreeWidget:如何动态更改 sizeHint?的主要内容,如果未能解决你的问题,请参考以下文章

我在具有动态数据的 QtreeWidget 中的 QTreewidgetItem 中单击的编辑按钮

如何设置QTreeWidget标头颜色并隐藏分割线?

QtreeWidget 样式表:更改所选项目样式

如何获取 QTreeWidget 可见区域中显示的所有项目?

如何在TreeView中选中他的节点时,更改节点的背景颜色?

在 QTreeWidget 中设置编辑器宽度以填充单元格