Search code examples
pythonpyqtpyqt5qlayoutqgridlayout

Why widgets overlaps in the OOP version


I stripped down my situation to a simple one: I want to program the GUI in PyQt5, where there is a main QGridLayout whose name is grid, in which there are another grid gridParamter and a QListView widget. In the gridParamter, there are 2 QLabel

Here is the code

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

def window():
    app = QApplication(sys.argv)
    win = QWidget()

    list1 = QListView()

    gridParameter = QGridLayout()

    idxRow = 0
    label_1 = QLabel("I am label one")
    gridParameter.addWidget(label_1, idxRow, 0)

    idxRow = 1
    label_2 = QLabel("I am label two")
    gridParameter.addWidget(label_2, idxRow, 1)

    grid = QGridLayout()
    grid.addLayout(gridParameter, 0, 0)
    grid.setSpacing(2)
    grid.addWidget(list1)

    win.setLayout(grid)
    win.show()
    sys.exit(app.exec_())

if __name__ == '__main__':      
    window()

which can produce the GUI as I expected. But when I try to rewrite it in OOP style, i.e.

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *


class MainWindow(QWidget):
    def __init__(self):
        QWidget.__init__(self)

        list1 = QListView(self)

        gridParameter = QGridLayout(self)

        idxRow = 0
        label_1 = QLabel("I am label one", self)
        gridParameter.addWidget(label_1, idxRow, 0)

        idxRow = 1
        label_2 = QLabel("I am label two", self)
        gridParameter.addWidget(label_2, idxRow, 1)

        grid = QGridLayout(self)
        grid.addLayout(gridParameter, 0, 0)
        grid.setSpacing(2)
        grid.addWidget(list1)

        self.setLayout(grid)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    mainWin = MainWindow()
    mainWin.show()
    sys.exit( app.exec_() )

I found that the label_1 overlaps with list1, and when I try to resize the main windows, list1 always takes the grid position (0, 0).


Solution

  • First you must understand the following:

    1. Keep in mind that the following expression:

      lay = FooLayout()
      some_widget.setWidget(lay)
      

    is equivalent to:

    lay = FooLayout(some_widget)
    

    And that both indicate that the layout will handle the geometry of the children.

    1. On the other hand, if a widget already has a layout, no other layout can be established unless the previous layout is deleted.

    So in your case only the first instruction works and not the next 2 so the layout grid will be eliminated and the listview is only maintained because it is a child of the widget.

    gridParameter = QGridLayout(self)
    # ...
    grid = QGridLayout(self)
    # ...
    self.setLayout(grid)
    

    In my case I avoid placing the parents of the widgets to see if there is a problem and also I only establish as a parent of the layout if necessary:

    import sys
    from PyQt5 import QtCore, QtGui, QtWidgets
    
    
    class MainWindow(QtWidgets.QWidget):
        def __init__(self):
            super(MainWindow, self).__init__()
    
            list1 = QtWidgets.QListView()
    
            gridParameter = QtWidgets.QGridLayout()
    
            idxRow = 0
            label_1 = QtWidgets.QLabel("I am label one")
            gridParameter.addWidget(label_1, idxRow, 0)
    
            idxRow = 1
            label_2 = QtWidgets.QLabel("I am label two")
            gridParameter.addWidget(label_2, idxRow, 1)
    
            grid = QtWidgets.QGridLayout(self) # <--- principal layout
            grid.addLayout(gridParameter, 0, 0)
            grid.setSpacing(2)
            grid.addWidget(list1)
    
    
    if __name__ == "__main__":
        app = QtWidgets.QApplication(sys.argv)
        mainWin = MainWindow()
        mainWin.show()
        sys.exit(app.exec_())
    

    In conclusion use self when necessary.