I just started to learn python and am currently trying to make a classic 'Hangman' game using tkinter. Learning by doing, you know..
See the code I have so far below. Surely far from perfect but working in general - any general feedback appreciated however.
Now where I am stuck (and trying for hours..):
After a word is completely guessed it will automatically show the next word (usually from a random list but I removed that here).
I want the final word to stay on the screen for some seconds but this is not happening no matter what I do. I played around with the after
method of the widgets and also with the time.sleep
now.
But no effect.
The main problem: When the last letter of a word is guessed, it will not show the last letter but go directly to the new word.
It seems the problem is the new_word()
function I created because when I remove it, at least the last letter will show (but the game is not moving forward of course).
Really drives me crazy because I can not find the logic behind that behavior. I hope someone can have a look and tell me what's wrong.
# -*- coding: utf-8 -*-
from tkinter import *
import random
import time
root = Tk()
root.geometry('700x800')
root.configure(background='white')
headline = Label(root, text='HANGMAN 2.0 \n',
bg='white',
font=('console', '30', 'bold underline')
).place(x=10, y=10)
letter_label = Label(root)
b_new_game = Button(root, text='New game', relief='solid', font=('console', 15), command=lambda: new_game())
b_new_game.place(x=10, y=450)
player_name = StringVar()
input_player_name = Entry(root, textvariable=player_name, font=('console', 15))
input_player_name.place(x=200, y=455)
player_name.set('player')
alphabet = [i for i in 'abcdefghijklmnopqrstuvwxyz'.upper()]
letters = []
l_buttons = []
start_trials = 10
input_word = ''
class Player:
"""Create player of the game"""
def __init__(self, name, trials, score):
self.name = name
self.trials = trials
self.score = score
def make_guess(self, letter):
if letter not in input_word:
self.trials -= 1
if self.trials == 0: # GAME OVER?
msg_box = Tk()
Label(msg_box, text='GAME OVER').pack()
# Add Highscore
for i, v in enumerate(input_word):
if v == letter:
letters[i] = letter
display_letters()
if '_' not in letters: # Won?
self.score = 1
time.sleep(1)
new_word()
show_stats()
return letters
class LetterButton:
"""Buttons for each letter to choose from"""
def __init__(self, letter):
self.letter = letter
self.letter_button = Button(root,
text=letter,
fg='white',
bg='black',
relief='ridge',
activebackground='grey',
font=('console', 14, 'bold'),
borderwidth=2,
command=lambda: self.picked_letter(letter)
)
def picked_letter(self, letter):
"""Returns the letter, deletes the button"""
self.letter_button.place_forget()
Player.make_guess(player, letter)
def new_game():
"""Start new game, reset stats"""
global player
player = Player(player_name.get(), start_trials, 0)
new_word()
def display_letters():
"""Display the hidden and guessed letters"""
global letter_label
letter_label.place_forget()
letter_label.config(bg='white', text=' '.join(letters), font=('console', 25, 'bold'))
letter_label.place(x=10, y=200)
def renew_buttons():
"""Deleting and recreating the buttons for the letters to choose"""
for button in l_buttons:
button.place_forget()
x, y = 10, 300
for pos, item in enumerate(alphabet):
if pos < (len(alphabet) // 2):
lb = LetterButton(item).letter_button
lb.place(x=x pos * 50, y=y, width=40)
l_buttons.append(lb)
else:
lb = LetterButton(item).letter_button
lb.place(x=x (pos - 13) * 50, y=y 50, width=40)
l_buttons.append(lb)
def show_stats():
x, y, fontsize, fg1, bg1 = 450, 80, 12, 'black', 'white'
Label(root, text='Player', fg=fg1, bg=bg1, font=('console', fontsize, 'bold')).place(x=x, y=y, width=80, anchor='w')
Label(root, text='Score', fg=fg1, bg=bg1, font=('console', fontsize, 'bold')).place(x=x, y=y 25, width=80, anchor='w')
Label(root, text='Trials', fg=fg1, bg=bg1, font=('console', fontsize, 'bold')).place(x=x, y=y 50, width=80, anchor='w')
Label(root, text=player.name, fg=fg1, bg=bg1, font=('console', fontsize, 'bold')).place(x=x 80, y=y, width=100, anchor='w')
Label(root, text=player.score, fg=fg1, bg=bg1, font=('console', fontsize, 'bold')).place(x=x 80, y=y 25, width=100, anchor='w')
Label(root, text=player.trials, fg=fg1, bg=bg1, font=('console', fontsize, 'bold')).place(x=x 80, y=y 50, width=100, anchor='w')
def new_word():
global letters
global input_word
input_word = 'TESTWORD'
letters = ['_' for _ in input_word]
display_letters()
renew_buttons()
show_stats()
root.mainloop()
CodePudding user response:
The time.sleep()
completely hangs the program. That might be the reason.
So create a new function with some name like newword_start()
and inside it, add these 2 things:
time.sleep(1)
new_word()
Then at the very starting of the code, add import threading as th
.
Then in the make_guess()
function, after if '_' not in letters...
, call the newword_start()
function like this:
th.Thread(target=newword_start).start()
So the final code will be:
# Other modules
import threading as th
import time
# Your code
def newword_start():
time.sleep(1)
new_word()
class Player:
# Your code
def make_guess(self, letter):
# Your code again
if '_' not in letters: # Won?
self.score = 1
th.Thread(target=newword_start).start()
# Rest all code