System tray icons for GTK4 applications - betray GNOME 3's philosophy with style!
Project description
๐๏ธ trayer - System Tray Icons for GTK4
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 integrationexample_hide_to_tray.py- Hide window to trayexample_dynamic_icon.py- Dynamic icon changesexample_dynamic_menu.py- Dynamic menu updates
๐จ Icon Names
Common icon names from system themes:
Applications:
application-x-executableapplications-internetapplications-multimedia
Status:
user-available(green)user-busy(red)user-away(yellow)
Mail:
mail-unreadmail-read
Symbols:
face-smileemblem-importantdialog-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 IDtitle(str): Tooltip texticon_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:
-
StatusNotifierItem - The tray icon itself
-
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:
- "Tray-er" - One who creates trays (like "player", "baker")
- "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
- ๐ Bug Reports: GitHub Issues
- ๐ Documentation: GitHub Wiki
- ๐ก Feature Requests: GitHub Discussions
Happy betraying! ๐๐๏ธ
๐จโ๐ป Author
Matteo Benedetto (@Enne2)
- ๐ Location: Italy
- ๐ GitHub: github.com/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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4bc2866e9e1cb3fc608f322c2e4a5cc36a1d6c14fc870d20cc47a0fa25b4e396
|
|
| MD5 |
75e5ed019d179fc43e70064fef94b71e
|
|
| BLAKE2b-256 |
a22cc35ad421fff880cec20a352fa31e0a16469f7971685689998c644107e036
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7b511a51ba1c08c5653bdbb91f008cb7dbba105ee5364ff831a20872fdb101de
|
|
| MD5 |
8f9e9e8de8518400d2370d8c9e1eff4b
|
|
| BLAKE2b-256 |
26447cca0538a40a1186b9b4240603526f4c0d4a6cd75995b2f98ee43d67abf0
|