TomSchimansky / TomSchimansky/CustomTkinter

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

Open
#822 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug question
Dominant language
Python
Stars
13.6k
Forks
1.2k
PR merge metrics
No merged PRs in 30d

Description

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()

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the supplied minimal reproduction and traceback, then inspect customtkinter/windows/widgets/ctk_label.py, especially CTkLabel initialization and _update_image. Reproduce the pyimage4 failure while the other window is destroyed from a thread, and determine a maintainable project-level correction or documented limitation. Done means the reproduction no longer raises the TclError under the supported window lifecycle.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
desktop
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.