moses-palmer / pystray

GNU General Public License v3.0
482 stars 59 forks source link

pystray run_detached not working on MacOS m2 chip , what is the right way to use to keep program live in background? #138

Open xu1jia2qi3 opened 1 year ago

xu1jia2qi3 commented 1 year ago

I am facing a problem when using pystray on MacOS m2 (Ventura 13.2) , It is working fine on windows. On MacOS, It can minimize to task tray but when click icon to resume back, show the error message below

Fatal Python error: PyEval_RestoreThread: the function must be called with the GIL held, but the GIL is released (the current Python thread state is NULL) Python runtime state: initialized

Current thread 0x00000001fed50100 (most recent call first): File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/tkinter/init.py", line 1458 in mainloop File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/customtkinter/windows/ctk_tk.py", line 158 in mainloop File "/Users/dr.inbox/Desktop/AutoOCR_GUI/MAC_Mchip_version/app_mac.py", line 335 in

Extension modules: objc._objc, CoreFoundation._inlines, CoreFoundation._CoreFoundation, Foundation._Foundation, Foundation._inlines, AppKit._inlines, AppKit._AppKit, PIL._imaging, _scproxy, objc._machsignals, PIL._imagingtk (total: 11) zsh: abort /usr/local/bin/python3

It has nothing wrong with my tkinkter code i think, even with the most simple tkinter example, it is still show error.

xu1jia2qi3 commented 1 year ago

import tkinter import os from pystray import MenuItem as item import pystray from PIL import Image

window = tkinter.Tk() window.minsize(600,450) window.title("Welcome to LikeGeeks app")

lbl = tkinter.Label(window, text="hello world")

lbl.grid(column=0, row=0)

def quit_window(icon, item): icon.visible = False icon.stop() window.quit()

def show_window(icon, item): icon.stop() window.after(0,window.deiconify)

def withdraw_window():
window.withdraw()

  image_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "images")
  icon_path = os.path.join(image_path, "noname.png") 
  image = Image.open(icon_path)

  menu = (item('Open', show_window,default=True), item('Quit', quit_window))
  icon = pystray.Icon("name", image, "Dr.inbox", menu)
  # run pystray in seperate thread so keep it alive in background
  icon.run_detached()

window.protocol('WM_DELETE_WINDOW', withdraw_window) window.mainloop()

xu1jia2qi3 commented 1 year ago

one finding for the problem is run_detached(), if i use run(), no problem. however I need the program to be kept alive in the background, so I use run_detached() in windows. but it seems not working on mac

moses-palmer commented 1 year ago

Thank you for your report, and I apologise for this very late response.

From the error, I would guess that there may be a bug in the objc or tkinter package on Apple silicon; pystray does not contain any native code, and thus does not manipulate the GIL. I am afraid that I cannot provide any input, as I do not have access to neither the hardware nor macOS. Have you tried updating objc or tkinter?

wanghaisheng commented 1 year ago

@moses-palmer
can you check this code https://github.com/wanghaisheng/tkinter-i18n-app-starter/blob/main/main.py

Fatal Python error: PyEval_RestoreThread: the function must be called with the GIL held, but the GIL is released (the current Python thread state is NULL)
Python runtime state: initialized

Current thread 0x000000010b702dc0 (most recent call first):
  File "/Users/wenke/miniconda3/lib/python3.9/tkinter/__init__.py", line 1429 in mainloop

either run or run_detached wont work on my old macmini

but this one works well

from pystray import MenuItem as item
import pystray
from PIL import Image
import tkinter as tk

def quit_window(icon, item):
    icon.stop()
    window.destroy()

def show_window(icon, item):
    icon.stop()
    window.after(0,window.deiconify)

def withdraw_window():  
    window.withdraw()
    image = Image.open("assets/icon.ico")
    menu = (item('Quit', quit_window), item('Show', show_window))
    icon = pystray.Icon("name", image, "title", menu)
    icon.run()

if __name__ == '__main__':
   global root
   window = tk.Tk()
   window.title("Welcome")

   window.protocol('WM_DELETE_WINDOW', withdraw_window)
   window.mainloop()