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
#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


Messages In This Thread
RE: Implementing number of guesses left in guessing game - by Yoriz - May-29-2021, 01:33 PM

Possibly Related Threads…
Thread Author Replies Views Last Post
  [Tkinter] Implementing tk.after zukochew 5 3,695 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