Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SysTrayIcon.shutdown produces threading join exception #32

Closed
Ari24-cb24 opened this issue Oct 30, 2020 · 6 comments
Closed

SysTrayIcon.shutdown produces threading join exception #32

Ari24-cb24 opened this issue Oct 30, 2020 · 6 comments

Comments

@Ari24-cb24
Copy link

Ari24-cb24 commented Oct 30, 2020

Hey there,

Same issue as the one 2 years ago,

SystrayIcon.shutdown() produces a threading join error.

  File "C:\Users\bobmy\AppData\Local\Programs\Python\Python37\lib\site-packages\infi\systray\traybar.py", line 123, in shutdown
    self._message_loop_thread.join()
  File "C:\Users\bobmy\AppData\Local\Programs\Python\Python37\lib\threading.py", line 1041, in join
    raise RuntimeError("cannot join current thread")
RuntimeError: cannot join current thread```

Greets
@Ari24-cb24
Copy link
Author

Found out, that this only happens, when passing on_quit into SystrayIcon()

@Ari24-cb24
Copy link
Author

Ari24-cb24 commented Oct 30, 2020

Fixed it with removing

menu_options = menu_options + (('Quit', None, SysTrayIcon.QUIT),)

in traybar.py and also building my own closing function with

import win32_adapter

menu = (
        ("Open", "./img/icon.ico", lambda st: print("Test")),
        ("Exit", None, self.__shutdown),
)

    self.stray = SysTrayIcon("./img/icon.ico", "Name", menu_options=menu)

def __shutdown(self, *args):
    win32_adapter.DestroyWindow(self.stray._hwnd)
    print("CLOSE")
    sys.exit(0)

@pythonmcpi
Copy link

pythonmcpi commented Nov 7, 2020

Using SysTrayIcon.shutdown() in the on_quit will error because the event monitor thread calls your on_quit function, which calls the shutdown function, which tries the join the event loop thread (to wait for it to exit before returning). But if a thread joins itself to wait for it to exit, it would block forever, so the threading module just raises an exception. A way to fix this error without directly editing module code is to signal another thread to run the shutdown function. In my case, I was able to use the main thread, and just tell it to block until it gets the signal.
TL;DR Call the shutdown() function in another thread to avoid the exception

@Ari24-cb24
Copy link
Author

Hey there again, sorry for the late answer,

I already tried calling the shutdown() function in another thread and I can't block the main Thread because I have other things running on the Mainthread.

But yeah, thanks for answering

@dibakarbose
Copy link

dibakarbose commented Nov 6, 2022

#32 (comment)

Call the shutdown() function in another thread to avoid the exception

Hi @pythonmcpi , I know this is very late comment but I am facing a similar situation.

Can you please provide a code example with respect to your comment. That would be really helpful

@pythonmcpi
Copy link

pythonmcpi commented Nov 17, 2022

@dibakarbose I can try looking for my original project, but I don't know if I'll be able to find it. I have too many abandoned projects :(

EDIT
So I somehow found it in the first project folder I looked in...

The code below is from a project that I ended up abandoning on November 7, 2020, exactly 2 hours and 5 minutes after writing my original comment. It probably requires an icon.ico file to be present in the directory. (My original ico file can be found here - Github doesn't let me attach .ico files) I have no idea if my old code works or not, and the code quality isn't great.

Good luck.

#import pystray
import contextlib
with contextlib.redirect_stdout(None):
    import pygame
import sys
import subprocess
import os
from PIL import Image, ImageDraw
from infi.systray import SysTrayIcon as sti
import time

#from pystray import Icon as picon, Menu as pmenu, MenuItem as pitem

class Launcher(object):
    def __init__(self, width=860, height=680):
##        self.icon = picon('Launcher', self.generate_icon(), menu=pmenu(
##            pitem(
##                'Quit',
##                self.quit
##                )
##            )
##                          )
        self.width = width
        self.height = height
        self.hidden = False
        self.menu = (("Toggle Visibility", None, lambda e: self.toggle_visible()),)
        self.icon = sti("icon.ico", "Launcher", self.menu, on_quit=lambda e: self.quit())
        pygame.init()

    def run(self):
        self.running = True
        #self.icon.run()
        self.icon.start()
        self.screen = pygame.display.set_mode((self.width, self.height), pygame.NOFRAME)
        self.mainloop()

    def generate_icon(self):
        # Checkerboard Pattern
        width = 32
        height = 32
        color1 = (255, 255, 255)
        color2 = (0, 0, 0)
        icon = Image.new('RGB', (width, height), color1)
        dc = ImageDraw.Draw(icon)
        dc.rectangle(
            (width // 2, 0, width, height // 2),
            fill=color2)
        dc.rectangle(
            (0, height // 2, width // 2, height),
            fill=color2)

        return icon

    def toggle_visible(self):
        if not self.running:
            return
        self.hidden = not self.hidden
        if self.hidden:
            pygame.display.iconify()
        else:
            pygame.display.deiconify()

    def mainloop(self):
        while self.running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    
        self.icon.shutdown()

    def quit(self):
        self.running = False
        #self.icon.stop()
        #self.icon.shutdown() # Shutdown has a line that joins the event monitor thread, but this function is called by the monitor thread, causing a RuntimeError. This line can be put in mainloop (which runs on main thread)
        pygame.quit()

if __name__ == "__main__":
    launcher = Launcher()
    launcher.run()
    #launcher.generate_icon().save("icon.ico", "ICO") # Save icon file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants