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.
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()