2

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!

Pedro Accorsi
  • 765
  • 1
  • 8
  • 21
  • It fails because there are threads that you can't quit or join at the time your code runs shutdown. You should write ```sys.exit(0)``` into your threading functions bot success and exceptions to release hanging threads. – furkanayd Dec 09 '19 at 04:58
  • the problem is that the thread is created by the ```infi.systray``` api, which means I shouldnt really modify anything there.. the issue should be with my current implementation, so where exactly I should use this ```sys.exit(0)```? – Pedro Accorsi Dec 09 '19 at 18:32

2 Answers2

0

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

doomed824
  • 3
  • 1
0

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.

LamerLink
  • 111
  • 6