PyQt5:创建强制固定高度的流布局

Posted

技术标签:

【中文标题】PyQt5:创建强制固定高度的流布局【英文标题】:PyQt5: Creating a flow layout that forces a fixed height 【发布时间】:2018-11-06 13:46:07 【问题描述】:

我正在尝试制作流式布局效果,用户可以在其中修改小部件的宽度,并调整大小以进行补偿。我已经研究过创建自定义布局,但布局从不增加高度来补偿缩小的宽度。

我能想到的最佳解决方案是在调整大小时手动设置高度,但在 PyQt5 中,这似乎(如果您快速尝试调整窗口大小)锁定整个窗口,因此您无法再调整大小或移动它.它可以以编程方式调整大小或移动,但窗口不再响应用户调整大小/移动请求。

似乎像一个递归错误,但我找不到任何被触发的递归事件,并且 UI 的其余部分没有挂起,只是调整大小。

这是我的用例:

import random
import sys
import math

from PyQt5.QtGui import QPalette, QColor
from PyQt5.QtWidgets import QWidget, QApplication


class ChangingHeightWidget(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._allow_height_change = False
        self._child_widgets = []
        self._create_children(8)


    def _create_children(self, count: int):
        for i in range(count):
            # Create a panel
            new_child = QWidget()
            new_child.setFixedSize(64, 64)
            new_child.setParent(self)
            new_child.show()

            # Set the color
            pal = QPalette()
            pal.setColor(QPalette.Background, QColor(random.randint(100, 255), random.randint(100, 255), random.randint(100, 255)))
            new_child.setAutoFillBackground(True)
            new_child.setPalette(pal)

            self._child_widgets.append(new_child)

        self._move_panels()

    def _move_panels(self):
        num_per_row = max(int((self.width()) / 64), 1)

        for i in range(8):
            y = int(i / num_per_row)
            x = i % num_per_row
            self._child_widgets[i].move(x * 64, y * 64)

        num_rows = math.ceil(8 / float(num_per_row))
        min_height = num_rows * 64
        self.setFixedHeight(min_height)

    def resizeEvent(self, QResizeEvent):
        self._move_panels()


if __name__ == '__main__':
    import callback_fix
    app = QApplication(sys.argv)
    gui = ChangingHeightWidget()
    gui.show()
    app.exec_()

有没有办法阻止这种锁定的发生?或者,有没有不需要在 resizeEvent 中调用 setFixedHeight 的方法来实现这个效果?

【问题讨论】:

对我有用,没有错误。 (我用的是qt 5.6.0,删除了import callback_fix这一行) 它适用于我一小会儿,但如果我调整它的大小足够多次,它最终会停止工作。几秒钟的不停调整大小通常会为我锁定它。我很确定这是因为在“resizeEvent”中执行了“setFixedHeight”,但我想不出另一种方法来达到这个效果。 我无法重现它.. callback_fix 是什么? 回调修复是为了确保错误消息出现在控制台中。我已经设法重组代码,以便在 resizeEvent 中不调用 setFixedHeight,这似乎解决了问题。似乎没有办法在调整大小期间设置高度而不会遇到窗口管理器的某种递归问题。 【参考方案1】:

试试看:

import random
import sys
import math

from PyQt5.QtGui     import QPalette, QColor
from PyQt5.QtWidgets import QWidget, QApplication, QListWidget


class ChangingHeightWidget(QListWidget):           # - QWidget  
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.setFrameShape(self.NoFrame)           # +++
        self.setFlow(self.LeftToRight)             # +++
        self.setWrapping(True)                     # +++
        self.setResizeMode(self.Adjust)            # +++


        self._allow_height_change = False
        self._child_widgets = []
        self._create_children(8)


    def _create_children(self, count: int):
        for i in range(count):
            # Create a panel
            new_child = QWidget()
            new_child.setFixedSize(64, 64)
            new_child.setParent(self)
            new_child.show()

            # Set the color
            pal = QPalette()
            pal.setColor(QPalette.Background, QColor(random.randint(100, 255), random.randint(100, 255), random.randint(100, 255)))
            new_child.setAutoFillBackground(True)
            new_child.setPalette(pal)

            self._child_widgets.append(new_child)

        self._move_panels()

    def _move_panels(self):
        num_per_row = max(int((self.width()) / 64), 1)

        for i in range(8):
            y = int(i / num_per_row)
            x = i % num_per_row
            self._child_widgets[i].move(x * 64, y * 64)

#        num_rows = math.ceil(8 / float(num_per_row))
#        min_height = num_rows * 64
#        self.setFixedHeight(min_height)

    def resizeEvent(self, QResizeEvent):
        self._move_panels()


if __name__ == '__main__':
#    import callback_fix
    app = QApplication(sys.argv)
    gui = ChangingHeightWidget()
    gui.show()
    app.exec_()

【讨论】:

我没有尝试使用列表小部件,但它似乎并没有解决问题,因为它根本没有强制小部件的高度。理想情况下,我正在寻找的是一个可以根据其宽度具有固定高度的小部件,只要宽度改变,它就会更新。 @Joe Shanahan 试试看:) 哦,抱歉,澄清一下:我试过了,但没有正确设置高度。 @Joe Shanahan 对不起,什么样的高度? 以您拥有的图像为例:您将宽度设置为 3 个正方形。为了适应屏幕上的所有方块,需要 3 个方块的高度。如果您要缩放窗口(水平)以便连续有 4 个正方形,那么您只需要 2 个正方形的高度就可以将它们全部放在屏幕上。在任何时候,我都希望高度是水平适合所有孩子的确切值。不多也不少。

以上是关于PyQt5:创建强制固定高度的流布局的主要内容,如果未能解决你的问题,请参考以下文章

UICollectionView 流布局没有固定的行高

vue+uniapp瀑布流布局多种实现方式

瀑布流布局

瀑布流布局(等宽不等高)

RecyclerView 瀑布流布局

在 PyQt5/Pyside2 中动态添加小部件到流布局