Asynchronously updating PyGTK tray icon

ghz 13hours ago ⋅ 2 views

Being very new to GTK, it appears to me that gtk.main() is synchronous, blocking any further processing until the respective UI is closed.

So how can I periodically (e.g. every 5 seconds) update/refresh StatusIcon's icon - do I have to resort to Twisted et al. for this?

Answer

You're right that gtk.main() is synchronous and blocks further execution until the application is closed, but you don’t need to use a complex framework like Twisted for periodic updates in a GTK application. You can leverage GTK's built-in functionality to perform periodic tasks without blocking the main event loop. Specifically, you can use gobject.timeout_add() to schedule periodic callbacks in the GTK event loop.

Here’s how you can periodically update a gtk.StatusIcon (tray icon) every 5 seconds without blocking the GTK main loop:

Solution using gobject.timeout_add()

import gtk
import gobject

class TrayIcon:
    def __init__(self):
        # Create the StatusIcon
        self.status_icon = gtk.StatusIcon()
        self.status_icon.set_from_file("icon1.png")  # Initial icon
        self.status_icon.set_tooltip("Tray Icon Example")

        # Set up the periodic update every 5 seconds (5000 ms)
        gobject.timeout_add(5000, self.update_icon)

    def update_icon(self):
        # Logic to update the icon (alternating between two icons)
        if self.status_icon.get_pixbuf() is None:
            self.status_icon.set_from_file("icon2.png")
        else:
            self.status_icon.set_from_file("icon1.png")
        
        # Return True to keep the timeout active
        return True

def main():
    tray = TrayIcon()

    # Start the GTK main loop
    gtk.main()

if __name__ == "__main__":
    main()

Explanation:

  1. GTK Status Icon: The gtk.StatusIcon() creates a tray icon, and set_from_file() sets the initial icon. You can replace this with any image or icon you'd like to use in the tray.

  2. Periodic Update: The function gobject.timeout_add(5000, self.update_icon) schedules the update_icon() method to be called every 5000 milliseconds (i.e., every 5 seconds). This call doesn’t block the main loop.

  3. Update Logic: Inside the update_icon() method, we alternate between two icons (icon1.png and icon2.png). This is just an example; you can update the icon based on any logic you need.

  4. GTK Main Loop: The gtk.main() starts the GTK event loop, which will continue running and handling events until the program exits. The timeout_add() ensures that update_icon() gets called periodically without blocking this loop.

  5. Returning True: The update_icon() method returns True, which ensures the timeout is re-triggered every 5 seconds. If you return False, the timeout would only be triggered once.

Key Concepts:

  • gobject.timeout_add(): This function adds a callback to be called after a specified interval (in milliseconds). It doesn’t block the GTK main loop, so other events (like UI updates or interactions) can still occur while the periodic task runs.

  • GTK Main Loop: The gtk.main() call starts the event loop, which processes events like user interactions, system messages, and periodic tasks scheduled with timeout_add(). This ensures the program remains responsive.

Why You Don’t Need Twisted:

You don’t need Twisted or any other complex asynchronous framework for this type of task. GTK itself, via gobject.timeout_add(), provides a simple mechanism for scheduling tasks without blocking the event loop. This allows you to perform periodic updates, UI changes, and other tasks in the background efficiently.

This method is both simple and effective for periodic updates (e.g., updating a tray icon every 5 seconds) in GTK without needing additional dependencies.