Python Forum
Function assigned at a button in tkinter - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: General Coding Help (https://python-forum.io/forum-8.html)
+--- Thread: Function assigned at a button in tkinter (/thread-21580.html)



Function assigned at a button in tkinter - riccardoob - Oct-05-2019

for y in range(0, pnumplayers):#third list of hands_inputs filled with fold button
    self.hands_inputs[2].append(Button(self.window, text='Fold', command = lambda: pplayers[y].fold(self.hands_inputs[0:3])))
In the above extract of my code I'm trying to fill a list of length
pnumplayer 
with tkinter button to then show them in window i previously created. The problem I encountered is that when I assign the function that's called when the button is pressed i want to assign different function to every button using this instruction:
pplayers[y].fold(self.hands_inputs[0:3])
where
pplayers 
is an array of object of type player I prevously created and at each button I want to assing the function fold for each player, however when I click on a given button it execute the same function though I checked and the string id of the commands are all different. Someone know how to solve this issue? Thanks in advance.


RE: Function assigned at a button in tkinter - stullis - Oct-05-2019

Hmm... Are you printing the id of the method or of the button? I imagine the mapping to the button is likely causing the change in id since each button needs to be identified separately.


RE: Function assigned at a button in tkinter - riccardoob - Oct-05-2019

Thanks for answering, by string id of the command I mean this output
print(self.hands_inputs[2][0:pnumplayer])
, and as I said in the post the ids are all different but the all link to the same position in the array pplayers (the one filled with player objects)


RE: Function assigned at a button in tkinter - stullis - Oct-05-2019

Okay. If I understand you correctly, the button command is performing the same operation on the same player. In that case, what is self.hands_inputs? It's evidently a list but you're assigning the button as an item in it and using that same index as part of the argument to player.fold(). That suggests to me that player.fold() is operating on the same object in all cases.


RE: Function assigned at a button in tkinter - riccardoob - Oct-05-2019

The list self.hands_inputs holds the buttons and pplayers is another list but that one holds player object, the player class has a method called fold, I'm trying to assign to
self.hands_inputs[2][0]
a button with a command that calls the fold function of the zeroth player. To do so I created the list pplayers (it's a parameter but it doesn't matter) that holds in each place a player object. In the for statement y ranges from 0 to let's say 5, so it should assing at each iteration
self.handsinputs[2][y]
a button with the command
pplayers.[y].fold(parameters)



RE: Function assigned at a button in tkinter - stullis - Oct-05-2019

I get that, what isn't making sense is why self.hands_inputs the argument to player.fold() and also the list containing the resulting button. That method Shouldn't need all that information, especially since that information includes all the buttons for the previous players.

On a side note, range(pnumplayers) is superfluous. This would work easier by iterating over pplayers instead.

for y in pplayers:
    self.hands_inputs[2].append(Button(self.window, text='Fold', command = lambda: y.fold(self.hands_inputs[0:3])))



RE: Function assigned at a button in tkinter - riccardoob - Oct-05-2019

Thanks for the side note. self.hands_inputs is the argument of player.fold() because fold is the function that's supposed to delete from the tkinter window (usinf the command widget.grid_forget()) some widget: the list phands_inputs is a list with 3 items, the first and the second one are filled with the names of the two cards in the hand of the player, the third item is filled with a list filled with button, so I pass the first 3 items of self.hands_inputs to then use them in the fold function inside a for statement to delete the widget from the window. More clearly, in the window I have let's say 5 rows: each one has two labels with the names of the two cards and a fold button, when I hit the fold button the line should disappear.

EDIT!!!!!:
I just noticed that using the for statement like I show in the code above the command tied to every button is alway referring to the last player (in the case I'm testing now the player with index 4), but if I do not use the for statement and do it the 'dummy' way, like that to be clear:
        
    self.hands_inputs[2].append(Button(self.window, text='Fold', command = lambda: pplayers[0].fold(self.hands_inputs[0:3])))
    self.hands_inputs[2].append(Button(self.window, text='Fold', command = lambda: pplayers[1].fold(self.hands_inputs[0:3])))
    self.hands_inputs[2].append(Button(self.window, text='Fold', command = lambda: pplayers[2].fold(self.hands_inputs[0:3])))
    self.hands_inputs[2].append(Button(self.window, text='Fold', command = lambda: pplayers[3].fold(self.hands_inputs[0:3])))
    self.hands_inputs[2].append(Button(self.window, text='Fold', command = lambda: pplayers[4].fold(self.hands_inputs[0:3])))
it works perfectly, but I need to do it with a for statement because the number of player is variable.


RE: Function assigned at a button in tkinter - stullis - Oct-05-2019

I'm glad you got that working-ish.

There's a problem with responsibilities here. When the fold button is pressed, the row with the player's cards and the button needs to be removed from the presentation. So, the GUI needs to know what to present. However, the player does not need to know what's presented; the player only needs to know that it folded.

This is the Single Responsibility Principle. As Uncle Bob puts it: "A class should have one and only one reason to change." The GUI should change when a change needs to be made to the GUI but the Player should remain unchanged in that case.

If we separate the front-end presentation and the back-end data flow, we can likely clear up the problem.

class Player:
    self.folded = False
    
    def fold(self):
        self.folded = True

class Presentation:
    def player_folds(self, index, player): # index is for removing the correct row of data
        player.fold()
        ...hide or forget the presented data...
When a new hand is dealt, the Presentation will need new information from the players. I recommend the players contain and manage their information and have a method for providing that to a caller.


RE: Function assigned at a button in tkinter - riccardoob - Oct-05-2019

Thanks for the answer, I kinda understood what I have to do. As soon as I find some time to test the solution and make it work I'll flag the discussion as solved.


RE: Function assigned at a button in tkinter - riccardoob - Oct-06-2019

So I tried to create a separate class to hold all the front-end presentation but the problem is still the same: when I tie the function player_folds to the buttons I have to pass them a different index and a different player object and the button are still all tued to the function that should be tied at the last one.