Reputation: 952
I am using QGridLayout in my project. I will automatically add some widgets in QGridLayout. Thus, I implement a class MultiWidgetLayout
to manager the widget. My test code is:
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_()
This code basically fullfile what I want, but there still be a small problem.
When I add three widgets, the layout is:
However, I want the QLabel(2) occupy the whole space of the bottom row.
The layout of 5 widgets is:
But, I hope QLable(3) and QLabel(4) occupy the whole space of the bottom row.
Any suggestion is appreciated!
Upvotes: 0
Views: 579
Reputation: 48231
Since the widgets are going to have similar sizes, hints and policies, you can add any remaining widgets to a horizontal layout that is placed at the last row, spanning all columns.
Consider that there is no need to remove widgets and add them again, since addWidget()
automatically replaces their position in the layout and reparents them whenever required, which also makes unnecessary to call show()
on all of them.
This is important, for two reasons:
show()
overrides an explicit hide()
or setVisible(False)
, and that shouldn't happen);Also note that sqrt
and ceil
are already provided by the math
module, which is part of the python standard library. Numpy is quite a big module, and if you don't need that for anything else you're going to add an important requirement which would ask for unnecessary resources (not only in memory, but also including install, if not even actual building).
In the following implementation I used an iter()
to cycle through all widgets, so that there's no need for item indexing.
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))
Upvotes: 1