Python 3和Tkinter:如何根据按钮选择运行条件块

huangapple go评论43阅读模式
英文:

Python 3 & Tkinter: How to run conditional block based on choice between buttons

问题

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_or_pass == 'call':
    # code that does stuff
elif call_or_pass == 'pass':
    # other code that does stuff
# Then lots more code after this...

I am struggling to translate this code to a 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 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.

英文:

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.

答案1

得分: 0

不确定这是否是您正在寻找的内容。

这使您可以配置2个按钮的命令,以带您进入新的功能。只要每个功能都带您进入另一个功能,就可以无限延续。

这的主要部分是button_choicerebind_buttons方法,通过将要调用的方法作为参数传递给lambda函数,您可以变化代码下一步采取的操作。我添加了**kwargs作为如何传递不同数量的数据的示例。

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("重新绑定按钮以运行新命令")
        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): ## 游戏示例
        if choice == "call":
            self.prompt_text.configure(text="用户打电话了")
            self.rebind_buttons(self.q3) ##这里设置下一个函数的操作
        elif choice == "pass":
            self.prompt_text.configure(text="用户放弃了")
            self.rebind_buttons(self.q3, item="hat") ##这里设置下一个函数的操作,它接受**kwargs,以便传递需要的数据。

    def q3(self, choice, **kwargs): ## 另一个游戏示例。这次kwargs被发送,以便您可以基于它做决定。
        if choice == "call":
            if kwargs.pop('item', None) is None:
                self.prompt_text.configure(text="用户打电话了,但没有帽子")
            else:
                self.prompt_text.configure(text="用户打电话了,他戴上了帽子")
            #self.rebind_buttons(self.q3) # 这应该是下一部分
        elif choice == "pass":
            if kwargs.pop('item', None) is None:
                self.prompt_text.configure(text="用户放弃了,失去了他的帽子")
            else:
                self.prompt_text.configure(text="用户放弃了,没有帽子,他必须回去")
            self.rebind_buttons(self.q2)

    def main(self):
        self.prompt_text = Label(self.root, text="")
        self.prompt_text.pack(side=TOP)
        ##创建按钮
        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() ##创建第一个选择

        self.root.mainloop()

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

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()

huangapple
  • 本文由 发表于 2023年6月9日 00:14:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/76433862.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定