0

It's in tkinter. The last line adds x = len(FRAME_LIST) "Buttons" into the dropdown menu. The problem is they all reference to the same frame (the last in the FRAME_LIST). How to I make it that every Button references a diffrent frame from FRAME_LIST?

    for F in FRAME_LIST:
        frame = ChallengePage(mainframe,self, F)
        self.frames[F] = frame
        frame.grid(row = 0, column = 0, sticky = "nsew")
        subMenu1.add_command(label = F.id, command = lambda: self.show_frames(F))

EDIT: So to be more precise, when I come across this problem i thought ok, the problem is I need to declare a local variable, so I tried this:

        A = F
        subMenu1.add_command(label = F.id, command = lambda: self.show_frames(A))

But it didnt work even though the A is declared INSIDE the loop, and redeclared in every loop, it still yields the same result.

I came now across the link: https://docs.python.org/3/faq/programming.html#why-do-lambdas-defined-in-a-loop-with-different-values-all-return-the-same-result

where it shows the solution:

        subMenu1.add_command(label = F.id, command = lambda A = F: self.show_frames(A))

Which somehow magically works, but I don't get why it is any diffrent from my local A.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Ryanless
  • 134
  • 1
  • 13
  • "but I don't get why it is any different from my local A" For the reason carefully explained on the same web page where that solution came from. – Karl Knechtel Aug 19 '22 at 13:27

1 Answers1

0

This is a common issue when using lambda in loops. I usually use a partial instead:

from functools import partial

for F in FRAME_LIST:
    frame = ChallengePage(mainframe, self, F)
    self.frames[F] = frame
    frame.grid(row=0, column=0, sticky="nsew")
    subMenu1.add_command(label=F.id, command=partial(self.show_frames, F))

Because: closures.

To summarize: the lambda in your loop will remember the name of the variable (F), but not the value it points to on each iteration of the loop. By the time you are clicking the buttons and triggering the command, the name F points to the last value in the iteration of your for loop, and so that is what will be passed to your lambda. Partials on the other hand are aware of the object that F points to at each iteration, and keeps track of it so that self.show_frames() is called with the appropriate argument.

Community
  • 1
  • 1
elethan
  • 16,408
  • 8
  • 64
  • 87