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

Managing the Size of Layouts and Widgets within them...



  • Hi friends,

    I'm working on understanding how to get my application to look the way I want it to with the layouts functionality using PySide2. I have 3 specific questions/issues I'm looking for some help with.

    1. Is there a way to make it so that when a selection is made between two radio buttons one of two Widgets appears in the same place (in this case a QLineEdit and a QComboBox widget).

    2. Is there a way to control the spacing of widgets other than using padding? With my radio buttons I'm using a horizontal layout within a vertical layout to put them side by side but there are only two of them and I don't like how far apart they are so I'd like to center them within the layout with a fixed padding size between the two widgets. Is that possible?

    3. The application I'm building will be run in full screen mode when it's all built. Is there a way to fix the size of a column in a horizontal layout (or a row in a vertical layout) so it always takes up a specific percentage of the total window size. Currently I'm using layouts within layouts to structure the document. For example with the code below if I want VertLay (which is the layout used as the second column in HorizLay) to always be 1/3 of the total screen is there an easy way to do that?

    Thanks in advance for any advice you can give me. The code I'm working with is below.

    from PySide2.QtCore import Qt
    from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout
    from PySide2.QtWidgets import QLineEdit, QListWidget, QLabel
    from PySide2.QtWidgets import QRadioButton, QButtonGroup
    from PySide2.QtWidgets import QComboBox, QHBoxLayout
    
    
    from sys import exit as sysExit
    
    class Widget(QWidget):
        def __init__(self):
            QWidget.__init__(self)
    
            self.radiobutton1 = QRadioButton("New")
            self.radiobutton2 = QRadioButton("Load")
    
            self.label1 = QLabel("Name")
            self.newvalue = QLineEdit()
            self.selectvalue = QComboBox()
    
            self.datalist = QListWidget()
    
            self.radiobuttons = QButtonGroup()
            self.radiobuttons.addButton(self.radiobutton1)
            self.radiobuttons.addButton(self.radiobutton2)
    
            RadioLay = QHBoxLayout()
            RadioLay.addWidget(self.radiobutton1)
            RadioLay.addWidget(self.radiobutton2)
    
            VertLay = QVBoxLayout()
            VertLay.addLayout(RadioLay)
            VertLay.addWidget(self.label1)
            VertLay.addWidget(self.newvalue)
    
            HorizLay = QHBoxLayout()
            HorizLay.addWidget(self.datalist)
            HorizLay.addLayout(VertLay)
    
            self.setLayout(HorizLay)
    
    if __name__ == "__main__":
        MainEventHandler = QApplication([])
    
        application = Widget()
        application.show()
        
        sysExit(MainEventHandler.exec_())
    


  • @BD4L

    Heya,

    Yeah if you don't want the splitter you can just use a layout and set the setStretch factor
    https://doc.qt.io/qt-5/qboxlayout.html
    Just start two a simple widget with a layout and add two widget to it. Then play with setting the stretch to get a feel for the behavoir.

    For the radial button I use setMinimumSize(80, 20) for them, you can comment all these out first. Then the QSpacerItem "hor_spacer_center " is a fixed pixel amount to can control between the two radial buttons. Have a play with that to bring them closer together

    Cheers,

    Edit: expanding example

    from PySide2.QtWidgets import QHBoxLayout,QLineEdit, QListWidget,QSizePolicy, QApplication, QWidget
    
    from sys import exit as sysExit
    
    
    class Widget(QWidget):
        def __init__(self):
            QWidget.__init__(self)
    
            self.h_layout = QHBoxLayout()
            self.view = QListWidget()
            self.view.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
            self.line = QLineEdit()
            self.line.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
            self.h_layout.addWidget(self.view, stretch=3)
            self.h_layout.addWidget(self.line, stretch=1)
            self.setLayout(self.h_layout)
    
    
    if __name__ == "__main__":
        MainEventHandler = QApplication([])
        application = Widget()
        application.show()
        sysExit(MainEventHandler.exec_())
    


  • Hi,

    Personally I find using Qt Designer and generating a .ui is always easier for me. Other people like to code it. I'll try my best at the ladder :P

    1. For this kind of stuff i have just been creating all four widgets at once then hide and unhided then base on signals generated from the buttons. In this case i connected the toggle signal to a custom function to manage the visible attribute.

    2. you can crate a horizontal spacer between the radial buttons and set its width to a fixed amount. I don't think I need to add the other two horizontal spacer here. We might be able to set an alignment policy on the layout to center the buttons, but I cant test that atm.

    3. I created a splitter and set the stretch factor to 3 time of the data_list_widget, splitters are also nice as the user can move the ui around if there is a long text.

    Hope that helps,

    If anyone has a clean layout let me know, I am lazy and use .ui all the time :P

    from PySide2.QtCore import Qt
    from PySide2.QtWidgets import QApplication, QWidget
    from PySide2.QtWidgets import QLineEdit, QListWidget, QLabel, QComboBox, QRadioButton
    from PySide2.QtWidgets import QSplitter, QSpacerItem, QSizePolicy
    from PySide2.QtWidgets import QHBoxLayout, QGridLayout, QGroupBox
    
    from sys import exit as sysExit
    
    
    class Widget(QWidget):
        def __init__(self):
            QWidget.__init__(self)
    
            self.left_grid_layout = QGridLayout()
            self.left_side_properties_grp = QGroupBox('Properties')
            self.grp_grid_layout = QGridLayout()
            self.left_side_properties_grp.setLayout(self.grp_grid_layout)
            
            self.h_layout = QHBoxLayout()
            self.new_radio_btn = QRadioButton("New")
            self.new_radio_btn.setMinimumSize(80, 20)
            self.new_le = QLineEdit()
            self.new_le.setMinimumSize(80, 20)
            self.load_radio_btn = QRadioButton("Load")
            self.load_radio_btn.setMinimumSize(80, 20)
            self.load_cbox = QComboBox()
            self.load_cbox.setMinimumSize(80, 20)
            self.load_cbox.addItems(['Shmee', 'Shmoo', 'Foo'])
            self.hor_spacer_l = QSpacerItem(2, 2, QSizePolicy.Expanding, QSizePolicy.Minimum)
            self.hor_spacer_center = QSpacerItem(30, 2, QSizePolicy.Fixed, QSizePolicy.Fixed)
            self.hor_spacer_r = QSpacerItem(2, 2, QSizePolicy.Expanding, QSizePolicy.Minimum)
            self.h_layout.addItem(self.hor_spacer_l)
            self.h_layout.addWidget(self.new_radio_btn)
            self.h_layout.addWidget(self.new_le)
            self.h_layout.addItem(self.hor_spacer_center)
            self.h_layout.addWidget(self.load_radio_btn)
            self.h_layout.addWidget(self.load_cbox)
            self.h_layout.addItem(self.hor_spacer_r)
            self.name_lb = QLabel("Name")
    
            self.name_le = QLineEdit()
            self.vertical_spacer = QSpacerItem(2, 2, QSizePolicy.Minimum, QSizePolicy.Expanding)
    
            self.grp_grid_layout.addLayout(self.h_layout, 0, 0)
            self.left_grid_layout.setAlignment(self.h_layout, Qt.AlignRight)
            self.grp_grid_layout.addWidget(self.name_lb)
            self.grp_grid_layout.addWidget(self.name_le)
            self.grp_grid_layout.addItem(self.vertical_spacer)
    
            self.data_list_widget = QListWidget()
            self.vert_splitter = QSplitter(Qt.Horizontal)
            self.vert_splitter.addWidget(self.data_list_widget)
            self.vert_splitter.addWidget(self.left_side_properties_grp)
            self.vert_splitter.setStretchFactor(0, 3)
            self.vert_splitter.setStretchFactor(1, 1)
    
            self.left_grid_layout.addWidget(self.vert_splitter)
    
            self.setLayout(self.left_grid_layout)
    
            self.new_le.setVisible(False)
            self.load_cbox.setVisible(False)
    
            self.new_radio_btn.toggled.connect(lambda: self.manage_buttons(True))
            self.load_radio_btn.toggled.connect(lambda: self.manage_buttons(False))
    
    
        def manage_buttons(self, toggle):
            self.new_le.setVisible(toggle)
            self.new_radio_btn.setVisible(not toggle)
            self.load_cbox.setVisible(not toggle)
            self.load_radio_btn.setVisible( toggle)
    
    
    if __name__ == "__main__":
        MainEventHandler = QApplication([])
    
        application = Widget()
        application.show()
    
        sysExit(MainEventHandler.exec_())
    

  • Banned

    @alom well at least you are honest, to that I tip my hat to you. However from my experience with my students using the Designer UI code takes a lot more energy in the long run than designing the GUI using proper Qt code

    Further while the Slider is a method perhaps needed with the Designer and GridLayout and Lambdas work they are all a bit more complicated than this situation really warrants. Here is a simpler version that should be real easy to follow. Note I consider Lambdas to be a style thing mostly although when applying K.I.S.S. to a project Lambdas end up being unnecessary most of the time and just causes your code to be slightly or a lot more complicated (depending on the expression used) than it needs to be

    from PySide2.QtCore    import Qt, Slot
    from PySide2.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout
    from PySide2.QtWidgets import QButtonGroup, QLabel, QListWidget, QStackedWidget
    from PySide2.QtWidgets import QLineEdit, QComboBox, QRadioButton
    
    from sys import exit as sysExit
    
    class Widget(QWidget):
        def __init__(self):
            QWidget.__init__(self)
    
            self.rdoNew = QRadioButton("New")
            self.rdoNew.setChecked(True)
            self.rdoNew.toggled.connect(self.TogNew)
    
            self.rdoLoad = QRadioButton("Load")
            self.rdoNew.setChecked(False)
            self.rdoLoad.toggled.connect(self.TogLoad)
    
            self.grpRadios = QButtonGroup()
            self.grpRadios.addButton(self.rdoNew)
            self.grpRadios.addButton(self.rdoLoad)
    
            hbxRadio = QHBoxLayout()
            hbxRadio.addWidget(self.rdoNew)
            hbxRadio.addWidget(QLabel('     '))
            hbxRadio.addWidget(self.rdoLoad)
            hbxRadio.addStretch(1)
    
            self.lneNewVal = QLineEdit()
            self.lneNewVal.setVisible(True)
    
            self.cboSelect = QComboBox()
            self.cboSelect.setVisible(False)
    
            self.stwSlctr = QStackedWidget()
            self.stwSlctr.addWidget(self.lneNewVal)
            self.stwSlctr.addWidget(self.cboSelect)
            self.stwSlctr.setCurrentIndex(0)
    
            self.lblName   = QLabel("Name")
            hbxNamLine = QHBoxLayout()
            hbxNamLine.addWidget(self.lblName)
            hbxNamLine.addWidget(self.stwSlctr)
    
            self.DataList = QListWidget()
    
            vbxFull = QVBoxLayout()
            vbxFull.addLayout(hbxRadio)
            vbxFull.addLayout(hbxNamLine)
            vbxFull.addWidget(self.DataList)
    
            self.setLayout(vbxFull)
    
        @Slot()
        def TogNew(self):
            self.stwSlctr.setCurrentIndex(0)
    
        @Slot()
        def TogLoad(self):
            self.stwSlctr.setCurrentIndex(1)
    
    if __name__ == "__main__":
        MainEventHandler = QApplication([])
    
        application = Widget()
        application.show()
    
        sysExit(MainEventHandler.exec_())
    
      # If anyone wants more extensive free help I run an online lab-like classroom-like 
      # message server feel free and drop by you will not be able to post until I clear 
      # you as a student as this prevents spammers so if interested here is the invite
      # https://discord.gg/3D8huKC
    


  • @alom @Denni-0

    Hey friends thanks for the quick responses. These are really helpful. The toggles work great for displaying only the fields I want to show, and I really like the vertical spacer that alom used but I'm wondering if there's a way to make it so users can't move the spacer to adjust the size of the column.

    The one thing that still doesn't seem to be working is centering the radio buttons in their layout area. When the view is stretched the radio buttons are shifted left of center with too much spacing between them:

    Layout2.PNG

    When the view is more compact the New button is left justified instead of centered and there's still too much space between them.

    Layout1.PNG

    I don't really want to use a label or anything like that to offset them because that could cause problems as the program is used on computers with different screen sizes. I guess what I'm really looking to understand is what tools do I have with PySides an QT that will allow me to control the placement of widgets within a layout but still allow for responsive design.


  • Banned

    QGridLayout -- which is a much more static but responsive method

    Or you just figure out how to use the QVBoxLayout and QHBoxLayout in a way that achieves the end result you are seeking.

    I believe one of these 2 methods will generally work for all situations assuming you are not using the Designer that is

    Also I noticed you do not default one of your radio buttons to True even though you are doing so with the display by displaying the New field rather than the combo box -- you should really sync that up in order to make it look right to the user

    Also yes you can make your vertical spacer static but that kind of defeats the purpose of it -- but I do believe you can create a static spacer that does not use that object as well



  • @BD4L

    Heya,

    Yeah if you don't want the splitter you can just use a layout and set the setStretch factor
    https://doc.qt.io/qt-5/qboxlayout.html
    Just start two a simple widget with a layout and add two widget to it. Then play with setting the stretch to get a feel for the behavoir.

    For the radial button I use setMinimumSize(80, 20) for them, you can comment all these out first. Then the QSpacerItem "hor_spacer_center " is a fixed pixel amount to can control between the two radial buttons. Have a play with that to bring them closer together

    Cheers,

    Edit: expanding example

    from PySide2.QtWidgets import QHBoxLayout,QLineEdit, QListWidget,QSizePolicy, QApplication, QWidget
    
    from sys import exit as sysExit
    
    
    class Widget(QWidget):
        def __init__(self):
            QWidget.__init__(self)
    
            self.h_layout = QHBoxLayout()
            self.view = QListWidget()
            self.view.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
            self.line = QLineEdit()
            self.line.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
            self.h_layout.addWidget(self.view, stretch=3)
            self.h_layout.addWidget(self.line, stretch=1)
            self.setLayout(self.h_layout)
    
    
    if __name__ == "__main__":
        MainEventHandler = QApplication([])
        application = Widget()
        application.show()
        sysExit(MainEventHandler.exec_())
    

Log in to reply