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

Deleting a specific row from a QFormLayout



  • I have a list of an arbitrary number of dictionaries I want to build in to a QFormLayout. Each element from the list will represent a row in the layout and, nested within that, will be a row for each key in the dictionary - QLabel with the key name, QLineEdit for the value.
    Programmatically, this looks like:

    from os import linesep
    import sys
    from PySide2 import QtWidgets, QtGui
    from PySide2.QtCore import Signal, Slot
    from PySide2.QtGui import QColor, QFont, QPalette, Qt, qBlue
    from PySide2.QtWidgets import (
        QApplication,
        QFileDialog,
        QFormLayout, QFrame,
        QHBoxLayout,
        QLabel,
        QLineEdit,
        QMessageBox,
        QPushButton, QSpacerItem,
        QVBoxLayout,
    )
    
    myList = [
        {
            "name": "mn",
            "phone": "2806"
        },
        {
            "name": "ts",
            "phone": "2800"
        },
        {
            "name": "mb",
            "phone": "2801"
        }
    ]
    
    outRow = QFormLayout()
    class nestedLayouts(QtWidgets.QWidget):
        def __init__(self):
            super().__init__()
            mainLt = QVBoxLayout()
    
            for l in myList:
                print (f"List element=>{l}")
                print(outRow.rowCount())
                deleteButton = delBtn(outRow.rowCount())
                deleteButton.setText(f"delete row {outRow.rowCount()}")
                deleteButton.clicked.connect(self.deleteRow)
                outRow.addRow(deleteButton, newRow(l))
                print (outRow.rowCount())
    
            mainLt.addLayout(outRow)
            self.setLayout(mainLt)
        
        @Slot(int)
        def deleteRow(self, num):
            print("deleting a row...", self, num)
            outRow.removeRow(num)
    
    class newRow(QFormLayout):
        def __init__(self, pList):
            super().__init__()
            self.pList = pList
    
            print (f"newRow dictionary input=>{pList}")
            for name, phone in pList.items():
                print(name, phone)
                label = QLabel(name)
                field = QLineEdit()
                field.setText(phone)
                self.addRow(label, field)
    
    class delBtn(QPushButton):
        def __init__(self, row):
            super().__init__()
            clicked = Signal(int)
    
            # clicked.emit(row)
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        app.setStyle("windowsvista")
        nest = nestedLayouts()
        nest.resize(800, 300)
        nest.show()
        sys.exit(app.exec_())
    

    Now, the should produce a layout of rows, each with a "delete" button adjacent to a set of label/field rows. And when I press the "delete" button, it should remove its parent row from the layout.

    How do I tell my deleteRow Slot function the row number of the calling button so I can issue an appropriate outRow.removeRow("the rownumber of the button's row")? The sample above ALWAYS deletes the first row and throws an index error looking for row 0.


  • Lifetime Qt Champion

    Hi,

    There are several possibilities. You can use a lambda. You can set the number on your button using a dynamic property and then use the sender method to retrieve the caller and from it the number in the dynamic property. You can use QSignalMapper.



  • Thanks SGaist,

    I'm too new to this to fully understand what you've said. I'm not sure how to set a dynamic property but I have seen references to sender methods - I'll have to do some further reading for that. Also not sure where I'd put the lambda you suggest or how to use QSignalMapper (more reading I guess!).

    However, I found that by defining the "delBtn" inside the row it belongs to, instead of creating it outside and then adding it, the deleteRow (now renamed to zapRow) slot function operates on its own row.

    from os import linesep
    import sys
    from PySide2 import QtWidgets, QtGui
    from PySide2.QtCore import Signal, Slot
    from PySide2.QtGui import QColor, QFont, QPalette, Qt, qBlue
    from PySide2.QtWidgets import (
        QApplication,
        QFileDialog,
        QFormLayout, QFrame,
        QHBoxLayout,
        QLabel,
        QLineEdit,
        QMessageBox,
        QPushButton, QSpacerItem,
        QVBoxLayout,
    )
    
    myList = [    {"name": "mn",  "phone": "2806"    },
        {"name": "ts", "phone": "2800" },
        {"name": "mb", "phone": "2801" }
    ]
    
    outRow = QFormLayout()
    class nestedLayouts(QtWidgets.QWidget):
        def __init__(self):
            super().__init__()
            mainLt = QVBoxLayout()
            for l in myList:
                outRow.addRow(delBtn(outRow.rowCount()), newRow(l))
            outRow.addRow(addBtn(outRow.rowCount()))
            mainLt.addLayout(outRow)
            self.setLayout(mainLt)
    
    class delBtn(QPushButton):
        def __init__(self, row):
            super().__init__()
            self.num = row
            self.setText(f"Delete this row")
            self.clicked.connect(self.zapRow)
    
        @Slot()
        def zapRow(self):
            row = outRow.getWidgetPosition(self)[0]
            print("deleting a row...", row)
            outRow.removeRow(self)
            print(f"delBtn {row} deleted, outRow count = {outRow.rowCount()}")
    
    class addBtn(QPushButton):
        def __init__(self, row):
            super().__init__()
            self.num = row
            self.setText(f"Add a row")
            self.clicked.connect(self.newRow)
    
        @Slot()
        def newRow(self):
            row = outRow.getWidgetPosition(self)[0]
            outRow.insertRow(row, delBtn(row), newRow({"name": "?", "phone": "?"}))
    
    class newRow(QFormLayout):
        def __init__(self, pList):
            super().__init__()
            self.pList = pList
            for name, phone in pList.items():
                print(name, phone)
                label = QLabel(name)
                field = QLineEdit()
                field.setText(phone)
                self.addRow(label, field)
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        app.setStyle("windowsvista")
        nest = nestedLayouts()
        nest.resize(800, 300)
        nest.show()
        sys.exit(app.exec_())
    

Log in to reply