Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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", the QListWidget followed by a QHBoxLayout 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)
    

    0_1542986454220_Screenshot from 2018-11-23 15-19-16.png

    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 some QVBoxLayout 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 the QVBoxLayout, 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 a QVBoxLayout inside it to hold the two rows, in the hope that the outer QHBoxLayout would not allow the inner QVBoxLayout 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 of QBoxLayout 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 a QVBoxLayout 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?



  • @JonB said in Making widgets touch/stick together in a layout:

    Does that sound correct?

    No.
    I'm used to the Qt4 way of layout->addItem(new QSpacerItem(/*...*/)); but stretch probably does exactly the same and is the correct solution



  • @VRonin
    I beg your pardon? I believe you have mis-read. I have agreed with @jazzycamel that I need a stretch() 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?



  • It is up to a certain point. if your widgets are using QSizePolicy::Expanding they will compete for space at the same level as the spacers (streches)



  • 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()
    

Log in to reply