Search code examples
pythontkinter

Having trouble calling a variable in Python (specifically Tkinter)


I am trying to create a small hangman game in tkinter and unfortunately my 'chances' variable is not playing nice and is somewhat preventing me from continuing. I was wondering if anyone could take a look at the code and see what's going on and how to fix it! (getting an error saying it's undefined however I've tried several attempts to fix it without success)

from tkinter import *
from tkinter import messagebox
import random

#Defines the main application size, title, and resizeability with name 'window'
window = Tk()
window.geometry('450x250')
window.resizable(False, False)
window.title("Hangman")
window.canvas = Canvas(window, width=450, height=250)
window.canvas.pack()

def gameEasy(*args):
    hangman = Tk()
    hangman.geometry('450x200')
    hangman.resizable(False, False)
    hangman.title("Hangman")
    hangman.canvas = Canvas(hangman, width=450, height=200)
    hangman.canvas.pack()

    hangman.canvas.create_line(50, 120, 70, 120)
    hangman.canvas.create_line(59, 120, 59, 80)
    hangman.canvas.create_line(59, 80, 39, 80)
    hangman.canvas.create_line(39, 80, 39, 85)

    wordlistEasy = '''cat dog apple stars movie hammer sprint farmer'''
    wordlistEasy = wordlistEasy.split(' ')
    word = random.choice(wordlistEasy)

    letterguessed = []
    chances = len(word) + 2
    correct = 0
    flag = 0
    X1 = 75
    Y1 = 120
    X2 = 85
    Y2 = 120

    for i in word:
        X1 += 12
        X2 += 12
        hangman.canvas.create_line(X1, Y1, X2, Y2, dash=(8,1))
    
    def main_loop(*args):
        Guess = str(GuessInput.get())
        global chances
        incorrectLabel.config(text="")

        while chances != 0 and flag == 0:
            chances -= 1

            if not Guess.isalpha() and Guess != "":
                incorrectLabel.config(text="Please input a LETTER!")
                chances += 1
                break
            elif len(Guess) > 1 and (Guess != word):
                incorrectLabel.config(text="Please input only one letter!")
                chances += 1
                break
            elif (Guess in letterguessed) and Guess != "":
                incorrectLabel.config(text="Letter already guessed!")
                chances += 1
                break
            else:
                letterguessed.append(Guess)
                letterList.config(text=(letterguessed))
                chancesLabel.config(text=f"Chances left: {chances}")
                chances -= 1
                break

        GuessInput.delete(0,"end") #Resets the entry box after each input

    GuessInput = StringVar()
    GuessInput = Entry(hangman, width=8, bd="1")
    GuessInput.place(x=15, y=31)
    GuessInput.focus_force()
    Guessbtn = Button(hangman, text="Guess", width=7, bd="1", command=main_loop).place(x=75, y=28)
    hangman.bind('<Return>', main_loop)

    lettersLabel = Label(hangman, text="Guessed letters:").place(x=220, y=5)
    letterList = Label(hangman, text="")
    letterList.place(x=220, y=20)
    chancesLabel = Label(hangman, text=f"Chances left: {chances}")
    chancesLabel.place(x=12, y=5)
    incorrectLabel = Label(hangman, text="", fg="red")
    incorrectLabel.place(x=12, y=52)

    hangman.mainloop()

def exit(*args):
    messagebox.showinfo("Thank You", "Thanks for playing!")
    window.destroy()

mainlabel = Label(window, text="Welcome to Hangman", font=("Arial Bold",14)).place(x=120, y=5)
introMessage = Message(window, text="This is a simple hangman program using Tkinter. To play, simply select a difficulty, to leave, select quit!", width=280)
introMessage.place(x=80, y=30)

difficultyLabel = Label(window, text="Select a difficulty", font=("Arial Bold",9)).place(x=170, y=70)
easybtn = Button(window, text = "Easy", width=10, bd="1", command=gameEasy).place(x=182, y=95) #Defining a button with properties
hardbtn = Button(window, text = "Hard", width=10, bd="1").place(x=182, y=120) #Defining a button with properties
window.canvas.create_line(175,150,265,150)
rulesbtn = Button(window, text = "Rules", width=10, bd="1").place(x=182, y=155) #Defining a button with properties
quitbtn = Button(window, text = "Quit", width=10, bd="1", command=exit).place(x=182, y=180) #Defining a button with properties

window.mainloop()

Tried making chances global, tried swapping the ordering of the function call several different ways etc.


Solution

  • chances is a local variable inside function gameEasy(), so you should not declare it as global variable inside the nested function main_loop(). You should declare it as nonlocal variable instead.

    Also avoid creating multiple instances of Tk(). Use Toplevel() for child windows.

    def gameEasy(*args):
        hangman = Toplevel()  # use Toplevel() instead of Tk() for child window
        ...
        def main_loop(*args):
            ...
            nonlocal chances    # declare chances as nonlocal variable
            ...
        ...