0

Making PushButton widgets for a program. The intent was to create each PushButton, connect it to a function which compared two string values, ap.parse_answer(), then add the PushButton to the appropriate cell of a QGridLayout:

answers = ["this", "that", "the other", "one more"]
correct_answer = "this"

for grid_pos in [(i,j) for i in range(0,2) for j in range(0,2)]:
        answer_disp = AnswerDisplay()
        current_answer = answers.pop()
        answer_disp.setText(current_answer)
        answer_disp.clicked.connect(
            lambda: self.ap.parse_answer(current_answer, answer))
        answer_grid.addWidget(answer_disp, *grid_pos)

Here is the AnswerDisplay class:

class AnswerDisplay(QtGui.QPushButton):
    def __init__(self):
        super(AnswerDisplay, self).__init__()
        answer_font = QtGui.QFont()
        answer_font.setWeight(24)
        answer_font.setPixelSize(20)
        self.setFont(answer_font)

Unfortunately, what happens is the same function gets connected to each button. The last function generated end up on all the buttons, so it seems the connect is being reapplied to previous created buttons. But how do I address this? My approach can't be completely invalid because the setText() function correctly sets the text for each button without overwriting the previous assignments.

I tried to address the issue making an single AnswerDisplay and then copying it with deepcopy():

for grid_pos in [(i,j) for i in range(0,2) for j in range(0,2)]:
    disp = AnswerDisplay()
    answer_disp = deepcopy(disp)
    super(AnswerDisplay, answer_disp).__init__()
    ...

but it produced the same undesired result.

I've done some searching, but all I've found are questions from people trying to get the kind of result I'm trying not to get. Any help would be appreciated.

Jeffrey Swan
  • 537
  • 2
  • 8
  • 26
  • 1
    possible duplicate of [PyQt connect inside for loop vs. separate calls results in different behavior](http://stackoverflow.com/questions/27208706/pyqt-connect-inside-for-loop-vs-separate-calls-results-in-different-behavior) – three_pineapples Jan 15 '15 at 02:50

1 Answers1

1

Your issue is that you're not capturing the values in the lambda function. Because of the way Python's scoping rules work, you're using the same value (the last one) each time.

Change the lambda line to this to capture the variables you want:

answer_disp.clicked.connect(
    lambda ca=current_answer, a=answer: self.ap.parse_answer(ca, a))

There are other related questions/answers that may give you more of an explanation about this (like here)

Community
  • 1
  • 1
Gerrat
  • 28,863
  • 9
  • 73
  • 101
  • Very interesting, and I think this explains some other trouble I've had with lambda expressions as well. In this case, I'm not sure. Every push button is now passing `False` as the value of `current_answer` to `parse_answer()`. Button names are still being set correctly, so a variable known to contain a string is being fed into the lambda, but if I add a `print()` statement to `parse_answers()` to display the arguments passed to it, the lambda seems to be making the assignment `ca=False`. – Jeffrey Swan Jan 14 '15 at 21:35
  • @Jefe: I'm not sure what your exact issue is, but in the example you posted, the variable: `answer` isn't being assigned anywhere, so that may be part of it. Maybe your actual code isn't what you posted here? – Gerrat Jan 14 '15 at 21:40
  • I'm sure it has everything to do with a lack of comprehension about lambda functions on my part, but it's an odd issue. Which ever assignment made first in the lambda expression gets set to False instead. So, `lambda ca=current_answer, a=answer: self.ap.parse_answer(ca, a)` passes (False, "answer string") and `lambda a=answer, ca=current_answer: self.ap.parse_answer(ca, a)` passes ("current answer string", False) to parse_answer(). – Jeffrey Swan Jan 14 '15 at 21:52
  • @Jefe If you are still having problems, posting a minimilistic working example that demonstrates the odd behaviour may help others debug the problem. – three_pineapples Jan 15 '15 at 02:54