Search code examples
pythontkintercustomtkinter

Error with CTkMessagebox (TypeError: 'str' object is not callable)


CONTEXT I programmed a hangman game on python and used "tkinter" for its GUI. It works perfectly. I switched from tkinter to "customtkinter" and "CTkMessagebox" to have a GUI with a better visual, eveyrthin works perfectly except one thing.

PROBLEM When my program check the entry of the user (to make sure it's a letter without an accent or a cedilla), a message box with an error message pops up if the entry is incorrect. But with CTkMessagebox nothin I receive the following error:

Exception in Tkinter callback Traceback (most recent call last): File "C:\Users\thiba\AppData\Local\Programs\Python\Python312\Lib\tkinter_init_.py", line 1948, in call return self.func(*args) ^^^^^^^^^^^^^^^^ File "C:\Users\thiba\AppData\Local\Programs\Python\Python312\Lib\site-packages\customtkinter\windows\widgets\ctk_button.py", line 554, in _clicked self._command() File "c:\Users\thiba\OneDrive\Documents\Studies\Programming\Python\Hangman\Hangman (with customtkinter)", line 32, in game CTkMessagebox(master=root, title="Entry error", message="Only letters (without an accent or a cedilla) can be entered.", icon="warning") File "C:\Users\thiba\AppData\Local\Programs\Python\Python312\Lib\site-packages\CTkMessagebox\ctkmessagebox.py", line 57, in init super().init() File "C:\Users\thiba\AppData\Local\Programs\Python\Python312\Lib\site-packages\customtkinter\windows\ctk_toplevel.py", line 36, in init super().init(*args, **pop_from_dict_by_set(kwargs, self.valid_tk_toplevel_arguments)) File "C:\Users\thiba\AppData\Local\Programs\Python\Python312\Lib\tkinter_init.py", line 2681, in init self.title(root.title()) ^^^^^^^^^^^^ TypeError: 'str' object is not callable

WHAT I TRIED

  • I made a another small program with CTkMessagebox and it worked
  • I put the line concerning the message box somewhere else in the code but the problem is the same

Here is my code

from Dictionary import dico
from Drawings import drawings

import random
import customtkinter as CTk
from CTkMessagebox import CTkMessagebox

word_guess = dico[random.randrange(len(dico) - 1)]
word_hidden = " _" * len(word_guess)
drawing_nb = 0
tries = 0
list = []
print(word_guess)





#------------------------------- GAME LOOP -------------------------------#
def game(*args):
    global word_guess
    global word_hidden
    global tries
    global drawings
    global drawing_nb

    letter_tried = f2_var_LetterTried.get().lower()
    letters_tried = f2_var_LettersTried.get()

    # Check the entry
    if not (letter_tried.isalpha() and letter_tried.isascii()):
        CTkMessagebox(master=root, title="Entry error", message="Only letters (without an accent or a cedilla) can be entered.", icon="warning")
        return
    
    # Check if the word/letter has already been tried
    for string in list:
        if letter_tried == string:
            f2_ent_WordEntry.delete(0, CTk.END)
            return

    tries +=1

    # For a letter
    if len(letter_tried) == 1:
        # Check if the letter has already been tried
        for string in list:
            if letter_tried == string:
                f2_ent_WordEntry.delete(0, CTk.END)
                return

        if letter_tried not in (word_guess or word_hidden):
            drawing_nb += 1
            f2_var_drawing.set(drawings[drawing_nb])
            if letters_tried == "":
                letters_tried += letter_tried
                f2_var_LettersTried.set(letters_tried)
            else:
                letters_tried += ", " + letter_tried
                f2_var_LettersTried.set(letters_tried)
        else:
            letter_pos = 1
            for char in word_guess:
                if letter_tried == char:
                    word_hidden = word_hidden[:letter_pos * 2 - 1] + letter_tried + word_hidden[letter_pos * 2:]
                letter_pos += 1
            f2_var_WordHidden.set(word_hidden)

    # For a word
    # Check if the word has already been tried
    for string in list:
        if letter_tried == string:
            f2_ent_WordEntry.delete(0, CTk.END)
            return

    if len(letter_tried) >1:
        if letter_tried == word_guess:
            end_game(f'Congratulations, you have found the word "{word_guess}"" in {tries} tries!')
            return
        else:
            drawing_nb += 1
            f2_var_drawing.set(drawings[drawing_nb])
            if letters_tried == "":
                letters_tried += letter_tried
                f2_var_LettersTried.set(letters_tried)
            else:
                letters_tried += ", " + letter_tried
                f2_var_LettersTried.set(letters_tried)
    
    if "_" not in word_hidden:
        end_game(f"Congratulations, you have found the word {word_guess} in {tries} tries!")
        return

    if drawing_nb == 10:
        end_game(f'Game Over, the word was "{word_guess}".')
    
    list.append(letter_tried)
    f2_ent_WordEntry.delete(0, CTk.END)





#------------------------------- CHANGING FRAME -------------------------------#
def first_game():
    root.bind("<Return>", game)
    f2_ent_WordEntry.focus()
    frame2.tkraise()

def end_game(message):
    global drawings
    global drawing_nb 
    f3_lb_EndMessage.configure(text=message)
    f3_lb_drawing.configure(text=drawings[drawing_nb])
    root.bind("<Return>","")
    frame3.tkraise()

def new_game():
    global word_guess
    global word_hidden
    global tries
    global drawings
    global drawing_nb
    global f2_var_LettersTried
    global list

    word_guess = dico[random.randrange(len(dico) - 1)]
    word_hidden = " _" * len(word_guess)
    drawing_nb = 0
    tries = 0
    list = []

    f2_var_LettersTried.set("")
    f2_var_WordHidden.set(word_hidden)
    f2_var_drawing.set(drawings[drawing_nb])

    f2_ent_WordEntry.delete(0, CTk.END)
    f2_ent_WordEntry.focus()
    root.bind("<Return>", game)
    frame2.tkraise()
    print(word_guess)





#------------------------------- START WINDOW -------------------------------#
# Create main application window
root = CTk.CTk()
root.title = "The Hangman"
root.geometry("750x300")

# Create 1ST frame2
frame1 = CTk.CTkFrame(root)
frame1.grid(column=0, row=0, sticky="news")

# Label for drawings
f1_lb_hangman = CTk.CTkLabel(frame1, text=drawings[10], font=("Consolas", 12), justify="left")
f1_lb_hangman.grid(column=0, row=0, rowspan=4)

# Label for title
f1_lb_title = CTk.CTkLabel(frame1, text="THE HANGMAN", font=("Consolas", 20))
f1_lb_title.grid(column=1, row=0, columnspan=2)

# Label for signature
f1_lb_signature = CTk.CTkLabel(frame1, text="By T.D.", font=('Consolas', 9))
f1_lb_signature.grid(column=1, row=1, columnspan=2, sticky="n")

# Button for starting a game
f1_btn_start = CTk.CTkButton(frame1, text="Start a game", command=first_game)
f1_btn_start.grid(column=1, row=2, columnspan=2)





#------------------------------- IN-GAME WINDOW -------------------------------#
# Create 2ND frame
frame2 = CTk.CTkFrame(root)
frame2.grid(column=0, row=0, sticky="news")

# Label for drawings
f2_var_drawing = CTk.StringVar()
f2_var_drawing.set(drawings[drawing_nb])
f2_lb_drawing = CTk.CTkLabel(frame2, textvariable=f2_var_drawing, font=("Consolas", 12), justify="left")
f2_lb_drawing.grid(column=0, row=0, rowspan=4)

# Label for title
f2_lb_title = CTk.CTkLabel(frame2, text="THE HANGMAN", font=("Consolas", 20))
f2_lb_title.grid(column=1, row=0, columnspan=2)

# Label for signature
f2_lb_signature = CTk.CTkLabel(frame2, text="By Thibaud D.", font=('Consolas', 9))
f2_lb_signature.grid(column=1, row=1, columnspan=2, sticky="n")

# Label for hidden word
f2_var_WordHidden = CTk.StringVar()
f2_var_WordHidden.set(word_hidden)
f2_lb_WordHidden = CTk.CTkLabel(frame2, textvariable=f2_var_WordHidden, font=("Consolas", 12), wraplength=500)
f2_lb_WordHidden.grid(column=1, row=2, columnspan=500)

# Labels for letters tried
f2_var_LettersTried = CTk.StringVar()
f2_lb_LettersTried = CTk.CTkLabel(frame2, textvariable=f2_var_LettersTried, font=("Consolas", 12), justify="left")
f2_lb_LettersTried.grid(column=2, row=3)
CTk.CTkLabel(frame2, text="Previous tries:", font=("Consolas", 12), justify="right").grid(column=1, row=3, sticky="e")

# Entry to try letter/word
f2_var_LetterTried = CTk.StringVar()
f2_ent_WordEntry = CTk.CTkEntry(frame2, textvariable=f2_var_LetterTried)
f2_ent_WordEntry.grid(column=1, row=4, sticky="w")

# Button to try entry
f2_btn_try = CTk.CTkButton(frame2, text="Try", command=game)
f2_btn_try.grid(column=2, row=4, sticky="w")





#------------------------------- END-GAME WINDOW -------------------------------#
# Create 3RD frame
frame3 = CTk.CTkFrame(root)
frame3.grid(column=0, row=0, sticky="news")

# Label for drawings
f3_lb_drawing = CTk.CTkLabel(frame3, font=("Consolas", 12), justify="left")
f3_lb_drawing.grid(column=0, row=0, rowspan=4)

# Label for title
f3_lb_title = CTk.CTkLabel(frame3, text="THE HANGMAN", font=("Consolas", 20))
f3_lb_title.grid(column=1, row=0, columnspan=2)

# Label for signature
f3_lb_signature = CTk.CTkLabel(frame3, text="By Thibaud D.", font=('Consolas', 9))
f3_lb_signature.grid(column=1, row=1, columnspan=2, sticky="n")

# Label for end message
f3_lb_EndMessage = CTk.CTkLabel(frame3, font=("Consolas", 12), wraplength=500, justify="left")
f3_lb_EndMessage.grid(column=1, row=2, columnspan=500)

# Labels for letters tried
f3_lb_LettersTried = CTk.CTkLabel(frame3, textvariable=f2_var_LettersTried, font=("Consolas", 12), justify="left", )
f3_lb_LettersTried.grid(column=2, row=3)
CTk.CTkLabel(frame3, text="Previous tries:", font=("Consolas", 12), justify="right").grid(column=1, row=3, sticky="e")

# Button to try entry
button_try = CTk.CTkButton(frame3, text="Start a new game", command=new_game)
button_try.grid(column=2, row=4, sticky="w")





frame1.tkraise()
root.mainloop()

Solution

  • You have override the title() function by the following line:

    root.title = "The Hangman"
    

    So when tkinter calls root.title() internally, it raises exception.

    Change the problem line to:

    root.title("The Hangman")