Description: So I am using customtkinter library instead of tkinter, the issue I am facing is that I want my tkinter app to be withdrawn when closed if the minimize window checkbox is checked in settings of the app. The first time I perform this action the app withdraws as well as pystray icon appears in the taskbar, when I open the window again using the "open app" button the app deiconifies but when I try the same method second time I get an error.
Source Code: Below is my code through which I am implementing this operation
def save_checkbox_state(state):
with open('.\\_internal\\src\\checkbox_state.pickle', 'wb') as file:
pickle.dump(state, file)
def read_checkbox_state():
try:
with open('.\\_internal\\src\\checkbox_state.pickle', 'rb') as file:
state = pickle.load(file)
# Modify this section according to your UI framework
if state == 'on':
checkbox.select()
check_var.set('on')
else:
checkbox.deselect()
check_var.set('off')
except FileNotFoundError:
return None
def app_destroy():
icon.stop()
app.destroy()
def open_app():
icon.stop()
app.after(0,app.deiconify())
image = Image.open(".\\_internal\\assets\\ico.png")
menu=pystray.Menu(pystray.MenuItem("Open enigma:guard",open_app),pystray.MenuItem("Exit", app_destroy))
icon = pystray.Icon('enigma:guard', image, "My system tray icon", menu)
def destroy_window():
checkbox_state = check_var.get()
save_checkbox_state(checkbox_state)
if checkbox_state == 'on':
app.withdraw()
icon.run()
else:
app.destroy()
icon.stop()
settings_minimize_frame = customtkinter.CTkLabel(settings_frame_win,text="",image=settings_minimize)
settings_minimize_frame.place(x=420,y=121)
check_var = customtkinter.StringVar(value="off")
checkbox = customtkinter.CTkCheckBox(settings_minimize_frame, text="", variable=check_var, onvalue="on", offvalue="off",width=30,height=35,checkbox_height=35,checkbox_width=35,border_color="#1FFFA9",bg_color="#000000")
checkbox.place(x=1078,y=33)
app.protocol("WM_DELETE_WINDOW", destroy_window)
Error: Below is the error I am facing when I close the window and it minimizes and I reopen it the second time:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\[hidden]\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 1948, in __call__
return self.func(*args)
^^^^^^^^^^^^^^^^
File "D:\python_encryptor_decryptor\project2\enigma_guard.py", line 1228, in destroy_window
icon.run()
File "C:\Users\[hidden]\AppData\Local\Programs\Python\Python311\Lib\site-packages\pystray\_base.py", line 212, in run
self._run()
File "C:\Users\[hidden]\AppData\Local\Programs\Python\Python311\Lib\site-packages\pystray\_win32.py", line 120, in _run
self._hwnd = self._create_window(self._atom)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\[hidden]\AppData\Local\Programs\Python\Python311\Lib\site-packages\pystray\_win32.py", line 244, in _create_window
hwnd = win32.CreateWindowEx(
^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\[hidden]\AppData\Local\Programs\Python\Python311\Lib\site-packages\pystray\_util\win32.py", line 204, in _err
raise ctypes.WinError()
OSError: [WinError 6] The handle is invalid.
Already tried: I tried to destroy the window instead of withdrawing it but that makes the useability a lot awkward since I am giving a splash screen at startup.
Solution: So after a lots of debugging I finally found the issue, the code of the pystray works in a loop in a certain way that it works only once. To repeat the loop everytime I created the function inside the destroy_window function itself so everytime the window closed the function runs creating the icon from pystray from scratch.
Source Code:
def save_checkbox_state(state):
with open('.\\_internal\\src\\checkbox_state.pickle', 'wb') as file:
pickle.dump(state, file)
def read_checkbox_state():
try:
with open('.\\_internal\\src\\checkbox_state.pickle', 'rb') as file:
state = pickle.load(file)
if state == 'on':
checkbox.select()
check_var.set('on')
else:
checkbox.deselect()
check_var.set('off')
except FileNotFoundError:
return None
def destroy_window():
checkbox_state = check_var.get()
save_checkbox_state(checkbox_state)
if checkbox_state == 'on':
app.withdraw()
def open_app():
icon.stop()
app.after(0,app.deiconify)
def app_destroy():
icon.stop()
app.destroy()
menu=pystray.Menu(pystray.MenuItem("Open enigma:guard",open_app),pystray.MenuItem("Exit", app_destroy))
image = Image.open(".\\_internal\\assets\\ico.png")
icon = pystray.Icon('enigma:guard', image, "My system tray icon", menu)
icon.run()
else:
app.destroy()
icon.stop()
settings_minimize_frame = customtkinter.CTkLabel(settings_frame_win,text="",image=settings_minimize)
settings_minimize_frame.place(x=420,y=121)
check_var = customtkinter.StringVar(value="off")
checkbox = customtkinter.CTkCheckBox(settings_minimize_frame, text="", variable=check_var, onvalue="on", offvalue="off",width=30,height=35,checkbox_height=35,checkbox_width=35,border_color="#1FFFA9",bg_color="#000000")
checkbox.place(x=1078,y=33)
app.protocol("WM_DELETE_WINDOW", destroy_window)