Текстовая игра на Python с Tkinter - PullRequest
0 голосов
/ 29 апреля 2018

Вчера я участвовал в локальном соревновании по программированию, где я попал в команду из четырех человек с участниками разного уровня квалификации. Мы пытались создать текстовую игру в стиле Zork, но, поскольку пользовательский интерфейс был одной из категорий, я был вынужден создавать графический интерфейс, с которым у меня нет опыта работы с Python. Я использовал Tkinter и получил все, что в основном работает внутри самого GUI, но не могу понять, как заставить GUI взаимодействовать с остальным кодом.

Я просто включил здесь соответствующие биты, отчасти потому, что остальная часть кода довольно беспорядочная, без комментариев и множества синтаксических ошибок, но я могу опубликовать ее при необходимости. Вот код:

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 () и графический интерфейс не открывался. После этого e.get () скинул TCL ошибки.

Итак, я привязал его к кнопке. Однако теперь код продолжает застрять в цикле while, даже если 1 вводится в поле ввода перед выполнением кода. Я озадачен этим, потому что Visual Studio сообщает player_input как «1» в поле «Autos», но в любом случае он продолжает проходить через цикл.

1 Ответ

0 голосов
/ 29 апреля 2018

Вы не можете сделать цикл 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...