Python Forum
[Tkinter] Implementing number of guesses left in guessing game
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tkinter] Implementing number of guesses left in guessing game
#1
I am building a guessing game where the user has to guess what the capital of a country is by looking at the image of the country's flag. For this I have made a GUI using the tkinter module.

I have added a function answer() which indicates if the user input is right or wrong. It works fine when the user enters the correct answer but does not get proper results if an incorrect answer is entered.

Could someone advise where I am going wrong and a possible solution for this.

from tkinter import *

# Initialization
root = Tk()

# Placing the GUI window on the top left corner of the user's computer
root.geometry('+0+0')
# Setting the title of the game
root.title("Flag Guessing Game")
# Setting the dimensions of the GUI window
root.geometry("300x400")

# Defining the heading of the game
lbl_heading = Label(root, text = "Welcome to this fun game!")
lbl_heading.pack(pady = 10)
lbl_heading.config(font=("Helvetica", 12, 'bold'))

# Inserting an image
image = PhotoImage(file="image2l.png")
setting = Label(root, image = image)
setting.pack()

# Define a new window when Rules is clicked
def create_window1():
    window1 = Toplevel(root)
    window1.title("Rules for this game")
    window1.geometry("600x150")
    tb1 = Label(window1, text = "The rules for this game are given below:")
    tb1.config(font=("Helvetica", 8, 'bold'))
    tb1.pack()
    tb2 = Label(window1, text = "1. Once you click on START, you will enter the game premises.")
    tb2.pack()
    tb3 = Label(window1, text = "2. Look at the image of the given flag.")
    tb3.pack()
    tb4 = Label(window1, text = "3. If you know which country the flag belongs to, please enter it in the text box provided.")
    tb4.pack()
    tb5 = Label(window1, text = "4. You will proceed to the next question only if you answer the current question correctly.")
    tb5.pack()
    tb6 = Label(window1, text = "5. If your answer is wrong, you will get threee chances after which you will need to restart the game.")
    tb6.pack()
    window1.mainloop()

# Create a button to read the rules of the game
B1 = Button(root, text="Rules", command=create_window1, width = 5)
B1.config(font=("Helvetica", 10, 'bold'))
B1.pack(pady = 15) 

# Define a new window when START is clicked
def create_window2():
    global e1
    global window2
    window2 = Toplevel(root)
    window2.title("Game Premises")
    window2.geometry("400x600")
    lbl_heading2 = Label(window2, text = "Guess the capital of this country:")
    lbl_heading2.pack(pady = 10)
    lbl_heading2.config(font=("Helvetica", 12, 'bold'))
    
    # Insert flag image
    image2 = PhotoImage(file="USFlag.png")
    setting2 = Label(window2, image = image2)
    setting2.pack()

    # User input button
    Label(window2, text="Enter the capital").pack(pady = 10)
    e1 = Entry(window2)
    e1.pack(pady = 10)
    user_input = e1.get()

    # Create a submit button
    B2 = Button(window2, text="Submit", command=answer, width = 10)
    B2.config(font=("Helvetica", 10, 'bold'))
    B2.pack(pady = 15)

    # Create a hint button 
    H = Button(window2, text="Hint", command = lambda root = window2: labeltext(window2), width = 5)
    H.config(font=("Helvetica", 10, 'bold'))
    H.pack(pady = 10)

    # Next button
    nextb = Button(window2, text="Next", command=second, width = 5)
    nextb.config(font=("Helvetica", 10, 'bold'))
    nextb.pack(pady = 10)

    quitb = Button(window2, text="Exit", command=window2.destroy, width = 5)
    quitb.config(font=("Helvetica", 10, 'bold'))
    quitb.pack(pady = 10)
    
    window2.mainloop()

def secondpage():
    global e2
    global window3
    window3 = Toplevel(root)
    window3.title("Game Premises")
    window3.geometry("400x600")
    lbl_heading2 = Label(window3, text = "Guess the capital of this country:")
    lbl_heading2.pack(pady = 10)
    lbl_heading2.config(font=("Helvetica", 12, 'bold'))
    
    # Insert flag image
    image2 = PhotoImage(file="USFlag.png")
    setting2 = Label(window3, image = image2)
    setting2.pack()
    setting2.image = image2

    # User input button
    Label(window3, text="Enter the capital").pack(pady = 10)
    e2 = Entry(window3)
    e2.pack(pady = 10)
    user_input2 = e2.get()
    
    # Create a submit button
    B2 = Button(window3, text="Submit", command=answer2, width = 10)
    B2.config(font=("Helvetica", 10, 'bold'))
    B2.pack(pady = 15)

    # Create a hint button 
    H = Button(window3, text="Hint", command = lambda root = window3: labeltext2(window3), width = 5)
    H.config(font=("Helvetica", 10, 'bold'))
    H.pack(pady = 10)

    # Previous button
    prevb = Button(window3, text="Previous", command=previouspage, width = 8)
    prevb.config(font=("Helvetica", 10, 'bold'))
    prevb.pack(pady = 10)

    quitb = Button(window3, text="Quit", command=window3.destroy, width = 5)
    quitb.config(font=("Helvetica", 10, 'bold'))
    quitb.pack(pady = 10)

    window3.mainloop()

def labeltext(create_window2):
    global labelt
    labelt = Label(window2, text = "Name of country: USA")
    labelt.pack()

def labeltext2(create_window2):
    global labelt
    labelt = Label(window3, text = "Name of country: India")
    labelt.pack()

def second():
    window2.destroy()
    secondpage()

def previouspage():
    window3.destroy()
    create_window2()

def answer():
    guessed = False
    tries = 2
    while not guessed and tries != 0:
        user_input = e1.get()
        if user_input == "Washington":
            Label(window2, text="Correct answer!").pack(pady = 10)
        else:
            Label(window2, text="Incorrect answer. Please try again").pack()
            Label(window2, text="You have " + str(tries) + " tries left").pack()
            tries -= 1
            if tries == 0:
                Label(window2, text="All tries have been exhausted. Please restart and try again").pack()
        
def answer2():
    user_input2 = e2.get()
    guesses_remaining = 3
    counter = 0
    if user_input2 == "New Delhi":
        Label(window3, text="Correct answer!").pack(pady = 10)
    else:
        Label(window3, text="Incorrect answer. Please try again").pack(pady = 10)
    
# Create a button to start the game
B3 = Button(root, text="START", command=create_window2, width = 10)
B3.config(font=("Helvetica", 14, 'bold'))
B3.pack()

# Exit button
exit_bmain = Button(root, text="Exit", command=root.destroy, width = 5)
exit_bmain.config(font=("Helvetica", 10, 'bold'))
exit_bmain.pack(pady = 10)

root.mainloop()
Reply
#2
You shouldn't use a while loop in GUI code like that to update the GUI, GUI code is event-driven.
from tkinter import * floods the namespace.
Only one tk mainloop is required
writing lots of GUI code with global variables is not a good approach, use classes to group things together.

I got a bit carried away and wrote your code using classes, hopefully, you can study it.
Note: I changed the image labels to just show text, they will need changing to show the images instead.
import tkinter as tk
from tkinter import messagebox
import dataclasses

RULES = ("1. Once you click on START, you will enter the game premises.",
         "2. Look at the image of the given flag.",
         "3. If you know which country the flag belongs to, please enter it in"" the text box provided.",
         "4. You will proceed to the next question only if you answer the current question correctly.",
         "5. If your answer is wrong, you will get threee chances after which you will need to restart the game.")


@dataclasses.dataclass
class Question:
    flag: str
    correct_answer: str
    hint: str
    hint_used: bool = dataclasses.field(default=False, init=False)
    answer: str = dataclasses.field(default='', init=False)
    tries: int = dataclasses.field(default=3, init=False)

    def set_answer(self, answer: str):
        if not self.tries:
            return
        self.tries -= 1
        self.answer = answer.lower()

    @property
    def is_correct(self):
        return self.answer == self.correct_answer

    @property
    def can_answer(self):
        return self.tries > 0 and not self.is_correct

    @property
    def answer_state_string(self):
        if self.is_correct:
            return f'{self.answer} is Correct!'
        elif not self.tries:
            return 'All tries have been exhausted'
        return (f'{self.answer} is incorrect. Please try again\n'
                f'You have {self.tries} tries left')


@dataclasses.dataclass
class Questions:
    questions: list[Question] = dataclasses.field(default_factory=list)
    question_index: int = dataclasses.field(default=0, init=False)

    @property
    def amount_of_questions(self):
        return len(self.questions)

    @property
    def has_next_question(self):
        return self.question_index < self.amount_of_questions-1

    @property
    def has_previous_question(self):
        return self.question_index > 0

    @property
    def current_question(self):
        try:
            return self.questions[self.question_index]
        except IndexError:
            return None

    def increase_question_index(self):
        self.question_index = max(
            self.question_index + 1, self.amount_of_questions-1)

    def decrease_question_index(self):
        self.question_index = min(self.question_index-1, 0)


questions = [Question('flag1.png', 'answer1', "Name of country: USA"),
             Question('flag2.png', 'answer2', "Name of country: India")]

flag_questions = Questions(questions)


class App(tk.Tk):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Placing the GUI window on the top left corner of the user's computer
        self.geometry('+0+0')
        # Setting the title of the game
        self.title("Flag Guessing Game")
        # Setting the dimensions of the GUI window
        self.geometry("300x400")

        # Defining the heading of the game
        lbl_heading = tk.Label(self, text="Welcome to this fun game!")
        lbl_heading.pack(pady=10)
        lbl_heading.config(font=("Helvetica", 12, 'bold'))

        # Inserting an image
        # image = PhotoImage(file="image2l.png")
        setting = tk.Label(self, text='image2l.png')
        setting.pack()

        # Create a button to read the rules of the game
        self.btn_rules = tk.Button(self, text="Rules",
                                   command=self.on_btn_rules, width=5)
        self.btn_rules.config(font=("Helvetica", 10, 'bold'))
        self.btn_rules.pack(pady=15)

        # Create a button to start the game
        self.btn_start = tk.Button(self, text="START",
                                   command=self.on_btn_start, width=10)
        self.btn_start.config(font=("Helvetica", 14, 'bold'))
        self.btn_start.pack()

        # Exit button
        exit_bmain = tk.Button(
            self, text="Exit", command=self.destroy, width=5)
        exit_bmain.config(font=("Helvetica", 10, 'bold'))
        exit_bmain.pack(pady=10)

    def on_btn_rules(self):
        self.btn_rules.config(state='disabled')
        self.rules_frame = RulesFrame(self)
        self.rules_frame.bind("<Destroy>", self.on_rules_frame_destroy)

    def on_rules_frame_destroy(self, event):
        self.btn_rules.config(state='normal')

    def on_btn_start(self):
        self.btn_start.config(state='disabled')
        self.questions_frame = QuestionsFrame(self)
        self.questions_frame.bind("<Destroy>", self.on_questions_frame_destroy)

    def on_questions_frame_destroy(self, event):
        self.btn_start.config(state='normal')


class RulesFrame(tk.Toplevel):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.title("Rules for this game")
        self.geometry("600x150")
        label = tk.Label(self, text="The rules for this game are given below:")
        label.config(font=("Helvetica", 8, 'bold'))
        label.pack()
        for rule in RULES:
            label = tk.Label(self, text=rule)
            label.pack()


# Define a new window when START is clicked
class QuestionsFrame(tk.Toplevel):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.title("Game Premises")
        self.geometry("400x600")
        lbl_heading2 = tk.Label(
            self, text="Guess the capital of this country:")
        lbl_heading2.pack(pady=10)
        lbl_heading2.config(font=("Helvetica", 12, 'bold'))

        # Insert flag image
        # image2 = PhotoImage(file="USFlag.png")
        self.lbl_flag = tk.Label(self)
        self.lbl_flag.pack()

        self.lbl_hint = tk.Label(self)
        self.lbl_hint.pack()

        # User input button
        tk.Label(self, text="Enter the capital").pack(pady=10)
        self.entry_answer = tk.Entry(self)
        self.entry_answer.pack(pady=10)

        frame1 = tk.Frame(self)
        # Create a submit button
        self.btn_submit = tk.Button(frame1, text="Submit",
                                    command=self.on_btn_submit)
        self.btn_submit.config(font=("Helvetica", 10, 'bold'))
        self.btn_submit.pack(padx=5, side='left')

        # Create a hint button
        self.btn_hint = tk.Button(
            frame1, text="Hint", command=self.on_btn_hint)
        self.btn_hint.config(font=("Helvetica", 10, 'bold'))
        self.btn_hint.pack(padx=20, side='left')
        frame1.pack(pady=10)

        frame2 = tk.Frame(self)
        # Previous button
        self.btn_previous = tk.Button(
            frame2, text="Previous", command=self.on_btn_previous)
        self.btn_previous.config(font=("Helvetica", 10, 'bold'))
        self.btn_previous.pack(padx=5, side='left')

        # Next button
        self.btn_next = tk.Button(
            frame2, text="Next", command=self.on_btn_next)
        self.btn_next.config(font=("Helvetica", 10, 'bold'))
        self.btn_next.pack(padx=5, side='left')

        btn_quit = tk.Button(frame2, text="Exit", command=self.destroy)
        btn_quit.config(font=("Helvetica", 10, 'bold'))
        btn_quit.pack(padx=20, side='left')
        frame2.pack(pady=10)

        self.flag_questions = flag_questions
        self.update()

    def get_current_question(self):
        return self.flag_questions.current_question

    def update(self):
        self.entry_answer.delete(0, 'end')
        question = self.get_current_question()
        if not question:
            return
        self.lbl_flag.config(text=question.flag)

        hint = f'Hint: {question.hint}' if question.hint_used else ''
        self.lbl_hint.config(text=hint)

        self.entry_answer.insert(0, question.answer)

        self.update_buttons()

    def update_buttons(self):
        question = self.get_current_question()
        for btn, state in ((self.btn_previous, self.flag_questions.has_previous_question),
                           (self.btn_next, self.flag_questions.has_next_question),
                           (self.btn_hint, not question.hint_used),
                           (self.btn_submit, question.can_answer)):
            self.btn_enable(btn, state)

    def btn_enable(self, btn, enable):
        btn.config(state='normal' if enable else 'disabled')

    def on_btn_hint(self):
        question = self.get_current_question()
        question.hint_used = True
        self.update()

    def on_btn_submit(self):
        user_input = self.entry_answer.get()
        question = self.get_current_question()
        question.set_answer(user_input)
        messagebox.showinfo(message=question.answer_state_string)
        if question.is_correct:
            self.flag_questions.increase_question_index()

        self.update()

    def on_btn_previous(self):
        self.flag_questions.decrease_question_index()
        self.update()

    def on_btn_next(self):
        self.flag_questions.increase_question_index()
        self.update()


if __name__ == '__main__':
    app = App()
    app.mainloop()
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [Tkinter] Implementing tk.after zukochew 5 3,638 Aug-10-2018, 12:47 PM
Last Post: Windspar

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020