How a QHBoxLayout could remove itself?

Question:

I have a code that makes some QHBoxLayouts with some elements in them. I want to remove each Row class (QHBoxLayouts object) when clicking on their own remove button.

Here is the code for testing it

from sys import exit, argv
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QScrollArea, QPushButton, QLineEdit

class MainWindow(QWidget):
    def __init__(self, parent = None) -> None:
        super().__init__(parent)
        self.sub_window = None
        self.screenWidth = 1920
        self.screenHeight = 1080
        self.windowWidth = 1000
        self.windowHeight = 800
        self.setupUi()
        self.show()

    def setupUi(self) -> None:
        self.setWindowTitle("Password Manager")
        self.setGeometry((self.screenWidth - self.windowWidth) // 2, (self.screenHeight - self.windowHeight) // 2, self.windowWidth, self.windowHeight)
        self.mainLayout = QVBoxLayout()
        self.scrollAreaLayoutContents = QVBoxLayout()
        self.scrollArea = QScrollArea()
        self.scrollArea.setLayout(self.scrollAreaLayoutContents)
        for i in range(10):
            self.scrollAreaLayoutContents.addLayout(Row(f"{i}"))
        self.mainLayout.addWidget(self.scrollArea)
        self.setLayout(self.mainLayout)

class Row(QHBoxLayout):
    def __init__(self, name:str) -> None:
        super().__init__()
        self.nameLineEdit = QLineEdit(f"{name}")
        self.nameLineEdit.setDisabled(True)
        self.addWidget(self.nameLineEdit)
        self.removeButton = QPushButton("remove")
        self.removeButton.clicked.connect(self.removeItSelf)
        self.addWidget(self.removeButton)
    
    def removeItSelf(self) -> None:
        self.removeItem(self)

if __name__ == "__main__":
    app = QApplication(argv)
    window = MainWindow()
    exit(app.exec())

I want to remove each Row class (QHBoxLayouts object) when clicking on their own remove button.

Asked By: DSA5252

||

Answers:

self.removeItem(self) will obviously not work, because removeItem() removes an item from the layout represented by self, and an item cannot remove itself from itself.

The removal should be done on the level of the parent layout, but there are three aspects that should be kept in mind:

  • there is no direct way to get the parent layout from a nested one;
  • removing the layout does not remove the widgets;
  • the layout could have other nested layout of its own;

The first point can be easily solved when talking about removal: since QLayout inherits from QObject, we can call deleteLater().

In your simple case, assuming that the contents are known and do not change, it could be done like this:

    def removeItSelf(self) -> None:
        self.nameLineEdit.deleteLater()
        self.removeButton.deleteLater()
        self.deleteLater()

In any other situations (for instance, if contents are dynamically added), the only way to ensure that all the contents get destroyed is by using a recursive function that iterates all layout items.

While that is feasible, there is a much simpler solution: instead of using a layout subclass, use a basic QWidget as a container. In that case, all widgets are children of the container, and deleting the container will automatically take care of their removal:

class Row(QWidget):
    def __init__(self, name:str) -> None:
        super().__init__()
        layout = QHBoxLayout(self)
        self.nameLineEdit = QLineEdit(f"{name}")
        self.nameLineEdit.setDisabled(True)
        layout.addWidget(self.nameLineEdit)
        self.removeButton = QPushButton("remove")
        self.removeButton.clicked.connect(self.removeItSelf)
        layout.addWidget(self.removeButton)
    
    def removeItSelf(self) -> None:
        self.deleteLater()

class MainWindow(QWidget):
    # ...

    def setupUi(self) -> None:
        # ...
        for i in range(10):
            self.scrollAreaLayoutContents.addWidget(Row(f"{i}"))
Answered By: musicamante
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.