How To Build Number Guessing Game Using CustomTkinter In Python?

Hello Pythonistas welcome back. Today we will continue our series CodeCraft: Building Skills One Project at a Time.

So let’s get started, the 5th project in this series is a Number Guessing Game Using CustomTkinter In Python.

This would be the GUI version of the second project of this series.

Pre-requisites

You need to go through the previous number-guessing project once. Just check out the source code here. If you understand this one you are good to go.

Why A Number Guessing Game Using CustomTkinter?

This way you would be able to create add-ons to your existing projects in the future.

Also, it’s the best way to lay your foundation for GUI’s and OOP.

Approach To Create A Number Guessing Game Using CustomTkinter

Task: Build a game where the computer randomly selects a number, and the user has to guess it.

  1. First, import the CustomTkinter module, then create a GUI class and initialize macros.
  2. Secondly, create a method to create the GUI design. Call this method inside __init__
  3. Create a method to take and clear input.
  4. Create 2 methods:
    • 1st to check win
    • 2nd to update the label
  5. Modify the play game method from the previous project using these methods.
  6. Create an instance of this class and call the mainloop method.

Step 1: Create a GUI class.

import random
from customtkinter import *
# Step 1: Create class
class NumGuessGUI(CTk):
    def __init__(self):
        super().__init__()
        self.MIN_NUMBER = 1
        self.MAX_NUMBER = 50
        self.MAX_ATTEMPTS = 10

Step 2: Create a method to create the GUI design.

We will build 3 methods:

  1. build_start_widget(): This would be the first interface when the game starts for introduction.
    • (An intro label and a start game button)
  2. build_game_widget(): When the user starts the game this screen will be shown.
    • (An entry widget to take the guess, a label to display the no. of attempts left, button to see if the guess is right or not.)
  3. end_game_gui(): This screen be shown when the game ends. It will display won or lose.
    • (label to display won or lost)
Click To See The Code
import random
from customtkinter import *
# Step 1: Create class
class NumGuessGUI(CTk):
    def __init__(self):
        super().__init__()
        self.MIN_NUMBER = 1
        self.MAX_NUMBER = 50
        self.MAX_ATTEMPTS = 10
        self.attempts = 1
        self.win = False
        self.original_num = random.randint(self.MIN_NUMBER, self.MAX_NUMBER)
        self.build_start_widget()
    
    # Step 2: Create GUI
    def build_start_widget(self):
        self.intro_label = CTkLabel(master=self, text=f"You will get a total of {self.MAX_ATTEMPTS} guesses.\nIn {self.MAX_ATTEMPTS} guesses you have to guess the correct no. between {self.MIN_NUMBER} to {self.MAX_NUMBER}.\nREADY???")
        self.intro_label.grid(row=0, column=0, padx=10, pady=10)

        self.start_button = CTkButton(master=self, text="Click To Start the Game", command=self.build_game_widget)
        self.start_button.grid(row=1, column=0, padx=10, pady=10)

    def build_game_widget(self):
        self.intro_label.grid_forget()
        self.start_button.grid_forget()

        self.num = CTkEntry(master=self)
        self.num.grid(row=0, column=0, padx=10, pady=10)

        # Initialize self.attempts
        self.desc_label = CTkLabel(master=self, text=f"You are left with {self.attempts} attempts")
        self.desc_label.grid(row=0, column=1, padx=10, pady=10)

        self.guess_button = CTkButton(master=self, text="Guess", command=self.end_game_gui)
        self.guess_button.grid(row=1, column=0, padx=10, pady=10)

    def end_game_gui(self):
        # Hide the input and other labels
        self.num.grid_forget()
        self.desc_label.destroy() 
        self.output_label.grid_forget()
        self.guess_button.grid_forget()

        if self.win:
            self.win_label = CTkLabel(master=self, text=f"\n\nYou got it right in {self.attempts} attempts.\nThe number is {self.original_num}")
            self.win_label.grid(row=0, column=0, padx=10, pady=10)
        else:
            self.lost_label = CTkLabel(master=self, text=f"GAME OVER\nThe correct number was {self.original_num}")
            self.lost_label.grid(row=0, column=0, padx=10, pady=10)

Step 3: Create a method to take and clear input

Initialize guessed_num and guessed_list inside __init__.

Then create get_val() method

        self.guessed_num = self.MAX_NUMBER + 1
        self.guessed_list = []
    
        # Step 3: Get Value
        def get_val(self):
            # Initialize guessed_num
            # Initialize guessed_list
            self.guessed_num = self.num.get()
            self.num.delete(0, END)

    Step 4: Create Check Win and Update Label Method

    # Step 4: Check Win and Update label
        def check_win(self) -> bool:
            return(self.guessed_num == self.original_num)
    
        def update_label(self):
            self.desc_label.grid_forget()
            self.desc_label = CTkLabel(master=self, text=f"You are left with {self.attempts} attempts")
            self.desc_label.grid(row=0, column=1, padx=10, pady=10)
    

    Step 5: Modify The play_game From The Previous Project

    Click For Code
    def play_game(self):
            if self.attempts >= self.MAX_ATTEMPTS:
                self.end_game_gui()
                return
    
            if not self.get_val():
                return
    
            self.attempts += 1
    
            if self.guessed_num == self.original_num:
                self.win = True
                self.end_game_gui()
            elif self.guessed_num in self.guessed_list:
                self.output_label.grid_forget()
                self.output_label = CTkLabel(master=self, text="You already guessed this number before!")
                self.output_label.grid(row=1, column=1, padx=10, pady=10)
            elif self.guessed_num > self.original_num:
                self.output_label.grid_forget()
                self.output_label = CTkLabel(master=self, text=f"The number is less than {self.guessed_num}")
                self.output_label.grid(row=1, column=1, padx=10, pady=10)
            else:
                self.output_label.grid_forget()
                self.output_label = CTkLabel(master=self, text=f"The number is greater than {self.guessed_num}")
                self.output_label.grid(row=1, column=1, padx=10, pady=10)
    
            self.guessed_list.append(self.guessed_num)
            self.update_label()
    
            if self.attempts >= self.MAX_ATTEMPTS and not self.win:
                self.end_game_gui()
    

    Step 6: Create an instance of and call the mainloop method.

    # Step 6: Create Instance And Call mainloop
    if __name__ == "__main__":
        app = NumGuessGUI()
        app.mainloop()

    Full Source Code

    Click To See Full Source Code
    import random
    from customtkinter import *
    
    # Step 1: Create class
    class NumGuessGUI(CTk):
        def __init__(self):
            super().__init__()
            self.MIN_NUMBER = 1
            self.MAX_NUMBER = 50
            self.MAX_ATTEMPTS = 10
            self.attempts = 0  # Start at 0 because we will increment before the first guess
            self.win = False
            self.original_num = random.randint(self.MIN_NUMBER, self.MAX_NUMBER)
            self.guessed_num = 0
            self.guessed_list = []
            self.build_start_widget()
        
        # Step 2: Create GUI
        def build_start_widget(self):
            self.intro_label = CTkLabel(master=self, text=f"You will get a total of {self.MAX_ATTEMPTS} guesses.\nIn {self.MAX_ATTEMPTS} guesses you have to guess the correct no. between {self.MIN_NUMBER} to {self.MAX_NUMBER}.\nREADY???")
            self.intro_label.grid(row=0, column=0, padx=10, pady=10)
    
            self.start_button = CTkButton(master=self, text="Click To Start the Game", command=self.build_game_widget)
            self.start_button.grid(row=1, column=0, padx=10, pady=10)
    
        def build_game_widget(self):
            self.intro_label.grid_forget()
            self.start_button.grid_forget()
    
            self.num = CTkEntry(master=self)
            self.num.grid(row=0, column=0, padx=10, pady=10)
    
            self.desc_label = CTkLabel(master=self, text=f"You are left with {self.MAX_ATTEMPTS - self.attempts} attempts")
            self.desc_label.grid(row=0, column=1, padx=10, pady=10)
            
            self.output_label = CTkLabel(master=self, text=f"")
            self.output_label.grid(row=1, column=1, padx=10, pady=10)
    
            self.guess_button = CTkButton(master=self, text="Guess", command=self.play_game)
            self.guess_button.grid(row=1, column=0, padx=10, pady=10)
    
        def end_game_gui(self):
            # Hide the input and other labels
            self.num.grid_forget()
            self.desc_label.destroy() 
            self.output_label.grid_forget()
            self.guess_button.grid_forget()
    
            if self.win:
                self.win_label = CTkLabel(master=self, text=f"\n\nYou got it right in {self.attempts} attempts.\nThe number is {self.original_num}")
                self.win_label.grid(row=0, column=0, padx=10, pady=10)
            else:
                self.lost_label = CTkLabel(master=self, text=f"GAME OVER\nThe correct number was {self.original_num}")
                self.lost_label.grid(row=0, column=0, padx=10, pady=10)
    
        # Step 3: Get Value
        def get_val(self):
            try:
                self.guessed_num = int(self.num.get())
                self.num.delete(0, END)
                return True
            except ValueError:
                self.output_label.grid_forget()
                self.output_label = CTkLabel(master=self, text="Enter a Valid Number!!")
                self.output_label.grid(row=1, column=1, padx=10, pady=10)
                return False
    
        # Step 4: Check Win and Update label
        def update_label(self):
            if hasattr(self, 'desc_label') and self.desc_label is not None:
                self.desc_label.grid_forget()
                self.desc_label = CTkLabel(master=self, text=f"You are left with {self.MAX_ATTEMPTS - self.attempts} attempts")
                self.desc_label.grid(row=0, column=1, padx=10, pady=10)
    
        # Step 5: Modify play_game
        def play_game(self):
            if self.attempts >= self.MAX_ATTEMPTS:
                self.end_game_gui()
                return
    
            if not self.get_val():
                return
    
            self.attempts += 1
    
            if self.guessed_num == self.original_num:
                self.win = True
                self.end_game_gui()
            elif self.guessed_num in self.guessed_list:
                self.output_label.grid_forget()
                self.output_label = CTkLabel(master=self, text="You already guessed this number before!")
                self.output_label.grid(row=1, column=1, padx=10, pady=10)
            elif self.guessed_num > self.original_num:
                self.output_label.grid_forget()
                self.output_label = CTkLabel(master=self, text=f"The number is less than {self.guessed_num}")
                self.output_label.grid(row=1, column=1, padx=10, pady=10)
            else:
                self.output_label.grid_forget()
                self.output_label = CTkLabel(master=self, text=f"The number is greater than {self.guessed_num}")
                self.output_label.grid(row=1, column=1, padx=10, pady=10)
    
            self.guessed_list.append(self.guessed_num)
            self.update_label()
    
            if self.attempts >= self.MAX_ATTEMPTS and not self.win:
                self.end_game_gui()
    
    # Step 6: Create Instance And Call mainloop
    if __name__ == "__main__":
        app = NumGuessGUI()
        app.mainloop()
    

    Challenge For You!

    This code has a bug.

    Exception in Tkinter callback
    Traceback (most recent call last):
      File "C:\Program Files\Python310\lib\tkinter\__init__.py", line 1921, in __call__
        return self.func(*args)
      File "C:\Users\maitr\AppData\Roaming\Python\Python310\site-packages\customtkinter\windows\widgets\ctk_button.py", line 531, in _clicked
        self._command()
      File "c:\Users\maitr\Desktop\CodeCraft\Projects\gui_num_guessing.py", line 101, in play_game
        self.update_label()
      File "c:\Users\maitr\Desktop\CodeCraft\Projects\gui_num_guessing.py", line 70, in update_label
        self.desc_label.grid_forget()
      File "C:\Users\maitr\AppData\Roaming\Python\Python310\site-packages\customtkinter\windows\widgets\core_widget_classes\ctk_base_class.py", line 324, in grid_forget
        return super().grid_forget()
      File "C:\Program Files\Python310\lib\tkinter\__init__.py", line 2532, in grid_forget
        self.tk.call('grid', 'forget', self._w)
    _tkinter.TclError: bad window path name ".!ctklabel11"

    Try and resolve this.

    Hope you had fun building this… See you in the next article till then, Happy Coding…

    Leave a Reply