【问题标题】:PyQT5: how to automatically align the widget in QGridLayout?PyQT5:如何在 QGridLayout 中自动对齐小部件?
【发布时间】:2021-05-30 09:10:26
【问题描述】:

我在我的项目中使用 QGridLayout。我会在 QGridLayout 中自动添加一些小部件。因此,我实现了一个类MultiWidgetLayout 来管理小部件。我的测试代码是:

import numpy as np
from PyQt5.QtWidgets import *
import sys
class MultiWidgetLayout(QGridLayout):

    def __init__(self):
        super().__init__()
        self.setSpacing(0)
        self.setContentsMargins(1,1,1,1)
        self.__widgets = []

    def getWidgetNum(self):
        return len(self.__widgets)

    def addWidget(self, widget):
        self.__widgets.append(widget)
        self.updateLayout()

    def setWidgets(self, widgets):
        self.__widgets = widgets
        self.updateLayout()

    def updateLayout(self):
        self.removeAllWidgets()
        n = len(self.__widgets)
        columns = int(np.ceil(np.sqrt(n)))
        rows = n//columns

        k = 0
        for r in range(rows):
            for c in range(columns):
                super().addWidget(self.__widgets[k], r, c)
                k += 1
        remain = n - columns*rows
        if remain > 0:
            for r in range(remain):
                super().addWidget(self.__widgets[k], rows, r)
                k += 1

        for widget in self.__widgets:
            widget.show()

    def removeAllWidgets(self):
        '''
        remove all the widgets
        '''
        N = self.count()
        for i in range(N-1,-1,-1):
            item = self.itemAt(i) # item is QHBoxLayout
            self.removeItem(item)
            # self.removeWidget(item)

        for widget in self.__widgets:
            widget.hide()

class Window(QWidget):

    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        self.setLayout(layout)
        self.multiWidgetLayout = MultiWidgetLayout()
        self.btn = QPushButton('add widget')
        self.btn.clicked.connect(self.addWidgetSlot)
        layout.addWidget(self.btn)
        layout.addLayout(self.multiWidgetLayout)

    def addWidgetSlot(self, check=False):
        num = self.multiWidgetLayout.getWidgetNum()
        label = QLabel(str(num))
        label.setStyleSheet('border: 2px solid red;')
        label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.multiWidgetLayout.addWidget(label)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    app.exec_()

这段代码基本上fullfile了我想要的,但还是有一个小问题。

当我添加三个小部件时,布局是:

但是,我希望 QLabel(2) 占据底行的整个空间。

5个小部件的布局是:

但是,我希望 QLable(3) 和 QLabel(4) 占据最后一行的整个空间。

欢迎提出任何建议!

【问题讨论】:

  • 这会很棘手:如果添加具有不同最小/最大尺寸或尺寸策略的小部件会怎样?
  • 所有小部件的策略应该相同。

标签: layout pyqt5


【解决方案1】:

由于小部件将具有相似的大小、提示和策略,因此您可以将任何剩余的小部件添加到放置在最后一行、跨越所有列的水平布局中。

考虑到没有必要删除小部件并再次添加它们,因为addWidget() 会自动替换它们在布局中的位置并在需要时重新设置它们,这也不需要调用show()所有这些。
这很重要,原因有两个:

  1. show() 会覆盖显式的 hide()setVisible(False),这不应该发生);
  2. 从布局中删除项目并不总是有效的,因为项目的父项(小部件或布局)不会更改,并且可能导致意外行为;

还要注意sqrtceil 已经由math 模块提供,该模块是python 标准库的一部分。 Numpy 是一个相当大的模块,如果你不需要它来做其他任何事情,你将添加一个重要的要求,这将要求不必要的资源(不仅在内存中,而且还包括安装,即使不是实际的 建筑)。

在以下实现中,我使用iter() 循环浏览所有小部件,因此不需要项目索引。

from math import sqrt, ceil
from PyQt5.QtWidgets import *
import sys

class MultiWidgetLayout(QGridLayout):
    def __init__(self):
        super().__init__()
        self.setSpacing(0)
        self.setContentsMargins(1, 1, 1, 1)
        self.__widgets = []
        self.__lastLayout = QHBoxLayout()

    # ...

    def updateLayout(self):
        n = len(self.__widgets)
        columns = ceil(sqrt(n))
        rows, remain = divmod(n, columns)
        widgets = iter(self.__widgets)
        for r in range(rows):
            for c in range(columns):
                super().addWidget(next(widgets), r, c)
        self.removeItem(self.__lastLayout)
        if remain:
            self.addLayout(self.__lastLayout, rows, 0, 1, columns)
            for i in range(remain):
                self.__lastLayout.addWidget(next(widgets))

【讨论】:

  • 如果我想清除QGridLayout中的所有小部件怎么办?如何解决这个问题?
  • @QiangZhang 如果“清除”是指删除,则在每个对象上使用deleteLater(),同时将它们从列表中删除:while self.__widgets: self.__widgets.pop().deleteLater();如果你的意思是别的,请澄清。
  • “清除”意味着删除 QGridLayout 中包含的所有小部件。这样我就可以将小部件重新添加到此布局中。我不想删除小部件。
  • 为什么需要“清除”小部件并重新添加?
  • 例如用户在布局中添加widget1/widget2/widget3。然后,用户只想在布局中添加 widget4/widget5。所以,我们需要清除widget1/2/3并重新添加widget 4/5。
猜你喜欢
  • 2021-07-23
  • 1970-01-01
  • 1970-01-01
  • 2021-08-06
  • 2021-10-12
  • 1970-01-01
  • 2019-12-09
  • 1970-01-01
  • 2012-02-27
相关资源
最近更新 更多