Home > Mobile >  Beginner problem with a tkinter 'Hangman' exercise
Beginner problem with a tkinter 'Hangman' exercise

Time:10-21

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
  • Related