0

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.

Jack Deeth
  • 3,062
  • 3
  • 24
  • 39
  • Have you *tried* it yourself to use asyncio and what problems did you have with it? – Gino Mempin Feb 20 '21 at 02:00
  • Your program seems just fine. Do you have a specific idea what you'd like to use asyncio for, or are you just asking out of curiosity? – user4815162342 Feb 20 '21 at 10:29
  • @user4815162342 Most of the results for "call a function every x seconds" use `asyncio` and I thought there was a reason to use that instead of threads. However in the last 14 hours I've written a threaded app that does exactly what I need. And got a couple of hours of sleep. – Jack Deeth Feb 20 '21 at 14:09

0 Answers0