0

I am converting a text based card game, written in Python 3, to a UI via tkinter. There are many instances where the player is prompted to make a choice. Getting this choice in text format is easy, however I am stuck trying to get this choice using buttons.

In the text based game I can easily get the player's choice through a variable set equal to input and then run resulting lines of code.

call_or_pass = user_input('What do you choose?')
if call:
    code that does stuff
elif pass:
    other code that does stuff
Then lots more code after this...

I am struggling to translate this code to tkinter UI. Keep in mind choice prompts and the buttons used to make the choice must appear in specific instances. My program is built within a class. A variable to track player choices, self.user_choice, is defined in the init and set to None by default. The buttons are created above .mainloop() within the UI code.

#buttons
self.call_button = tk.Button(text='Call', font=self.medium, command=lambda: self.button_choice('call'))
self.pass_button = tk.Button(text='Pass', font=self.medium, command=lambda: self.button_choice('pass'))

The buttons are bound to a function called button_choice. Each button passes an argument to this function that updates the self.user_choice variable.

def button_choice(self, choice):
    if choice == 'call':
        self.user_choice = 'call'
    elif choice == 'pass':
        self.user_choice = 'pass'

When a choice prompt occurs, the buttons are drawn to the UI with a place_buttons function. But, I am calling place_buttons in the specific instance with the function select_button. Once the update to self.user_choice is made, the program should run one of two conditional block options within select_button. For debugging purposes, I am trying to change some text at the top of a window.

#draws the button to the UI when called
def place_buttons(self):
    self.call_button.place(x=self.window_width - (self.window_width * .58), y=self.window_height - (self.window_height * .38))
    self.pass_button.place(x=self.window_width - (self.window_width * .48), y=self.window_height - (self.window_height * .38))

#function for a specific choice prompt instance
def select_button(self):
    self.place_buttons()

    #in the text based game, the call_or_pass = input() was here followed by the below if/elif block

    if self.user_choice == 'call':
        self.prompt_text.config(text='You selected call')
    elif self.user_choice == 'pass':
        self.prompt_text.config(text='You selected pass')

However, I can't figure out setting the path of the conditional block in select_button equal to the result of the clicked button.

  • I know that the if/elif code in select_button doesn't work because no button has been clicked, thus self.user_choice is equal to None.
  • I tried writing a while loop above the block in select_button but that creates an infinite loop.
  • I know that I need to return the value of the if/elif in button_choice to select_button, but given button_choice is bound to the buttons, I can't call it from select_button.
  • I know that I could update the text within the button_choice, but the broader goal is to run many lines of code which is better suited for the separate select_button function.

Any help would be greatly appreciated. I can provide additional context or code as requested. The full debug program is 67 lines. I tried to keep the code snippets above as succinct as possible while giving enough context.

Thanks.

JohnG
  • 3
  • 3
  • For choices you have `Checkbuttons` and `Radiobuttons` then you iterate over their variables of the widget itself and guided by the checkmarks your workflow should lead you to any goal that you want. – Thingamabobs Jun 08 '23 at 16:19

1 Answers1

0

Not sure if this is what you are looking for.

This lets you configure the command of the 2 buttons to take you to a new function. as long as each function takes you to another one it can be endless.

The main part of this is the button_choice and rebind_buttons methods by passing the method you want to call next as an argument into the lambda function you can vary what actions your code takes next. I added **kwargs as example on how you can pass varying amounts of data through as well.


from tkinter import *

class myGame:
    def __init__(self):
        self.root = Tk()
        self.callbutton = None
        self.passbutton = None
        self.prompt_text = None

    def button_choice(self, choice, callback, **kwargs):
        callback(choice, **kwargs)
    def rebind_buttons(self, command, **kwargs):
        print("rebinding buttons to run new command")
        self.callbutton.configure(command=lambda c=self.callbutton.cget('text').lower(), f=command: self.button_choice(c, f, **kwargs))
        self.passbutton.configure(command=lambda c=self.passbutton.cget('text').lower(), f=command: self.button_choice(c, f, **kwargs))

    def starter(self):
        self.rebind_buttons(self.q2)

    def q2(self, choice, **kwargs): ## just an example of a game
        if choice == "call":
            self.prompt_text.configure(text="The user called")
            self.rebind_buttons(self.q3) ##this is where you set the action to the next function
        elif choice == "pass":
            self.prompt_text.configure(text="The user passed")
            self.rebind_buttons(self.q3, item="hat") ##this is where you set the action to the next function, it accepts **kwargs so data can be passed if needed.

    def q3(self, choice, **kwargs): ## another example of a game. this time kwargs are being sent so you can make decisions based on that.
        if choice == "call":
            if kwargs.pop('item', None) is None:
                self.prompt_text.configure(text="The user called but doesnt have a hat")
            else:
                self.prompt_text.configure(text="The user called he put on his hat")
            #self.rebind_buttons(self.q3) # this should be the next part
        elif choice == "pass":
            if kwargs.pop('item', None) is None:
                self.prompt_text.configure(text="The user passed and lost his hat")
            else:
                self.prompt_text.configure(text="The user passed, without a hat he has to go back")
            self.rebind_buttons(self.q2)

    def main(self):
        self.prompt_text = Label(self.root, text="")
        self.prompt_text.pack(side=TOP)
        ##creates the buttons
        self.callbutton = Button(self.root, text="Call", command=lambda c='call', f=None:self.button_choice(c, f))
        self.passbutton = Button(self.root, text="Pass", command=lambda c='pass', f=None:self.button_choice(c, f))
        self.callbutton.pack(side=LEFT)
        self.passbutton.pack(side=RIGHT)
        self.starter() ##creates the first choice

        self.root.mainloop()

if __name__ == "__main__":
    game = myGame()
    game.main()