How to destroy the icon tray when the program ends

1.4k Views Asked by At

I have a class that is called when my .py program starts and it creates an Icon tray in the windows task bar. In it, there is the option quit, that is mapped to the function kill_icon_tray from my class, that should terminate the Icon and then finish my program.

This is the class (some methods were ommited as they are not needed):

from infi.systray import SysTrayIcon

class Tray_icon_controller:

    def __init__(self):
        self.menu_options = (("Open Chat Monitor", None, self.open_chat),)
        self.systray = SysTrayIcon("chat.ico", "Engineer Reminder", self.menu_options, on_quit=self.kill_icon_tray);

    def init_icon_tray(self):
        self.systray.start();

    def kill_icon_tray(self, systray):
        self.systray.shutdown()

But this is returning me the following exception whenever I click on quit in the Icon tray:

$ py engineer_reminder.py
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 237, in 'calling callback function'
  File "C:\Users\i866336\AppData\Local\Programs\Python\Python38-32\lib\site-packages\infi\systray\traybar.py", line 79, in WndProc
    self._message_dict[msg](hwnd, msg, wparam.value, lparam.value)
  File "C:\Users\i866336\AppData\Local\Programs\Python\Python38-32\lib\site-packages\infi\systray\traybar.py", line 195, in _destroy
    self._on_quit(self)
  File "C:\Users\i866336\Documents\GitHub\chat_reminder\cl_tray_icon_controller.py", line 17, in kill_icon_tray
    self.systray.shutdown()
  File "C:\Users\i866336\AppData\Local\Programs\Python\Python38-32\lib\site-packages\infi\systray\traybar.py", line 123, in shutdown
    self._message_loop_thread.join()
  File "C:\Users\i866336\AppData\Local\Programs\Python\Python38-32\lib\threading.py", line 1008, in join
    raise RuntimeError("cannot join current thread")
RuntimeError: cannot join current thread

I tried to modify the method kill_icon_tray to this instead, but it threw the same exception:

    def kill_icon_tray(self, systray):
        self.systray.shutdown()

As per infi.systray documentation, I am doing it correctly:

To destroy the icon when the program ends, call systray.shutdown()

So I'm not sure what I'm missing here... could anyone assist? Thanks!

2

There are 2 best solutions below

0
doomed824 On

experienced the same problem as you, you could have found the solution but for anyone else that has the same problem.

What fixed it for me is changing the systray.shutdown() to SysTrayIcon.shutdown

    def kill_icon_tray(systray):
    SysTrayIcon.shutdown

Hope this helps

0
LamerLink On

In case anyone else ever has this issue, patching the SysTrayIcon.shutdown method is what fixed it for me.

from infi.systray import SysTrayIcon
from infi.systray.traybar import PostMessage, WM_CLOSE


# Remove the thread.join from the SysTrayIcon.shutdown function
def custom_shutdown(self):
    if not self._hwnd:
        return      # not started
    PostMessage(self._hwnd, WM_CLOSE, 0, 0)
    #self._message_loop_thread.join()

# Overwrite shutdown function
SysTrayIcon.shutdown = custom_shutdown

# Use it as normal
sys_tray.shutdown()

I'm not certain why this issue happens. In my situation, I had daemons that were created and finished during the SysTrayIcon object's life, but before they were created I could SysTrayIcon._message_loop_function.join() successfully to the MainThread, yet after I could not...

Anyhow, simply calling PostMessage(self._hwnd, WM_CLOSE, 0, 0) alone seems to work, too.