Home > OS >  How can I obtain the index or the position of a clicked element on QGridLayout PyQt5?
How can I obtain the index or the position of a clicked element on QGridLayout PyQt5?

Time:02-26

I need to get the index or position of a pushbutton that was created dynamically in pyqt5, I have this code but currently when I click on the [-] buttons I get the position of the last added pushbutton.

import sys
from PyQt5.QtWidgets import (QLineEdit, QLabel, QGridLayout, QWidget, 
                             QPushButton, QApplication, QSpinBox)

class Window(QWidget):
    def __init__(self):
        super().__init__()

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

        #Creating elements
        self.spn_amount = QSpinBox(self)
        self.txt_registercode = QLineEdit(self)
        self.txt_description = QLineEdit(self)
        self.txt_cost = QLineEdit(self)
        self.txt_total = QLineEdit(self) 
        self.btn_add = QPushButton(" ",self) #Button that add rows

        #adding functionality to [ ] button
        self.btn_add.clicked.connect(self.getrow_add)
        
        #Adding elements to grid
        self.grid.addWidget(self.spn_amount, 0, 0)
        self.grid.addWidget(self.txt_registercode, 0, 1)
        self.grid.addWidget(self.txt_description, 0, 2)
        self.grid.addWidget(self.txt_cost, 0, 3)
        self.grid.addWidget(self.txt_total, 0, 4)
        self.grid.addWidget(self.btn_add, 0, 5)

        self.row = 0
        self.column = 0

    def getrow_add(self): #Function that get a the actual row of the button to create a new row after
        self.row = self.grid.rowCount() #Obtener el número de filas actuales en el grid
        self.column = self.grid.columnCount() #Obtiene el número de columnas actuales en el grid
        self.addtextbox()

    def getrow_del(self):
        index = self.grid.indexOf(self.btn_del_din)
        position = self.grid.getItemPosition(index)
        self.row = position[0] #Obtengo la posición de la fila
        self.column = self.grid.columnCount() #Obtiene el número de columnas actuales en el grid
        print(self.row)


    def addtextbox(self):
        
        self.lineEdits = []
            
        #Creation of dynamicall elements
        self.spn_amount_din = QSpinBox(self)
        self.txt_registercode_din = QLineEdit(self)
        self.txt_description_din = QLineEdit(self)
        self.txt_cost_din = QLineEdit(self)
        self.txt_total_din = QLineEdit(self) 
        self.btn_del_din = QPushButton("-",self) #Button that delete rows

        #Adding functionality to the [-] button
        self.btn_del_din.clicked.connect(self.getrow_del)

        #Dynamically adding elements to grid
        self.grid.addWidget(self.spn_amount_din, self.row, 0)
        self.grid.addWidget(self.txt_registercode_din, self.row, 1)
        self.grid.addWidget(self.txt_description_din, self.row, 2)
        self.grid.addWidget(self.txt_cost_din, self.row, 3)
        self.grid.addWidget(self.txt_total_din, self.row, 4)
        self.grid.addWidget(self.btn_del_din, self.row, 5)

        self.lineEdits.append('')

    def deltextbox(self):

        for i in range (1, self.column):
            self.grid.itemAtPosition(self.row, i).widget().deleteLater()
        

if __name__ == "__main__":
    application = QApplication(sys.argv)
    window = Window()
    window.setWindowTitle('Dynamically add elements')
    window.show()
    sys.exit(application.exec_())

When I click on the [-] buttons I need to get the specific position of that widget.

Here is my GUI

For example here I have 6 rows and 6 columns, if I click on the first button [-] I would expect to get the value of the row as 1, and the value of the column as 5, however I get the value not from that first button [-] but from the last added, obtained for row 5 and for column 5.

In my code I get that with the function:

def getrow_del(self):
    index = self.grid.indexOf(self.btn_del_din)
    position = self.grid.getItemPosition(index)
    self.row = position[0] #Obtengo la posición de la fila
    self.column = self.grid.columnCount() #Obtiene el número de columnas actuales en el grid
    print(self.row)

CodePudding user response:

The main problem with your code is that you're constantly overwriting instance attributes every time a new row is created.

When using functions that create objects dynamically, you should never create instance attributes for those objects unless you specifically want to keep a reference to the last elements created (which is not your case).

In order to properly keep references to those elements, they must be added to a persistent object container (such as a list) that is an instance attribute.

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.grid = QGridLayout(self)
        self.items = []
        self.addRow()

    def addRow(self):
        spn_amount_din = QSpinBox()
        txt_registercode_din = QLineEdit()
        txt_description_din = QLineEdit()
        txt_cost_din = QLineEdit()
        txt_total_din = QLineEdit() 

        row = len(self.items)
        if row == 0:
            button = QPushButton(' ')
            button.clicked.connect(self.addRow)
        else:
            button = QPushButton('-')
            button.clicked.connect(lambda: self.removeRow())

        row_items = (spn_amount_din, txt_registercode_din, txt_description_din, txt_cost_din, txt_total_din, button)
        self.items.insert(row, row_items)
        for column, widget in enumerate(row_items):
            self.grid.addWidget(widget, row, column)

    def removeRow(self, row=None):
        if row is None:
            button = self.sender()
            for row, widgets in enumerate(self.items):
                if button in widgets:
                    break
        widgets = self.items.pop(row)
        for widget in widgets:
            self.grid.removeWidget(widget)
            widget.deleteLater()
        self.adjustSize()

Be aware that while QGridLayout provides rowCount() and columnCount(), they should be used with care, as they are mostly used internally and for basic, static purposes: the row and column count is not updated when widgets are removed; this means that if you want to achieve a more advanced implementation (such as inserting rows or sorting items), QGridLayout is usually not a proper candidate.

A better solution is to use a main QVBoxLayout and add each row as a nested QHBoxLayout, but an even better implementation should actually use a class as container for all widgets, which makes the whole concept much easier to manage and allows direct access to each row element using their attribute:

class OrderWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.spn_amount_din = QSpinBox()
        self.txt_registercode_din = QLineEdit()
        self.txt_description_din = QLineEdit()
        self.txt_cost_din = QLineEdit()
        self.txt_total_din = QLineEdit()
        self.button = QPushButton()
        
        layout = QHBoxLayout(self)
        layout.addWidget(self.spn_amount_din)
        layout.addWidget(self.txt_registercode_din)
        layout.addWidget(self.txt_description_din)
        layout.addWidget(self.txt_cost_din)
        layout.addWidget(self.txt_total_din)
        layout.addWidget(self.button)


class Window(QWidget):
    def __init__(self):
        super().__init__()
        QVBoxLayout(self) # just create the layout and set it
        self.items = []
        self.insertRow(0)

    def insertRow(self, row=None):
        if row is None:
            row = len(self.items)
        widget = OrderWidget()
        self.layout().addWidget(widget)
        self.items.insert(row, widget)
        if row == 0:
            widget.button.setText(' ')
            widget.button.clicked.connect(lambda: self.insertRow())
        else:
            widget.button.setText('-')
            widget.button.clicked.connect(lambda: self.removeRow(widget))

    def removeRow(self, row):
        if isinstance(row, QWidget):
            widget = row
            row = self.items.index(widget)
        else:
            widget = self.items[row]
        self.layout().removeWidget(widget)
        widget.deleteLater()
        del self.items[row]
        self.adjustSize()
  • Related