0

Attempts to pass a unique string to a QPushButton clicked slot fails.

Using pyqt5, I am iterating over a list of string parameters to create a UI form laid out as: QLabel QLineEdit QPushButton -- one line for each parameter in the list.

The QPushButton's click signal is connected to a function that opens a QFileDialog allowing user to select a file or directory that is of a specific type for the parameter. The parameter is passed along:

 btn.clicked.connect(lamda: self.openFileDialog(param)
  1. I've tried creating/passing a unique copy of the 'param' string btn.clicked.connect(lamda: self.openFileDialog(copy.copy(param))

  2. I've tried using a string variable outside of the scope of the for loop i.e. p = None for idx, param in enumerate(general_params_list): p = param btn.clicked.connect(lamda: self.openFileDialog(p))

  3. Again playing w/ scope -- used a global variable instead: self.p

  4. I tried creating and storing a list of unique buttons (rather than re-using a single variable instance). I created the list outside of the for loop and initialized it w/in the loop.

  5. Finally augmented (4) by creating/storing/using a list of copied 'param' strings.

r = 0
ig1Layout = QGridLayout()

for idx, param in enumerate(general_params_list):
    paramLabel = QLabel(param)
    textLine = QLineEdit()
    btn = QPushButton("..")

    btn.clicked.connect(lambda: self.openFileDialog(param))

    ig1Layout.addWidget(paramLabel, r, 0)
    ig1Layout.addWidget(textLine, r, 1)
    ig1Layout.addWidget(btn, r, 2)

    r += 1


def openFileDialog(self, btnType):
    print("\nopenFileDialog() - clicked on '..' open dialog for: ", btnType)

Expected result is that each function slot is passed a unique parameter string so that I may distinguish which button was pressed.

Actual result is that the very last parameter string in the original list is set (passed) to all the buttons. i.e. I'm passing/setting/using a reference to 'param' rather than its value??

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • change `btn.clicked.connect(lambda: self.openFileDialog(param))` to `btn.clicked.connect(lambda checked, param=param: self.openFileDialog(param))` – eyllanesc Apr 25 '19 at 19:10

1 Answers1

0

Try it:

import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *


class Window(QWidget):
    def __init__(self,parent=None):
        super(Window,self).__init__(parent)

        r = 0
        general_params_list = ("param 1", "param 2", "param 3")
        self.textLine_list = []

        ig1Layout = QGridLayout(self)

        for idx, param in enumerate(general_params_list):
            paramLabel = QLabel(param)
            self.textLine = QLineEdit() 
            self.textLine_list.append(self.textLine.text())

            self.btn = QPushButton("btn{}".format(idx))

            self.textLine.textChanged.connect(
                lambda text, idx=idx : self.on_text_changed(text, idx))  
            self.btn.clicked.connect(
                lambda state, param=param, i=idx : self.openFileDialog(param, i))                

            ig1Layout.addWidget(paramLabel,      r, 0)
            ig1Layout.addWidget(self.textLine,   r, 1)
            ig1Layout.addWidget(self.btn,        r, 2)
            r += 1

    def openFileDialog(self, btnType, i):
        print("\nopenFileDialog() - clicked on '..' open dialog for: ", btnType)
        print("{} -> {}".format(btnType, self.textLine_list[i]))

    def on_text_changed(self, text, idx):
        self.textLine_list[idx] = text


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

enter image description here

S. Nick
  • 12,879
  • 8
  • 25
  • 33