Skip to main content

System tray icons for GTK4 applications - betray GNOME 3's philosophy with style!

Project description

๐Ÿ—‚๏ธ trayer - System Tray Icons for GTK4

PyPI version Python Versions License: MIT

Etymology: From "tray" + "-er" (one who creates trays), and coincidentally from Middle English "traitor" โ€” because this library gleefully betrays GNOME 3's philosophy of hiding tray icons. ๐Ÿ˜ˆ

Add system tray icons with context menus to your GTK4 applications with just a few lines of code!


โœจ Features

  • ๐ŸŽฏ Simple API - Add tray icons in 3 lines of code
  • ๐Ÿ–ฑ๏ธ Full Click Support - Left, right, and middle-click actions
  • ๐Ÿ“‹ Context Menus - Easy menu creation with separators
  • ๐Ÿ”„ Dynamic Updates - Change icons and menus at runtime
  • ๐ŸŽจ Theme Integration - Uses system icon themes
  • ๐Ÿง Linux Desktop Support - Works on GNOME (with extension), KDE, XFCE, Cinnamon
  • ๐Ÿ“ฆ Zero Config - Implements StatusNotifierItem + DBusMenu protocols

๐Ÿš€ Quick Start

Installation

pip install trayer

System Requirements:

# On Debian/Ubuntu:
sudo apt install python3-gi gir1.2-gtk-4.0 python3-dbus

# On GNOME, you also need:
sudo apt install gnome-shell-extension-appindicator
gnome-extensions enable appindicatorsupport@ubuntu.com
# Then logout/login

Basic Usage

from trayer import TrayIcon
import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk

class MyApp(Gtk.Application):
    def do_activate(self):
        self.window = Gtk.ApplicationWindow(application=self)
        self.window.set_title("My App")
        self.window.present()
    
    def toggle_window(self):
        if self.window.is_visible():
            self.window.hide()
        else:
            self.window.present()

# Create app
app = MyApp(application_id="com.example.myapp")

# Create tray icon
tray = TrayIcon(
    app_id="com.example.myapp",
    title="My Application",
    icon_name="application-x-executable"
)

# Add click action
tray.set_left_click(app.toggle_window)

# Add menu items
tray.add_menu_item("Show Window", callback=lambda: app.window.present())
tray.add_menu_item("Hide Window", callback=lambda: app.window.hide())
tray.add_menu_separator()
tray.add_menu_item("Quit", callback=app.quit)

# Setup and run (IMPORTANT: setup() before run()!)
tray.setup()
app.run()

That's it! ๐ŸŽ‰


๐Ÿ“– Documentation

Create Tray Icon

from trayer import TrayIcon

tray = TrayIcon(
    app_id="com.example.myapp",      # Your application ID
    title="My Application",           # Tooltip text
    icon_name="application-x-executable"  # Icon from theme
)

Click Actions

# Left-click
tray.set_left_click(lambda: print("Left clicked!"))

# Middle-click
tray.set_middle_click(lambda: print("Middle clicked!"))

# Right-click automatically shows the menu

Menu Items

# Add menu item
tray.add_menu_item("Show", callback=show_window)

# Add disabled item
tray.add_menu_item("Premium Feature", callback=None, enabled=False)

# Add separator
tray.add_menu_separator()

# Add quit button
tray.add_menu_item("Quit", callback=app.quit)

Dynamic Updates

# Change icon
tray.change_icon("mail-unread")

# Change status
tray.change_status("NeedsAttention")  # Active, Passive, or NeedsAttention

# Update menu dynamically
tray.menu_items.clear()
tray.add_menu_item("New Item", callback=some_function)
tray.update_menu()

Complete Example

See examples/ directory for full working examples:

  • example_minimal.py - Minimal integration
  • example_hide_to_tray.py - Hide window to tray
  • example_dynamic_icon.py - Dynamic icon changes
  • example_dynamic_menu.py - Dynamic menu updates

๐ŸŽจ Icon Names

Common icon names from system themes:

Applications:

  • application-x-executable
  • applications-internet
  • applications-multimedia

Status:

  • user-available (green)
  • user-busy (red)
  • user-away (yellow)

Mail:

  • mail-unread
  • mail-read

Symbols:

  • face-smile
  • emblem-important
  • dialog-information

Find more: Look in /usr/share/icons/ or use gtk4-icon-browser


๐Ÿ”ง API Reference

TrayIcon(app_id, title, icon_name="application-x-executable")

Create a new tray icon.

Parameters:

  • app_id (str): Application ID
  • title (str): Tooltip text
  • icon_name (str): Icon name from system theme

tray.set_left_click(callback)

Set action for left-clicking the tray icon.

tray.set_middle_click(callback)

Set action for middle-clicking the tray icon.

tray.add_menu_item(label, callback, enabled=True, visible=True)

Add a menu item.

tray.add_menu_separator()

Add a separator line to the menu.

tray.setup()

Initialize the tray icon. Must be called before app.run()!

tray.change_icon(icon_name)

Change the tray icon dynamically.

tray.change_status(status)

Change status: "Active", "Passive", or "NeedsAttention".

tray.update_menu()

Update the menu after modifying items dynamically.


๐Ÿ› Troubleshooting

Tray icon doesn't appear on GNOME

Install and enable the AppIndicator extension:

sudo apt install gnome-shell-extension-appindicator
gnome-extensions enable appindicatorsupport@ubuntu.com
# Then logout/login

Menu doesn't show

Make sure you called tray.setup() before app.run()

Callbacks don't work

Ensure callbacks don't take arguments or use lambda:

# โœ… Correct
tray.add_menu_item("Show", lambda: app.show_window(True))

# โŒ Wrong
tray.add_menu_item("Show", app.show_window(True))

๐Ÿค How It Works

This library implements two D-Bus protocols:

  1. StatusNotifierItem - The tray icon itself

  2. DBusMenu - The context menu

Your application communicates with the desktop environment via D-Bus to display the icon and menu.


๐Ÿ“‹ Requirements

  • Python 3.8+
  • PyGObject (GTK4 bindings)
  • dbus-python
  • A desktop environment with StatusNotifierItem support (GNOME with extension, KDE, XFCE, Cinnamon)

๐Ÿค” Why "trayer"?

The name has a double meaning:

  1. "Tray-er" - One who creates trays (like "player", "baker")
  2. "Traitor" (Middle English) - Because we gleefully betray GNOME 3's philosophy of removing tray icons!

The GNOME team decided tray icons were "legacy" and removed native support. This library brings them back through the StatusNotifierItem protocol. We're the rebels of the desktop world! ๐Ÿ˜Ž


๐Ÿ“„ License

MIT License - Use freely in your projects!


๐Ÿ™ Contributing

Contributions are welcome! Please feel free to submit a Pull Request.


๐Ÿ’ฌ Support


Happy betraying! ๐Ÿ˜ˆ๐Ÿ—‚๏ธ


๐Ÿ‘จโ€๐Ÿ’ป Author

Matteo Benedetto (@Enne2)

Eclectic systems designer passionate about elegant solutions to complex problems.


๐Ÿ“„ License

MIT License - Use freely in your projects! See LICENSE file for details.

ยฉ 2025 Matteo Benedetto

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

trayer-0.1.1.tar.gz (12.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

trayer-0.1.1-py3-none-any.whl (10.9 kB view details)

Uploaded Python 3

File details

Details for the file trayer-0.1.1.tar.gz.

File metadata

  • Download URL: trayer-0.1.1.tar.gz
  • Upload date:
  • Size: 12.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for trayer-0.1.1.tar.gz
Algorithm Hash digest
SHA256 4bc2866e9e1cb3fc608f322c2e4a5cc36a1d6c14fc870d20cc47a0fa25b4e396
MD5 75e5ed019d179fc43e70064fef94b71e
BLAKE2b-256 a22cc35ad421fff880cec20a352fa31e0a16469f7971685689998c644107e036

See more details on using hashes here.

File details

Details for the file trayer-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: trayer-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 10.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for trayer-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 7b511a51ba1c08c5653bdbb91f008cb7dbba105ee5364ff831a20872fdb101de
MD5 8f9e9e8de8518400d2370d8c9e1eff4b
BLAKE2b-256 26447cca0538a40a1186b9b4240603526f4c0d4a6cd75995b2f98ee43d67abf0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page