How to properly use run_detached() and stop() in pystray?

2.2k Views Asked by At

I'm trying to use pystray without blocking the main thread. Based on the pystray docs we can use the function run_detached() to start without blocking.

I'm using pystray on windows so, apparently I don't need to pass any argument to run_detached() to work.

The first thing I tried is to run this code:

import pystray
from pystray import MenuItem as item
from PIL import Image, ImageTk

def show_window(icon):
    print('Test')


def quit_window(icon):
    icon.stop()
    

icon = 'icon.ico'
image=Image.open(icon)

menu=pystray.Menu(item('Show', show_window, default=True), item('Quit', quit_window))
icon=pystray.Icon("name", image, "My System Tray Icon", menu)
icon.run_detached()

But I received this error:

Exception in thread Thread-2:
Traceback (most recent call last):
  File "...\lib\threading.py", line 973, in _bootstrap_inner
    self.run()
  File "...\lib\threading.py", line 910, in run
    self._target(*self._args, **self._kwargs)
  File "...\lib\site-packages\pystray\_base.py", line 384, in <lambda>
    threading.Thread(target=lambda: self.run(setup)).start()
NameError: name 'setup' is not defined

So I tried to bypass this error by changing the line 384 in _base.py removing the setup variable

#threading.Thread(target=lambda: self.run(setup)).start() 
threading.Thread(target=lambda: self.run()).start()

The code worked like expected and created the tray icon with the menu buttons working properly.

The problem is when I press "Quit" because the stop() function is not working like when I use icon.run(). The thread appears to keep running and the tray icon stay frozen and the program don't end.

Is there another way to make this work properly?

EDIT: I found this issue in the official git repository LINK and it appears to be a bug already reported. I want to know if is possible to make a workaround.

2

There are 2 best solutions below

1
On

Modifying the stop() function further to exit from the thread using os._exit will work if you don't need the calling thread to remain available.

0
On

Based on https://github.com/moses-palmer/pystray/issues/94 I found changing

threading.Thread(target=lambda: self.run()).start()

into

threading.Thread(deamon=true,target=lambda: self.run()).start()

works for me. My icon.stop() actually closes the thread now