Reputation: 9650
I'm using the Qt framework to build my graphical user interface. I use a QGridLayout to position my QWidgets neatly.
The GUI looks like this:
My application regularly adds new widgets to the GUI at runtime. These new widgets are usually not added at the end of the QLayout, but somewhere in the middle.
The procedure to do this is a bit cumbersome. Applied on the figure above, I would need to take out widg_C
, widg_D
, ... from the QGridLayout. Next, I add widg_x
and widg_y
, and finally I put the other widgets back again.
This is how I remove widgets from the QGridLayout:
for i in reversed(range(myGridLayout.count())):
self.itemAt(i).widget().setParent(None)
###
As long as you're dealing with a small amount of widgets, this procedure is not a disaster. But in my application I display a lot of small widgets - perhaps 50 or more! The application freezes a second while this procedure is ongoing, which is very annoying to the user.
Is there a way to insert widgets somewhere in a QLayout, without the need to take out other widgets?
EDIT: Apparently the solution for the QVBoxLayout is very simple. Just use the function insertWidget(..)
instead of addWidget(..)
. The docs can be found a this link: http://doc.qt.io/qt-5/qboxlayout.html#insertWidget
Unfortunately, I couldn't find a similar function for the QGridLayout.
EDIT: Many people rightly mentioned that putting back a lot of widgets shouldn't cause a performance issue - it is very fast indeed (thank you @ekhumoro to point that out). Apparently, the performance issue I faced had to do with the algorithm putting the widgets back. It is a fairly complicated recursive algorithm that puts every widget on the right coordinates in the QGridLayout. This resulted in a "flicker" on my display. The widgets are taken out, and put back inside with some delay (due to the algorithm) - causing the flicker.
EDIT: I found a solution such that I can easily insert new rows into the QGridLayout. Inserting new rows means that I don't need to take out and replace all the widgets from scratch - hence I avoid the expensive recursive algorithm to run.
The solution can be found in my answer below.
Upvotes: 7
Views: 10772
Reputation: 9650
Thank you @ekhumoro, @Stuart Fisher, @vahancho and @mbjoe for your help. I eventually found a way to solve the issue. I no longer use the QGridLayout()
. Instead, I built a wrapper around the QVBoxLayout
to behave as if it was a GridLayout, with an extra function to insert new rows:
class CustomGridLayout(QVBoxLayout):
def __init__(self):
super(CustomGridLayout, self).__init__()
self.setAlignment(Qt.AlignTop) # !!!
self.setSpacing(20)
def addWidget(self, widget, row, col):
# 1. How many horizontal layouts (rows) are present?
horLaysNr = self.count()
# 2. Add rows if necessary
if row < horLaysNr:
pass
else:
while row >= horLaysNr:
lyt = QHBoxLayout()
lyt.setAlignment(Qt.AlignLeft)
self.addLayout(lyt)
horLaysNr = self.count()
###
###
# 3. Insert the widget at specified column
self.itemAt(row).insertWidget(col, widget)
''''''
def insertRow(self, row):
lyt = QHBoxLayout()
lyt.setAlignment(Qt.AlignLeft)
self.insertLayout(row, lyt)
''''''
def deleteRow(self, row):
for j in reversed(range(self.itemAt(row).count())):
self.itemAt(row).itemAt(j).widget().setParent(None)
###
self.itemAt(row).setParent(None)
def clear(self):
for i in reversed(range(self.count())):
for j in reversed(range(self.itemAt(i).count())):
self.itemAt(i).itemAt(j).widget().setParent(None)
###
###
for i in reversed(range(self.count())):
self.itemAt(i).setParent(None)
###
''''''
Upvotes: 4