Search code examples
pythonpyqtpyqt5qlayout

Unequal parts of layout PyQt5


I am facing currently the following problem: I have created a design for my PyQt layout, and trying to realize it manually, I read about possibilities how to generate python code from .ui file after QT Creator, but I want to learn the manual way. Here is my source code, I have divided all the necessary parts into group boxes, I don't know how to locate them properly.

And the next question, is it possible to hide the Group box "secret" when the Checkbox "Advanced" is off? I haven't found any tutorials like that.

My desired Layout

import sys
from PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QGridLayout, QCheckBox, QLabel, QGroupBox, QSpinBox, QVBoxLayout, QHBoxLayout, QGridLayout, QProgressBar
from PyQt5.Qt import QIcon

class Example(QWidget):

def __init__(self):
    super().__init__()
    self.initUI()

def initUI(self):

    self.setWindowIcon(QIcon('icon.png'))
    self.setFixedSize(400,200)
    self.setWindowTitle('My app')

    #self.statusBar().showMessage('ready')

    groupBox1 = QGroupBox('Config')
    vBox1 = QVBoxLayout(self)

    readFile = QPushButton('Read File')
    decodeFile = QPushButton('Decode')
    chBox = QCheckBox('Advanced')

    vBox1.addWidget(readFile)
    vBox1.addWidget(decodeFile)
    vBox1.addWidget(chBox)
    groupBox1.setLayout(vBox1)

    groupBox2 = QGroupBox('Secret')
    gLayout = QGridLayout(self)

    label1 = QLabel('Label 1')
    label2 = QLabel('Label 2')

    edit2 = QSpinBox()
    edit3 = QSpinBox()

    gLayout.addWidget(label1, 0, 0)       
    gLayout.addWidget(label2, 1, 0)
    gLayout.addWidget(edit2, 0, 1)
    gLayout.addWidget(edit3, 1, 1)

    groupBox2.setLayout(gLayout)

    groupBox3 = QGroupBox('Progress')
    vBox2 = QVBoxLayout(self)

    pBar = QProgressBar()

    vBox2.addWidget(pBar)
    groupBox3.setLayout(vBox2)

    hbox1 = QHBoxLayout(self)
    hbox1.addWidget(groupBox1)
    hbox1.addWidget(groupBox2)
    hbox1.addWidget(groupBox3)
    self.setLayout(hbox1)
    self.resize(480, 320);
    self.show()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

I would also appreciate any critic about the source code. How to write in a better way


Solution

  • In order to accommodate the widget as samples in your figure you must use a combination of QVBoxLayout, QHBoxLayout, QFormLayout. In order for the occupied space to always be proportional, we must use the stretch:

    {your layout}.addWidget(QWidget * widget, int stretch = 0, Qt::Alignment alignment = 0)
    {your layout}.addLayout(QLayout * layout, int stretch = 0)
    

    This factor is the weight given to the widget or sub-layouts within the parent layout.

    To hide the QGroupBox you must use the hide function, and to show it, it must be controlled with the signal stateChanged of the QCheckBox.

    def onSecret(s):
        if s == Qt.Checked:
            groupBox2.show()
        else: 
            groupBox2.hide()
    
    onSecret(chBox.checkState())
    chBox.stateChanged.connect(onSecret)
    

    As the groupBox secret is going to be hidden the groupbox progress will stay in the middle, to push it to the part we will use a QSpacerItem.

    Screenshots:

    enter image description here

    enter image description here

    Complete code:

    import sys
    from PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QFormLayout, QCheckBox, QLabel, QGroupBox, QSpinBox, QVBoxLayout, QHBoxLayout, QProgressBar, QSpacerItem, QSizePolicy
    from PyQt5.QtCore import Qt
    
    class Example(QWidget):
        def __init__(self, parent=None):
            QWidget.__init__(self, parent=parent)
            self.initUI()
    
        def initUI(self):
            self.setFixedSize(400,200)
            self.setWindowTitle('My app')
    
            hbox = QHBoxLayout(self)
            groupBox1 = QGroupBox("Config", self)
            vbox = QVBoxLayout(groupBox1)
            readFile = QPushButton("Read File", groupBox1)
            vbox.addWidget(readFile, 1)
    
            decodeFile = QPushButton("Decode", groupBox1)
            vbox.addWidget(decodeFile, 1)
    
            chBox = QCheckBox("Advanced", groupBox1)
            vbox.addWidget(chBox, 1)
    
    
            hbox.addWidget(groupBox1, 1)
    
            vbox2 = QVBoxLayout()
            groupBox2 = QGroupBox("Secret", self)
            vbox3 = QVBoxLayout(groupBox2)
            flay = QFormLayout()
    
            label1 = QLabel("Label 1", groupBox2)
            edit2 = QSpinBox(groupBox2)
    
            flay.addRow(label1, edit2)
    
            label2 = QLabel("Label 2", groupBox2)
            edit3 = QSpinBox(groupBox2)
    
            flay.addRow(label2, edit3)
    
            vbox3.addLayout(flay, 1)
    
            vbox2.addWidget(groupBox2)
    
            groupBox3 = QGroupBox("Progress", self)
    
            vbox4 = QVBoxLayout(groupBox3)
    
            pBar = QProgressBar(groupBox3)
    
            pBar.setValue(21)
            vbox4.addWidget(pBar)
            vbox2.addWidget(groupBox3)
            spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
            vbox2.addItem(spacerItem)
    
            hbox.addLayout(vbox2, 1)
    
            def onSecret(s):
                if s == Qt.Checked:
                    groupBox2.show()
                else: 
                    groupBox2.hide()
    
            onSecret(chBox.checkState())
            chBox.stateChanged.connect(onSecret)
    
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        ex = Example()
        ex.show()
        sys.exit(app.exec_())