0

I'm trying to create buttons (QPushButtons) based on an entry (QLineEdit). The idea is that I want the user to be able to create as many buttons as wanted, simply by adding new text in the entry box and by then pressing "Add Label" (see picture below).

UI so far

While I'm able to do this, I can't for now retrieve the label value of each of these buttons, since the procedure I use erases all the previous values (I can only retrieve the last value entered). I'd like to be able to print each specific Label Value when clicking each button.

My code is below:

from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLineEdit
import sys

    class MyWindow(QMainWindow):
        def __init__(self):
            super(MyWindow, self).__init__()
            self.setGeometry(100, 100, 1500, 1500)
            self.setWindowTitle("My Program")
            self.labelButtons = []  # List of all the buttons displaying labels
            self.eraseButtons = []  # List of all the buttons displaying "X"
            self.Yposition = 50
            self.initUI()

        def initUI(self):
            self.labelEntry = QLineEdit(self)
            self.labelEntry.move(50, self.Yposition)
            self.labelEntry.resize(300, 40)

            self.addLabelButton = QPushButton(self)
            self.addLabelButton.setText("Add Label")
            self.addLabelButton.move(400, self.Yposition)
            self.addLabelButton.resize(300, 40)
            self.addLabelButton.clicked.connect(self.addNewLabel)

        def addNewLabel(self):
            self.Yposition += 50
            self.newLabelName = self.labelEntry.text()
            self.labelButtons.append(self.createButtonLabel(self.newLabelName))
            self.eraseButtons.append(self.eraseButtonLabel())
            self.updatelabels()

        def createButtonLabel(self, labelname):
            self.button = QPushButton(self)
            self.button.setText(str(labelname))
            self.button.resize(300, 40)
            self.button.move(50, self.Yposition)
            self.button.clicked.connect(self.printbutton)
            return self.button

        def eraseButtonLabel(self):
            self.buttonErase = QPushButton(self)
            self.buttonErase.setText("X")
            self.buttonErase.resize(40, 40)
            self.buttonErase.move(360, self.Yposition)
            self.buttonErase.clicked.connect(self.printbutton)
            return self.buttonErase

        def updatelabels(self):
            for button in self.labelButtons:
                button.show()
            for button in self.eraseButtons:
                button.show()

        def printbutton(self):
            print(self.button.text())


    if __name__ == '__main__':
        app = QApplication(sys.argv)
        win = MyWindow()
        win.show()
        sys.exit(app.exec_())

<!-- end snippet -->

Marc
  • 29
  • 6
  • i'm not sure but when you click button then `PyQt` should send `event` with information which `widget` created this `event` (which `button` was click) and then you can use it to access `widget`. I'm not sure but maybe you need `def printbutton(self, event):` ? – furas Feb 08 '21 at 20:39
  • you could add code with all imports - it would be simpler to run it and test some ideas. – furas Feb 08 '21 at 20:44
  • 1
    `def printbutton(self): print(self.sender().text())`. Or use a [QButtonGroup](https://doc.qt.io/qt-5/qbuttongroup.html). – ekhumoro Feb 08 '21 at 20:46
  • Thank you for the self.sender(), it indeeds allows to retrieve the text from each button. Regarding the deletion buttons do you have any idea? Also for @furas, sorry I forgot to paste the imports, here they are: from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLineEdit import sys – Marc Feb 08 '21 at 21:59
  • next time put import (and other code, data and error message) in question, not in comment - it will be more readable and more people will see it. – furas Feb 08 '21 at 22:03
  • I did both, in comments, and edited my first message with it – Marc Feb 08 '21 at 22:05

1 Answers1

1

Using Google pyqt clicked event I found you have to use

def printbutton(self):
    widget = self.sender()
    print(widget.text())
    

ZetCode: Events and signals in PyQt5


EDIT:

As for erease button - you should get button from createButtonLabel and send it to eraseButtonLabel

    labelbutton = self.createButtonLabel(self.newLabelName)
    erasebutton = self.eraseButtonLabel(labelbutton)

and you can use lambda to assing function with argument

def eraseButtonLabel(self, labelbutton):
    # ... code ...
    self.buttonErase.clicked.connect(lambda: self.erasebutton(labelbutton))

and function should get this argument

def erasebutton(self, button):
    widget = self.sender()
    print('clicked:', widget.text())
    print('  erase:', button.text())

Or you can assing button to own variable in buttonErase

def eraseButtonLabel(self, labelbutton):
    # ... code ...
    self.buttonErase.assigned_button = labelbutton
    self.buttonErase.clicked.connect(self.erasebutton)

and use it in function

def erasebutton(self):
    widget = self.sender()
    print('clicked:', widget.text())
    print('  erase:', widget.assigned_button.text())

Full code which uses both methods at the same time but you need only one method.

from PyQt5.QtWidgets import *
import sys

class MyWindow(QMainWindow):
    def __init__(self):
        super(MyWindow, self).__init__()
        self.setGeometry(100, 100, 1500, 1500)
        self.setWindowTitle("My Program")
        self.labelButtons = []  # List of all the buttons displaying labels
        self.eraseButtons = []  # List of all the buttons displaying "X"
        self.Yposition = 50
        self.initUI()

    def initUI(self):
        self.labelEntry = QLineEdit(self)
        self.labelEntry.move(50, self.Yposition)
        self.labelEntry.resize(300, 40)

        self.addLabelButton = QPushButton(self)
        self.addLabelButton.setText("Add Label")
        self.addLabelButton.move(400, self.Yposition)
        self.addLabelButton.resize(300, 40)
        self.addLabelButton.clicked.connect(self.addNewLabel)

    def addNewLabel(self):
        self.Yposition += 50
        self.newLabelName = self.labelEntry.text()
        
        labelbutton = self.createButtonLabel(self.newLabelName)
        erasebutton = self.eraseButtonLabel(labelbutton)
        
        self.labelButtons.append(labelbutton)
        self.eraseButtons.append(erasebutton)
        self.updatelabels()

    def createButtonLabel(self, labelname):
        self.button = QPushButton(self)
        self.button.setText(str(labelname))
        self.button.resize(300, 40)
        self.button.move(50, self.Yposition)
        self.button.clicked.connect(self.printbutton)
        return self.button

    def eraseButtonLabel(self, labelbutton):
        self.buttonErase = QPushButton(self)
        self.buttonErase.setText("X")
        self.buttonErase.resize(40, 40)
        self.buttonErase.move(360, self.Yposition)
        self.buttonErase.assigned_button = labelbutton
        self.buttonErase.clicked.connect(lambda: self.erasebutton(labelbutton))
        #self.buttonErase.clicked.connect(self.erasebutton)
        return self.buttonErase    

    def updatelabels(self):
        for button in self.labelButtons:
            button.show()
        for button in self.eraseButtons:
            button.show()

    def printbutton(self):
        print('clicked:', self.sender().text())

    def erasebutton(self, button):
        widget = self.sender()
        print('clicked:', widget.text())
        print('  erase:', button.text())
        print('  erase:', widget.assigned_button.text())

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = MyWindow()
    win.show()
    sys.exit(app.exec_())

EDIT:

Other method is to create own widget which has both buttons labelbutton and erasebutton and then erasebutton has direct access only to own labelbutton.

BTW: and for similar reason I would keep buttons as pairs

 self.buttons.append([labelbutton, erasebutton])

instead of separted lists

 self.labelButtons.append(labelbutton)
 self.eraseButtons.append(erasebutton)

Example in which I create own widget.

from PyQt5.QtWidgets import *
import sys

class MyWidget(QWidget):
    def __init__(self, parent, labelname, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)

        self.resize(350, 40)
        
        self.labelButton = QPushButton(self)
        self.labelButton.setText(str(labelname))
        self.labelButton.resize(300, 40)
        self.labelButton.move(0, 0)
        self.labelButton.clicked.connect(self.printbutton)

        self.buttonErase = QPushButton(self)
        self.buttonErase.setText("X")
        self.buttonErase.resize(40, 40)
        self.buttonErase.move(310, 0)
        self.buttonErase.clicked.connect(self.erasebutton)

        self.show()

    def printbutton(self):
        print('clicked:', self.labelButton.text())

    def erasebutton(self):
        print('clicked:', self.buttonErase.text())
        print('  erase:', self.labelButton.text())


class MyWindow(QMainWindow):
    def __init__(self):
        super(MyWindow, self).__init__()
        self.setGeometry(100, 100, 1500, 1500)
        self.setWindowTitle("My Program")
        self.widgets = []
        self.Yposition = 50
        self.initUI()

    def initUI(self):
        self.labelEntry = QLineEdit(self)
        self.labelEntry.move(50, self.Yposition)
        self.labelEntry.resize(300, 40)

        self.addLabelButton = QPushButton(self)
        self.addLabelButton.setText("Add Label")
        self.addLabelButton.move(400, self.Yposition)
        self.addLabelButton.resize(300, 40)
        self.addLabelButton.clicked.connect(self.addNewLabel)

    def addNewLabel(self):
        self.Yposition += 50
        text = self.labelEntry.text()
                
        widget = MyWidget(self, text)
        widget.move(50, self.Yposition)
        
        self.widgets.append(widget)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = MyWindow()
    win.show()
    sys.exit(app.exec_())
furas
  • 134,197
  • 12
  • 106
  • 148
  • Thank you it indeed works fine to retrieve each button text value. Do you also have tips regarding the "deletion" buttons? – Marc Feb 08 '21 at 22:00
  • you should get `button` from `createButtonLabel` and send it to `eraseButtonLabel` - and it should connect click with extra argument - this `button` – furas Feb 08 '21 at 22:05
  • see new code in answer. – furas Feb 08 '21 at 22:16
  • Thank you very much, I'm looking at it. Very nice to keep buttons as pairs. I'm now trying to delete the pair when clicking on the X button. – Marc Feb 08 '21 at 22:25
  • I added example in which I create own widget with two buttons. – furas Feb 08 '21 at 22:37
  • Thank you but I get this error with your new code: Traceback (most recent call last): line 34, in addNewLabel self.updatelabels() line 57, in updatelabels button.show() AttributeError: 'NoneType' object has no attribute 'show' – Marc Feb 08 '21 at 22:55
  • I don't have `button.show()` nor `self.updatelabels()` in last code. – furas Feb 08 '21 at 22:58
  • I forgot `return self.buttonErase` in `def eraseButtonLabel()` – furas Feb 08 '21 at 23:01
  • Thank you. I can now also easily delete the bouton pair by calling deleteLater() in the erasebutton function. I now need to find a way to re-arrange the remaining bouton pair along the y axis, but that's a big step already, thank you very much. – Marc Feb 08 '21 at 23:41