Skip to content

Commit ac29578

Browse files
committed
Fixed the tray icon not appearing on Mac
1 parent 321f4e8 commit ac29578

File tree

1 file changed

+47
-12
lines changed

1 file changed

+47
-12
lines changed

src/run_tribler.py

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
import sys
44
import traceback
5+
from typing import TYPE_CHECKING
6+
7+
if TYPE_CHECKING:
8+
from pystray import Icon
59

610

711
def show_error(exc: Exception, shutdown: bool = True) -> None:
@@ -21,11 +25,11 @@ def show_error(exc: Exception, shutdown: bool = True) -> None:
2125
import win32api
2226

2327
win32api.MessageBox(0, text, title)
24-
elif sys.platform == 'Linux':
28+
elif sys.platform == 'linux':
2529
import subprocess
2630

2731
subprocess.Popen(['xmessage', '-center', text]) # noqa: S603, S607
28-
elif sys.platform == 'Darwin':
32+
elif sys.platform == 'darwin':
2933
import subprocess
3034

3135
subprocess.Popen(['/usr/bin/osascript', '-e', text]) # noqa: S603
@@ -152,6 +156,46 @@ def load_torrent_uri(parsed_args: Arguments) -> str | None:
152156
return torrent_uri
153157

154158

159+
async def mac_event_loop() -> None:
160+
"""
161+
Consume Mac events on the asyncio main thread.
162+
163+
WARNING: sendEvent_ can block on some events. In particular, while the tray menu is open.
164+
"""
165+
from AppKit import NSApp, NSEventMaskAny
166+
from Foundation import NSDate, NSDefaultRunLoopMode
167+
168+
while True:
169+
event = NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_(NSEventMaskAny, NSDate.now(),
170+
NSDefaultRunLoopMode, True)
171+
if event is None:
172+
await asyncio.sleep(0.5)
173+
else:
174+
NSApp().sendEvent_(event)
175+
await asyncio.sleep(0.01)
176+
177+
178+
def spawn_tray_icon(session: Session, config: TriblerConfigManager) -> Icon:
179+
"""
180+
Create the tray icon.
181+
"""
182+
import pystray
183+
image_path = tribler.get_webui_root() / "public" / "tribler.png"
184+
image = Image.open(image_path.resolve())
185+
api_port = session.rest_manager.get_api_port()
186+
url = f"http://{config.get('api/http_host')}:{api_port}/ui/#/downloads/all?key={config.get('api/key')}"
187+
menu = (pystray.MenuItem('Open', lambda: webbrowser.open_new_tab(url)),
188+
pystray.MenuItem('Quit', lambda: session.shutdown_event.set()))
189+
icon = pystray.Icon("Tribler", icon=image, title="Tribler", menu=menu)
190+
webbrowser.open_new_tab(url)
191+
if sys.platform == "darwin":
192+
icon.run_detached(None)
193+
asyncio.ensure_future(mac_event_loop()) # noqa: RUF006
194+
else:
195+
threading.Thread(target=icon.run).start()
196+
return icon
197+
198+
155199
async def main() -> None:
156200
"""
157201
The main script entry point.
@@ -185,16 +229,7 @@ async def main() -> None:
185229
if server_url and torrent_uri:
186230
await start_download(config, server_url, torrent_uri)
187231
if not headless:
188-
import pystray
189-
image_path = tribler.get_webui_root() / "public" / "tribler.png"
190-
image = Image.open(image_path.resolve())
191-
api_port = session.rest_manager.get_api_port()
192-
url = f"http://{config.get('api/http_host')}:{api_port}/ui/#/downloads/all?key={config.get('api/key')}"
193-
menu = (pystray.MenuItem('Open', lambda: webbrowser.open_new_tab(url)),
194-
pystray.MenuItem('Quit', lambda: session.shutdown_event.set()))
195-
icon = pystray.Icon("Tribler", icon=image, title="Tribler", menu=menu)
196-
webbrowser.open_new_tab(url)
197-
threading.Thread(target=icon.run).start()
232+
icon = spawn_tray_icon(session, config)
198233

199234
await session.shutdown_event.wait()
200235
await session.shutdown()

0 commit comments

Comments
 (0)