TomSchimansky / CustomTkinter

A modern and customizable python UI-library based on Tkinter
MIT License
11.58k stars 1.08k forks source link

CTkImage on setting inside a label throws (_tkinter.TclError: image "pyimage4" doesn't exist) error #822

Open TriDEntApollO opened 1 year ago

TriDEntApollO commented 1 year ago

I am working on pycharm so the error is not Spyder specific as some were saying on stackoverflow. i tries runnign the program on the global system terminal still the same error.

Code

class EyeButton:
    def __init__(self, master: customtkinter.CTkEntry, hide_char: str):
        self.master = master
        self.hide_char = hide_char
        self.unhide = customtkinter.CTkImage(dark_image=Image.open("Eye.png"), size=(22, 18))
        self.hide = customtkinter.CTkImage(dark_image=Image.open("NotEye.png"), size=(24, 20))
        self.buttonLabel = customtkinter.CTkLabel(master=self.master, font=('Roboto', 12), text='', image=self.unhide, fg_color='transparent', height=self.master.winfo_height()-10) # Traces back error to this line while defining the image self.unhide

    def initialize(self):
        self.buttonLabel.bind("<Button-1>", lambda e: self.state())
        self.buttonLabel.place(relx=0.99, rely=0.5, anchor='e')
        self.tracker = 1

    def state(self):
        if self.tracker == 1:
            self.master.configure(show="")
            self.buttonLabel.configure(image=self.hide)
            self.tracker = False
        else:
            self.master.configure(show=self.hide_char)
            self.buttonLabel.configure(image=self.unhide)
            self.tracker = True
        self.tracker *= 1

def login_box():
    ConnectionWin.after(200, lambda: threading.Thread(target=ConnectionWin.destroy).start())
    box = customtkinter.CTk()
    box.title("PyChat Login")
    box.iconbitmap('PyChat.ico')
    w = 400
    h = 410
    box.geometry(get_geo(box, w, h))
    frame = customtkinter.CTkFrame(master=box)
    frame.pack(pady=20, padx=40, fill="both", expand=True)
    uname = customtkinter.CTkEntry(master=frame, placeholder_text="Username", width=240)
    uname.pack(pady=12, padx=10)
    passwd = customtkinter.CTkEntry(master=frame, placeholder_text="Password", show="•", width=240)
    passwd.pack(pady=12, padx=10)
    box.update()
    showButton = EyeButton(master=passwd, hide_char="•") #Getting error while creating child
    showButton.initialize()
    LoginButton = customtkinter.CTkButton(master=frame, text="Sign in", command=login)
    LoginButton.pack(pady=12, padx=10)
    box.update()
    box.mainloop()

The class is used to create a eye button that hides and unhides password inside the CTkEntry box. Previously it used to work fine unless i added a loading screen before the login screen that gets destroyed inside the login_box function via a thread (the ConnectionWin) but since the button is initilized before the window gets destroyed it trows the following error.

Error

Traceback (most recent call last):
  File "C:\Users\Rishi\PycharmProjects\PyChat\test.py", line 253, in connect
    login_box()
  File "C:\Users\Rishi\PycharmProjects\PyChat\test.py", line 180, in login_box
    showButton = EyeButton(master=passwd, hide_char="•")
  File "C:\Users\Rishi\PycharmProjects\PyChat\test.py", line 16, in __init__
    self.buttonLabel = customtkinter.CTkLabel(master=self.master, font=('Roboto', 12), text='', image=self.unhide, fg_color='transparent', height=self.master.winfo_height()-10)
  File "C:\Users\Rishi\PycharmProjects\PyChat\venv\lib\site-packages\customtkinter\windows\widgets\ctk_label.py", line 93, in __init__
    self._update_image()
  File "C:\Users\Rishi\PycharmProjects\PyChat\venv\lib\site-packages\customtkinter\windows\widgets\ctk_label.py", line 130, in _update_image
    self._label.configure(image=self._image.create_scaled_photo_image(self._get_widget_scaling(),
  File "C:\Program Files\Python310\lib\tkinter\__init__.py", line 1675, in configure
    return self._configure('configure', cnf, kw)
  File "C:\Program Files\Python310\lib\tkinter\__init__.py", line 1665, in _configure
    self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: image "pyimage4" doesn't exist

I tried decreasing the after() time for destroying the window but that results in another RuntimError mainthread is not inside mainloop and i also tried to disable the garbage collector so that it doesnt collect the vraible data and clear it by doing g.disable() and checked the gc state by gc.isenabled() which returned false but still the same error.

Code to replicate the error

Use a basic server to accept the connection to initiate the login window

import time
import traceback
import PIL.ImageTk
from PIL import Image
import tkinter as tk
import threading
import customtkinter
import socket as sock

class EyeButton:
    def __init__(self, master: customtkinter.CTkEntry, hide_char: str):
        self.master = master
        self.hide_char = hide_char
        self.unhide = customtkinter.CTkImage(dark_image=Image.open("Eye.png"), size=(22, 18))
        self.hide = customtkinter.CTkImage(dark_image=Image.open("NotEye.png"), size=(24, 20))
        self.buttonLabel = customtkinter.CTkLabel(master=self.master, text='', image=self.unhide, fg_color='transparent', height=self.master.winfo_height()-10)
        self.buttonLabel.image = self.unhide

    def initialize(self):
        self.buttonLabel.bind("<Button-1>", lambda e: self.state())
        self.buttonLabel.place(relx=0.99, rely=0.5, anchor='e')
        self.tracker = 1

    def state(self):
        if self.tracker == 1:
            self.master.configure(show="")
            self.buttonLabel.configure(image=self.hide)
            self.tracker = False
        else:
            self.master.configure(show=self.hide_char)
            self.buttonLabel.configure(image=self.unhide)
            self.tracker = True
        self.tracker *= 1

class HyperLabel(customtkinter.CTkLabel):
    def __init__(self, *args, **kwargs):
        customtkinter.CTkLabel.__init__(self, *args, **kwargs)
        self.bind('<Enter>', lambda e: self.on_hover())
        self.bind('<Leave>', lambda e: self.on_leave())

    def on_hover(self):
        self.configure(text_color="#54a2de", font=('Roboto', 12, 'underline'))

    def on_leave(self):
        self.configure(text_color="#1f6aa5", font=('Roboto', 12))

class HyperImage(customtkinter.CTkLabel):
    def __init__(self, on: customtkinter.CTkImage, off: customtkinter.CTkImage, *args, **kwargs):
        self.on = on
        self.off = off
        customtkinter.CTkLabel.__init__(self, *args, **kwargs, image=off)
        self.bind('<Enter>', lambda e: self.on_hover())
        self.bind('<Leave>', lambda e: self.on_leave())

    def on_hover(self):
        self.configure(image=self.on)

    def on_leave(self):
        self.configure(image=self.off)

def get_geo(master, width, height):
    screen_width = master.winfo_screenwidth()
    screen_height = master.winfo_screenheight()
    x = (screen_width / 2) - (width / 2)
    y = (screen_height / 2) - (height / 2)
    position = '%dx%d+%d+%d' % (width, height, x, y)
    return position

def login_box():
    ConnectionWin.after(200, lambda: threading.Thread(target=ConnectionWin.destroy).start())
    box = customtkinter.CTk()
    box.title("PyChat Login")
    box.iconbitmap('PyChat.ico')
    w = 400
    h = 410
    box.geometry(get_geo(box, w, h))
    box.minsize(w, h)
    box.maxsize(w, h)
    frame = customtkinter.CTkFrame(master=box)
    frame.pack(pady=20, padx=40, fill="both", expand=True)
    label = customtkinter.CTkLabel(master=frame, text="Login", font=("Roboto", 24))
    label.pack(pady=(12, 26), padx=10)
    warning = customtkinter.CTkLabel(master=frame, text="", font=("Roboto", 12), text_color="#d94b58")
    warning.pack(pady=0, padx=0)
    warning.place(anchor='w', relx=0.135, rely=0.18)
    uname = customtkinter.CTkEntry(master=frame, placeholder_text="Username", width=240)
    uname.pack(pady=12, padx=10)
    passwd = customtkinter.CTkEntry(master=frame, placeholder_text="Password", show="•", width=240)
    passwd.pack(pady=12, padx=10)
    box.update()
    time.sleep(0.2)
    showButton = EyeButton(master=passwd, hide_char="•")
    showButton.initialize()
    LoginButton = customtkinter.CTkButton(master=frame, text="Sign in", command=login)
    LoginButton.pack(pady=12, padx=10)
    remember = customtkinter.CTkCheckBox(master=frame, text="Stay Logged In")
    remember.pack(pady=12, padx=10)
    text = customtkinter.CTkLabel(master=frame, text="New to Pychat?", text_color="silver", font=('Roboto', 12))
    signup = HyperLabel(master=frame, text="Sign up", text_color="#1f6aa5", font=('Roboto', 12))
    signup.bind("<Button-1>", lambda e: register())
    text.pack(pady=8, padx=2)
    signup.pack(pady=8, padx=2)
    box.update()
    box.mainloop()

def splash():
    global SplashWin
    SplashWin = customtkinter.CTk()
    SplashWin.overrideredirect(True)
    w = 350
    h = 180
    SplashWin.geometry(get_geo(SplashWin, w, h))
    icon = customtkinter.CTkImage(dark_image=Image.open("PyChat.png"), size=(98, 98))
    logo = customtkinter.CTkLabel(master=SplashWin, text="", image=icon)
    logo.place(anchor='center', relx=0.3, rely=0.36)
    label = customtkinter.CTkLabel(master=SplashWin, text="PyChat", font=('Roboto', 40))
    label.pack(pady=0, padx=0)
    label.place(anchor='w', relx=0.45, rely=0.36)
    loading = customtkinter.CTkLabel(master=SplashWin, text="Loading...", font=('Roboto', 16), text_color='silver')
    loading.place(relx=0.5, rely=0.7, anchor='center')
    loadbar = customtkinter.CTkProgressBar(master=SplashWin, corner_radius=0, width=350, border_width=0, fg_color='#1f6aa5', progress_color='#54a2de', indeterminate_speed=1.2)
    loadbar.pack(side=tk.BOTTOM, pady=0, padx=0)
    loadbar.configure(mode="indeterminnate")
    loadbar.start()
    SplashWin.after(4000, connect)
    SplashWin.mainloop()

def fail_box():
    ConnectionWin.after(500, lambda: threading.Thread(target=ConnectionWin.destroy).start())
    global popup
    popup = customtkinter.CTk()
    popup.title("PyChat Connection Error")
    popup.iconbitmap('PyChat.ico')
    w = 400
    h = 240
    popup.geometry(get_geo(popup, w, h))
    popup.minsize(w, h)
    popup.maxsize(w, h)
    frame = customtkinter.CTkFrame(master=popup)
    frame.pack(pady=20, padx=40, fill="both", expand=True)
    label = customtkinter.CTkLabel(master=frame, text="Connection Failed!", font=('Roboto', 28))
    label.pack(padx=20, pady=20)
    error = customtkinter.CTkLabel(master=frame, text="Unable to Connect to Server\nPlease try again later.", text_color='silver', font=('Roboto', 14))
    error.pack(padx=20, pady=10)
    button = customtkinter.CTkButton(master=frame, text="OK", width=70, command=lambda: popup.destroy())
    button.pack(padx=20, pady=20)
    popup.mainloop()

def connect():
    SplashWin.destroy()
    global client
    th = threading.Thread(target=connect_box)
    th.daemon = True
    th.start()
    client = sock.socket(sock.AF_INET, sock.SOCK_STREAM)
    time.sleep(3)
    try:
        client.connect(('127.0.0.1', 5574))
        loadbar1.stop()
        ConnectionWin.withdraw()
        login_box()
    except ConnectionRefusedError:
        loadbar1.stop()
        ConnectionWin.withdraw()
        fail_box()
    except ConnectionError:
        loadbar1.stop()
        ConnectionWin.withdraw()
        fail_box()
    except Exception:
        print(traceback.format_exc())
        exit()

def connect_box():
    global ConnectionWin, loadbar1
    ConnectionWin = customtkinter.CTk()
    ConnectionWin.title("PyChat")
    ConnectionWin.iconbitmap('PyChat.ico')
    w = 400
    h = 370
    screen_width = ConnectionWin.winfo_screenwidth()
    screen_height = ConnectionWin.winfo_screenheight()
    x = (screen_width / 2) - (w / 2)
    y = (screen_height / 2) - (h / 2)
    position = '%dx%d+%d+%d' % (w, h, x, y)
    ConnectionWin.geometry(position)
    ConnectionWin.minsize(w, h)
    ConnectionWin.maxsize(w, h)
    icon = customtkinter.CTkImage(dark_image=Image.open("PyChat.png"), size=(98, 98))
    logo = customtkinter.CTkLabel(master=ConnectionWin, text="", image=icon)
    logo.place(anchor='center', relx=0.3, rely=0.14)
    label = customtkinter.CTkLabel(master=ConnectionWin, text="PyChat", font=('Roboto', 40))
    label.pack(pady=0, padx=0)
    label.place(anchor='w', relx=0.45, rely=0.14)
    frame = customtkinter.CTkFrame(master=ConnectionWin)
    frame.pack(pady=(100, 20), padx=40, fill="both", expand=True)
    label = customtkinter.CTkLabel(master=frame, text="Connecting to server...", font=('Roboto', 24))
    label.pack(padx=40, pady=40)
    loadbar1 = customtkinter.CTkProgressBar(master=frame, corner_radius=20, width=240, progress_color='#54a2de', indeterminate_speed=0.8)
    loadbar1.pack(pady=0, padx=0)
    loadbar1.configure(mode="indeterminnate")
    loadbar1.start()
    Github = customtkinter.CTkImage(dark_image=Image.open("master/sources/github.png"), size=(124, 64))
    GithubHighlight = customtkinter.CTkImage(dark_image=Image.open("master/sources/github_highlight.png"), size=(124, 64))
    GitLabel = HyperImage(master=ConnectionWin, text="", width=24, height=30, cursor="hand2", on=GithubHighlight, off=Github)
    GitLabel.bind("<Button-1>", lambda e: print('https://github.com/TriDEntApollO?tab=repositories'))
    GitLabel.pack(pady=0, padx=20)
    developed = customtkinter.CTkLabel(master=ConnectionWin, text="Developed by TriDEntApollO", text_color="silver")
    developed.pack(pady=(0, 20), padx=20)
    ConnectionWin.mainloop()
    return

customtkinter.set_appearance_mode("Dark")
customtkinter.set_default_color_theme("dark-blue")
splash()

Server code to accept the connection

import socket as sock

server = sock.socket(sock.AF_INET, sock.SOCK_STREAM)
name = sock.gethostname()
host, port = sock.gethostbyname(name), 5574
server.bind((host, port))
server.listen(1)
client, address = server.accept()
Swastik2442 commented 1 year ago

I am having the same error while running the code on a Jupyter Notebook, it works fine on a normal .py file. Any solution?

TriDEntApollO commented 1 year ago

I still haven't found any fix to the issue but i have seen this issue mostly occurs on Spyder or Jupiter notebook it's something with the ide/editor using their own shell rather use the global system shell/terminal

This probably won't be of much help but you can try also the issue is raised only when a previous window was created but not destroyed so you could try destroying the previous window first as tkinter only allows one root window at a time.

On Mon, 2 Jan 2023, 2:47 pm Swastik Kulshreshtha, @.***> wrote:

I am having the same error while running the code on a Jupyter Notebook, it works fine on a normal .py file. Any solution?

— Reply to this email directly, view it on GitHub https://github.com/TomSchimansky/CustomTkinter/issues/822#issuecomment-1368767908, or unsubscribe https://github.com/notifications/unsubscribe-auth/AQHGKDHYUJ7COCZTWOSARPLWQKMJTANCNFSM6AAAAAAS2KDJAE . You are receiving this because you authored the thread.Message ID: @.***>