Making widgets touch/stick together in a layout
-
Well, I may have tried this one before, but I'm stuck again....
- I have a (what I call) a "composite" widget, i.e. it contains several widgets.
- I want a
QListWidget
... - and immediately under it a row of buttons
So, a top-level
QVBoxLayout
containing two "items", theQListWidget
followed by aQHBoxLayout
on which the buttons will be placed, right?class JEditableListCompositeWidget(QtWidgets.QWidget): def __init__(self, parent: QtWidgets.QWidget=None): super().__init__(parent) # the top-level QVBoxLayout vLayout = QtWidgets.QVBoxLayout() self.setLayout(vLayout) # the QListWidget self.lw = QtWidgets.QListWidget() self.lw.setFixedHeight(50) vLayout.addWidget(self.lw) # the following QHBoxLayout, with the buttons on it hLayout = QtWidgets.QHBoxLayout(self) self.btnAdd = QtWidgets.QPushButton("Add") hLayout.addWidget(self.btnAdd) self.btnDelete = QtWidgets.QPushButton("Delete") hLayout.addWidget(self.btnDelete) vLayout.addLayout(hLayout)
No! I do not want that gap between the bottom of the listbox and the top of the buttons, I want the buttons "attached" to just below the listbox. (And note that this time, unlike some previous topic I raised on this issue, I am accepting that the
QListWidget
has a fixed height, so it does not need grow-space below it.)I don't care if I have to introduce an extra level of
QBoxLayout
if that would solve. It must not matter where I place this whole widget --- maybe inside someQVBoxLayout
in the parent dialog --- I do not want stretch introduced between these two rows of controls.Now this cannot be so hard, can it...? :)
-
So the answer is: to keep widgets together when stretching is involved, and it make such that multiple/composite widgets behave as if you had only added a single widget there, you need explicit stretches above & below, something like:
vLayout = QVBoxLayout() vLayout.addStretch() vLayout.addWidget(widget1) vLayout.addWidget(widget2) vLayout.addStretch()
-
Try adding a spacer item to the bottom of the layout after the buttons as follows:
vLayout.addStretch()
This should create a QSpacerItem that fills the available space beneath the buttons and pushes them up to meet the QListWidget.
-
@jazzycamel
Well, that's a pretty good start, 'coz it makes it work for now, so thank you!That made the whole widget come at the top of its parent container/layout. So I also put a second
vLayout.addStretch()
at the start of theQVBoxLayout
, above the listbox, to balance the one at the bottom. That centered the location of the composite widget vertically in its container, which seems right.But, don't you & I know your solution is really "dirty"? :) I don't want to put stretch at the bottom (or the top), I just want no stretch between the two rows! I sort of wondered if I should put them inside some new, say,
QHBoxLayout
, which could have aQVBoxLayout
inside it to hold the two rows, in the hope that the outerQHBoxLayout
would not allow the innerQVBoxLayout
to stretch at all, but I haven't tried it... -
@JonB
I disagree: I don't think its quick and dirty. Qt has to make a default assumption of how to pack the layout and it chooses to evenly distribute the widgets. It then gives you the option to modify that behaviour by adding stretch. If it was packed the way you prefer by default, they would then have to add an option add stretch between all the widgets to evenly distribute them.To my mind, your alternative solution of multiple nested layouts feels 'dirty'... I guess its all a matter of opinion and preference :) I'm glad it's working for you either way.
-
@jazzycamel
No, that's fine, if that is indeed the way I'm supposed to achieve it I'm good with that. Yes, another layer ofQBoxLayout
to achieve would have been messy.Am I right or wrong to think that if I'm going to add a
stretch()
at the bottom, after the buttons, I should also add one at the top, before the list widget, to "even it up"? Or, is it intended that the stretch should be at the bottom only and not at the top?EDIT I think I am right, because if I replace the whole thing by just a
QListWidget
, placed on aQVBoxLayout
in a dialog, the listbox appears at the vertical center of the layout, not at the top which is what it does if I only add your stretch once at the bottom, whereas my two stretches make the composite widget also be in the vertical center. Does that sound correct? -
@VRonin
I beg your pardon? I believe you have mis-read. I have agreed with @jazzycamel that I need astretch()
at the bottom, after the buttons, then my gap between the listbox and the buttons disappears. That is not the final question. I am saying that done only like that causes the widget's contents, as a whole, to appear at the top of their container, presumably because all the stretch goes to the bottom.I am then saying that if I add another, second
stretch()
at the top of my composite widget, above the listbox, any stretch gets distributed evenly to the top & bottom (but not middle) of my widget. That makes it place its content in the vertical center of its container.I am asking if that sounds correct, because only this way does my composite behave like if all I had was a listbox in the first place. I have verified that would have appeared vertically centered in the container, not at the top. So I've made my widget behave identically by having a stretch at the top and at the bottom. Doesn't that not sound correct?
-
So the answer is: to keep widgets together when stretching is involved, and it make such that multiple/composite widgets behave as if you had only added a single widget there, you need explicit stretches above & below, something like:
vLayout = QVBoxLayout() vLayout.addStretch() vLayout.addWidget(widget1) vLayout.addWidget(widget2) vLayout.addStretch()