【问题标题】:Python Text-Based Game with Tkinter使用 Tkinter 的基于 Python 文本的游戏
【发布时间】:2018-04-29 19:24:28
【问题描述】:

我昨天参加了一场当地的编程比赛,参加了一个由 4 人组成的团队,成员的技能水平各不相同。我们试图制作一个基于文本的 Zork 风格的游戏,但由于 UI 是我创建的 GUI 类别之一,但我没有使用 Python 的经验。我使用了 Tkinter,并且大部分都在 GUI 内部工作,但不知道如何让 GUI 与其余代码交互。

我只是在这里包含相关的部分,部分原因是其余的代码非常混乱,没有 cmets 和很多语法错误,但如果需要,我可以发布它。代码如下:

def pinput():
    global e
    player_input = e.get()  
    print(player_input)

def progress():
    hallway_1()

def hallway_1():
    global player_input
    global prin
    global e
    prin.set("You are in a hallway. Yellow lockers line the sides of all walls and you see three doors. One leads into a Library, one to a History classroom, and one to a Math room. You can 1.) Search the hallway or 2.) Procede to one of the rooms.")
    while player_input != "1" and player_input != "2": #Best guess at a way to get the code to wait for input, actually just causes code to freeze
        pass
    if player_input == "1": #Even if 1 is in the entry box beforehand, it never gets here
        prin.set("You search through the lockers and find 1 health potion.\n Would you like to add it to your inventory? 1.) Yes 2.) No")
        player_input = "999"

def player_attack():
    global enemy_hp
    enemy_hp -= player_strength
    prin.set("You just attacked the " + enemy_name + "!")

def player_block():
    global block
    prin.set("You predict that your enemy is going to attack and decide to block")
    block = 1

def player_use():
    global player_item_use
    global item_name
    player_item_use = 100
    prin.set("You decide to use one of your items. What do you use?")
    while player_turn_input != 0 or 1 or 2 or 3 or 4 or 5 or 6 or 7 or 8 or 9 or 10 or 11 or 12 or 13 or 14 or 15 or 16 or 17 or 18 or 19 or 20 or 21 or 22 or 23 or 24 or 25 or 26:
        player_item_use = e.get()
        time.sleep(1)
    item_name = Inventory[player_item_use]
    item(item_name)

def run_away():
    global player_hp
    global lost_item_1
    global lost_item_2
    prin.set("You decide to run away from the " + enemy_name + "!\nThis causes you to lose 20 health points and two inventory items.")
    player_hp -= 20
    if backpack == True:
        lost_item_1 = random.randint(1,27)
        lost_item_2 = random.randint(1,27)
    else:
        lost_item_1 = random.randint(1,9)
        lost_item_2 = random.randint(1,9)
    RemoveInventory(lost_item_1)
    RemoveInventory(lost_item_2)
    library()

top = tk.Tk()
prin = tk.StringVar()
#path = "smalllibrary.png"
#tkimage = ImageTk.PhotoImage(Image.open(path))
#displayimage = tk.Label(top, image=tkimage).grid(row=0) 
inv = tk.Text(top, width="24")
inv.grid(row=0, column=1)
for x in Inventory:
    inv.insert("end", str(x) + str(invnum) + '\n')
    invnum += 1
healthbar = tk.Label(top, text=("Health: " + str(player_hp))).grid(row=1, column=1)
text = tk.Label(top, bg="black", fg="white", textvariable=prin, justify="left", cursor="box_spiral").grid(row=1, sticky="w")
e = tk.Entry(top)
e.grid(row=2)
e.focus_set()
Attack = tk.Button(top, text ="Attack", activebackground="red", width="10", command = player_attack).grid(row=4, sticky="e")
Block = tk.Button(top, text ="Block", activebackground="red", width="10", command = player_block).grid(row=5, sticky="e")
Use = tk.Button(top, text ="Use Item", activebackground="red", width="10", command = player_use).grid(row=4, column=1, sticky="w")
Run = tk.Button(top, text ="Run", activebackground="red", width="10", command = run_away).grid(row=5, column=1, sticky="w")
Enter = tk.Button(top,text='Enter',command=pinput).grid(row=3)
Progress = tk.Button(top, text="Progress", activebackground="green", 
prin.set("REDACTED")#This had my team's full names

top.mainloop()

所以,有一些问题。出于测试目的,我添加了“进度”按钮,理想情况下游戏将从走廊 1 开始。但是,我无法弄清楚将 hallway_1() 函数放在哪里才能使其工作。在 top.mainloop() 之前,GUI 不会打开。之后,e.get() 抛出了 TCL 错误。

所以,我将它绑定到一个按钮。但是,现在即使在代码运行之前将 1 输入到输入框中,代码也会一直卡在“while”循环中。我对此感到困惑,因为 Visual Studio 在“自动”框中将 player_input 报告为“1”,但它仍然在循环中运行。

【问题讨论】:

  • 您不能在 GUI 事件处理程序中执行像这样的 while 循环。在您从处理程序函数返回之前,GUI 不会更新其显示、接受用户输入或执行任何其他操作,这意味着一旦您到达那里,player_input 将永远不会改变。处理程序函数需要做一件事,设置未来处理程序所需的任何内容,然后立即返回。
  • 好的,我明白了,但我仍然不明白为什么即使 player_input 等于 '1' 也会运行循环
  • 啊,这又是一个问题——你只是在pinput 中忘记了global player_input,所以你只是创建了一个同名的局部变量。全局变量不是 "1"。但是,鉴于您在这里的代码,这应该会引发NameError
  • 啊,好的,谢谢!!

标签: python tkinter


【解决方案1】:

您不能像在 GUI 事件处理程序中那样执行 while 循环。在您从处理函数返回之前,GUI 不会更新其显示、接受用户输入或做任何其他事情,这意味着一旦您到达那里, player_input 将永远不会改变。处理程序函数需要做一件事,设置未来处理程序所需的任何内容,然后立即返回。

在您第一次编写 GUI 时,您很难开始思考事件循环和处理程序回调,但这绝对是必不可少的;除非你这样做,否则事情看起来很奇怪和武断。

因此,答案不是“等待输入”,而是设置一个在输入准备好时触发的处理程序。 (您可以改为在 after 函数上设置一个处理程序,使其每 N 毫秒触发一次,而不是在输入时触发,但这通常只会让事情变得更复杂。)

执行此操作的一种方法是让“进度”按钮触发“下一步”,并跟踪一些状态,让您确定“下一步”是什么。事实上,你已经拥有那个状态:​​玩家是否已经给出了正确的输入。所以:

def pinput():
    global player_input
    player_input = e.get()  
    print(player_input)

def progress():
    pinput()
    if player_input != "1" and player_input != "2":
        hallway_1_enter()
    else:
        hallway_1_doit()

def hallway_1_enter():
    global player_input
    global prin
    global e
    prin.set("You are in a hallway. Yellow lockers line the sides of all walls and you see three doors. One leads into a Library, one to a History classroom, and one to a Math room. You can 1.) Search the hallway or 2.) Procede to one of the rooms.")

def hallway_1_doit():
    if player_input == "1": #Even if 1 is in the entry box beforehand, it never gets here
        # etc.

这显然是一个笨拙的设计,但它是对您现有设计的最小更改,可以帮助您迈出第一步。一旦你掌握了窍门,你应该能够从那里改进它。

还要注意我输入的global player_input pinput。您现有的函数没有那个,所以它只是创建一个同名的局部变量,然后立即消失;全局永远不会改变。任何想要分配给全局的函数都需要global 语句。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-13
    • 1970-01-01
    • 1970-01-01
    • 2021-06-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多