0

I am currently working on a hangman game using tkinter in python.

When I click a button of the letter and it is in the word that we are guessing it should show the letter. But when I click the button this problem is popping up:

This example is only with one button. People say that this problem is because of the mainloop(), but i have no idea how to fix it.

from tkinter import *
from tkinter import messagebox
from generate_word import word

#DEFAULT VALUES
score = 0
count = 0
win_count = 2
WINDOW_BG = '#e5404e'
WINDOW_SIZE = '1200x870+300+80'
FONT = ('Arial', 40)
    from tkinter import *
from tkinter import messagebox
from generate_word import word

#DEFAULT VALUES
score = 0
count = 0
win_count = 2
WINDOW_BG = '#e5404e'
WINDOW_SIZE = '1200x870+300+80'
FONT = ('Arial', 40)
#this is an example with only one button
buttons = [['b1','a',80,740]]


#Creating window and configurating it
window = Tk()
window.geometry(WINDOW_SIZE)
window.title('Hangman')
window.config(bg = WINDOW_BG)

#generates all of the labels for the word
def gen_labels_word():
    label = Label(window, text = " ", bg = WINDOW_BG, font = FONT)
    label.pack( padx = 40,pady = (500,100),side = LEFT)

    label1 = Label(window, text = word[0], bg = WINDOW_BG, font = FONT)
    label1.pack( padx = 41,pady = (500,100),side = LEFT)

    x = 21
    for var in range(1,len(word)): 
        exec('label{}=Label(window,text="_",bg=WINDOW_BG,font=FONT)'.format(var))
        exec('label{}.pack(padx = {}, pady = (500,100), side=LEFT)'.format(var,x))
        x += 1

    exec('label{} = Label(window, text = "{}", bg = WINDOW_BG, font = FONT)'.format(len(word),word[-1]))
    exec('label{}.pack( padx = {},pady = (500,100),side = LEFT)'.format(len(word), x+1))              



# ---------------------------------------------------------------------------------------------------------------------------------------------------

gen_labels_word()

#----------------------------------------------------------------------------------------------------------------------------------------------------
#letters icons(images)       


#hangman (images)
hangman = ['h0','h1','h2','h3','h4','h5','h6']
for var in hangman:
    exec(f'{var}=PhotoImage(file="{var}.png")')

han = [['label0','h0'],['label1','h1'],['label2','h2'],['label3','h3'],['label4','h4'],['label5','h5'],['label6','h6']]
for p1 in han:
    exec('{}=Label(window, bg = WINDOW_BG ,image={})'.format(p1[0],p1[1]))
exec('label0.place(x = 620,y = 0)')

for var in letters:
        exec(f'{var}=PhotoImage(file="{var}.png")')
for var in buttons:
        exec(f'{var[0]}=Button(window,bd=0,command=lambda: game_brain("{var[0]}","{var[1]}"),bg = WINDOW_BG,font=FONT,image={var[1]})')
        exec('{}.place(x={},y={})'.format(var[0],var[2],var[3]))




def game_brain(button, letter):
        global count,win_count,score
        exec('{}.destroy()'.format(button))
        if letter in word:
            for i in range(1,len(word)):
                if word[i] == letter:
                    win_count += 1
                    exec(f'label{i}.config(text="{letter}")')
            if win_count == len(word):
                score += 1
                messagebox.showinfo('GOOD JOB, YOU WON!\n GOODBYE!')
                window.destroy()

        else:
            count += 1
            exec('label{}.destroy()'.format(count-1))
            exec('label{}.place(x={},y={})'.format(count,620,0))
            if count == 6:
                messagebox.showinfo('GAME OVER','YOU LOST!\nGOODBYE!')
                window.destroy()

                
def EXIT():
    answer = messagebox.askyesno('ALERT','Do you want to exit the game?')
    if answer == True:
        window.destroy()

e1 = PhotoImage(file = 'exit.png')
ex = Button(window,bd = 0,command = EXIT,bg = WINDOW_BG,font = FONT,image = e1)
ex.place(x=1050,y=20)
#-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

window.mainloop()
  • You shouldn't be using `exec` to create widgets, largely because of problems like this -- it makes your code impossible to debug. You can store the labels in a dictionary: `label[var] = Label(...)`. – Bryan Oakley Jun 02 '22 at 14:12

1 Answers1

0

Why exec function in python adds '.!' at the beginning of the text?

The exec function isn't doing that. Tkinter by default names all of its widgets with a leading exclamation point. When you print out a widget, it has to be converted to a string. For tkinter widgets, the result of str(some_widget) is the widget's name.

You can see this quite easily without exec:

import tkinter as tk
root = tk.Tk()
label = tk.Label(root)
print(label)

The above will print something like .!label. If you create a second label it will be .!label2, a third will be .!label3 and so on.


On an unrelated note, you shouldn't be using exec to create widgets. It makes the code very hard to understand and debug. If you want to create widgets in a loop, add them to a dictionary or list instead of dynamically creating variables with exec.

For example:

    labels = {}
    for var in range(1,len(word)): 
        label = Label(window,text="_",bg=WINDOW_BG,font=FONT)
        label.pack(padx=500, pady=100)
        labels[var] = label

With that, you can later reference the widgets as labels[1], labels[2], etc.

You should do the same thing with the images, or anything else that you create in a loop and want to keep track of.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685