Can/should I use asyncio instead of threading with Pystray?

991 Views Asked by At

I'm writing a Python program which periodically scrapes a website for a single number, which is then displayed in the system tray via Pystray. I can make it do what I want using threading but would it be better to use asyncio - and if so, how?

From the Pystray manual:

The call to pystray.Icon.run() is blocking, and it must be performed from the main thread of the application. The reason for this is that the system tray icon implementation for OSX will fail unless called from the main thread, and it also requires the application runloop to be running. pystray.Icon.run() will start the runloop.

Does this mean that asyncio is incompatible and for this application there must be a separate thread?

Here's a working demo using threading:

from pystray import Icon as icon, Menu as menu, MenuItem as item
from PIL import Image, ImageDraw, ImageFont
import threading
import time
import random

def make_number_icon(number):
    # draws the number as two digits in red on a transparent background
    img = Image.new('RGBA', (64,64), color=(0,0,0,0))
    fnt = ImageFont.truetype(r'C:\Windows\Fonts\GOTHICB.TTF', 58)
    d = ImageDraw.Draw(img)
    d.text((0,-8), f"{number:02}", font=fnt, fill='red')
    return img

def update_icon(icon, _=None):
    # web-scraping replaced with an RNG for demo
    count = random.randrange(100)
    icon.icon = make_number_icon(count)

icon = icon("Pystray test",
            icon=make_number_icon(0),
            menu=menu(item("Update now", update_icon),
                      item("Quit", icon.stop)))

def update_periodically_forever():
    global icon
    while True:
        update_icon(icon)
        time.sleep(2) # shortened from 60 for demo

# I understand daemon=True cleans up this thread when we reach EOF
update_thread = threading.Thread(target=update_periodically_forever, daemon=True)
update_thread.start()

# this is a blocking call; we stay here until icon.stop() is called
icon.run()

What it looks like:

enter image description here

update_icon should be called roughly every 60 seconds. It doesn't matter if that's 60 seconds after the function was last called or 60 second after the function returned from its previous call.

0

There are 0 best solutions below