1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
|
Creating a *system tray icon*
-----------------------------
In order to create a *system tray icon*, the class ``pystray.Icon`` is used::
import pystray
from PIL import Image, ImageDraw
def create_image(width, height, color1, color2):
# Generate an image and draw a pattern
image = Image.new('RGB', (width, height), color1)
dc = ImageDraw.Draw(image)
dc.rectangle(
(width // 2, 0, width, height // 2),
fill=color2)
dc.rectangle(
(0, height // 2, width // 2, height),
fill=color2)
return image
# In order for the icon to be displayed, you must provide an icon
icon = pystray.Icon(
'test name',
icon=create_image(64, 64, 'black', 'white'))
# To finally show you icon, call run
icon.run()
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.
If you only target *Windows*, calling ``run()`` from a thread other than the
main thread is safe.
The ``run()`` method accepts an optional argument: ``setup``, a callable.
The ``setup`` function will be run in a separate thread once the *system tray
icon* is ready. The icon does not wait for it to complete, so you may put any
code that would follow the call to ``pystray.Icon.run()`` in it.
The call to ``pystray.Icon.run()`` will not complete until ``stop()`` is called.
Getting input from the *system tray icon*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In order to receive notifications about user interaction with the icon, a
popup menu can be added with the ``menu`` constructor argument.
This must be an instance of ``pystray.Menu``. Please see the reference for more
information about the format.
It will be displayed when the right-hand button has been pressed on the icon on
*Windows*, and when the icon has been clicked on other platforms. Menus are not
supported on *X*.
Menus also support a default item. On *Windows*, and *X*, this item will be
activated when the user clicks on the icon using the primary button. On other
platforms it will be activated if the menu contains no visible entries; it does
not have to be visible.
All properties of menu items, except for the callback, can be dynamically
calculated by supplying callables instead of values to the menu item
constructor. The properties are recalculated every time the icon is clicked or
any menu item is activated.
If the dynamic properties change because of an external event, you must ensure
that ``Icon.update_menu`` is called. This is required since not all supported
platforms allow for the menu to be generated when displayed.
Creating the menu
~~~~~~~~~~~~~~~~~
*This is not supported on Xorg; please check Icon.HAS_MENU at runtime for
support on the current platform.*
A menu can be attached to a system tray icon by passing an instance of
:class:`pystray.Menu` as the ``menu`` keyword argument.
A menu consists of a list of menu items, optionally separated by menu
separators.
Separators are intended to group menu items into logical groups. They will not
be displayed as the first and last visible item, and adjacent separators will be
hidden.
A menu item has several attributes:
*text* and *action*
The menu item text and its associated action.
These are the only required attributes. Please see *submenu* below for
alternate interpretations of *action*.
*checked*
Whether the menu item is checked.
This can be one of three values:
``False``
The item is decorated with an unchecked check box.
``True``
The item is decorated with a checked check box.
``None``
There is no hint that the item is checkable.
If you want this to actually be togglable, you must pass a callable that
returns the current state::
from pystray import Icon as icon, Menu as menu, MenuItem as item
state = False
def on_clicked(icon, item):
global state
state = not item.checked
# Update the state in `on_clicked` and return the new state in
# a `checked` callable
icon('test', create_image(), menu=menu(
item(
'Checkable',
on_clicked,
checked=lambda item: state))).run()
*radio*
*This is not supported on macOS; please check Icon.HAS_MENU_RADIO at
runtime for support on the current platform.*
Whether this is a radio button.
This is used only if ``checked`` is ``True`` or ``False``, and only has a
visual meaning. The menu has no concept of radio button groups::
from pystray import Icon as icon, Menu as menu, MenuItem as item
state = 0
def set_state(v):
def inner(icon, item):
global state
state = v
return inner
def get_state(v):
def inner(item):
return state == v
return inner
# Let the menu items be a callable returning a sequence of menu
# items to allow the menu to grow
icon('test', create_image(), menu=menu(lambda: (
item(
'State %d' % i,
set_state(i),
checked=get_state(i),
radio=True)
for i in range(max(5, state + 2))))).run()
*default*
*This is not supported on Darwin and using AppIndicator; please check
Icon.HAS_DEFAULT at runtime for support on the current platform.*
Whether this is the default item.
It is drawn in a distinguished style and will be activated as the default
item on platforms that support default actions. On *X*, this is the only
action available.
*visible*
Whether the menu item is visible.
*enabled*
Whether the menu item is enabled. Disabled menu items are displayed, but are
greyed out and cannot be activated.
*submenu*
The submenu, if any, that is attached to this menu item. Either a submenu
or an action can be passed as the second argument to the constructor.
The submenu must be an instance of :class:`Menu`::
from pystray import Icon as icon, Menu as menu, MenuItem as item
icon('test', create_image(), menu=menu(
item(
'With submenu',
menu(
item(
'Submenu item 1',
lambda icon, item: 1),
item(
'Submenu item 2',
lambda icon, item: 2))))).run()
Once created, menus and menu items cannot be modified. All attributes except for
the menu item callbacks can however be set to callables returning the current
value. This also applies to the sequence of menu items belonging to a menu: this
can be a callable returning the current sequence.
Displaying notifications
~~~~~~~~~~~~~~~~~~~~~~~~
*This is not supported on macOS and Xorg; please check Icon.HAS_NOTIFICATION
at runtime for support on the current platform.*
To display a system notification, use :meth:`pystray.Icon.notify`::
from pystray import Icon as icon, Menu as menu, MenuItem as item
icon('test', create_image(), menu=menu(
item(
'With submenu',
menu(
item(
'Show message',
lambda icon, item: icon.notify('Hello World!')),
item(
'Submenu item 2',
lambda icon, item: icon.remove_notification()))))).run()
Integrating with other frameworks
---------------------------------
The *pystray* ``run`` method is blocking, and must be called from the main
thread to maintain platform independence. This is troublesome when attempting
to use frameworks with an event loop, since they may also require running in
the main thread.
For this case you can use ``run_detached``. This allows you to setup the icon
and then pass control to the framework. Please see the documentation for more
information.
Selecting a backend
-------------------
*pystray* aims to provide a unified *API* for all supported platforms. In some
cases, however, that is not entirely possible.
This library supports a number of backends. On *macOS* and *Windows*, the
operating system has system tray icons built-in, so the default backends should
be used, but on *Linux* you may have to make a decision depending on your
needs.
By setting the environment variable ``PYSTRAY_BACKEND`` to one of the strings in
the next section, the automatic selection is turned off.
Supported backends
~~~~~~~~~~~~~~~~~~
*appindicator*
This is one of the backends available on *Linux*, and is the preferred
choice. All *pystray* features except for a menu default action are
supported, and if the *appindicator* library is installed on the system
and the desktop environment supports it, the icon is guaranteed to be
displayed.
If the *appindicator* library is not available on the system, a fallback on
*ayatana-appindicator* is attempted.
*darwin*
This is the default backend when running on *macOS*. All *pystray* features
are available.
*gtk*
This is one of the backends available on *Linux*, and is prioritised above
the *XOrg* backend. It uses *GTK* as underlying library. All *pystray*
features are available, but it may not actually result in a visible icon:
when running a *gnome-shell* session, an third party plugin is required to
display legacy tray icons.
*win32*
This is the default backend when running on *Windows*. All *pystray*
features are available.
*xorg*
This is one of the backends available on *Linux*. It is used as a fallback
when no other backend can be loaded. It does not support any menu
functionality except for a default action.
|