implementing-applets.md
Python script, Unicode text, UTF-8 text executable
How to implement applets for the Panorama panel
The Panorama panel loads Python packages from
~/local/share/panorama-panel/applets
and ~/usr/share/panorama-panel/applets
.
Each of the loaded packages can provide one or more subclasses of
panorama_panel.Applet
, which is itself a subclass of Gtk.Box
. The
panorama_panel
module is automatically made available to all modules the panel
imports this way. Your subclass can set the class attributes name
and
description
to be presented in menus.
Try to make your class name reasonably unique, because the panel identifies
applets by their __name__
and doesn't support multiple classes sharing one.
Managing configuration
In the constructor you receive the configuration (a Python dictionary) as the
third parameter. You also have to implement the get_config(self)
method to
return the configuration when asked to, that is, when the panel is saving its
settings. This will be serialised to a YAML file.
Using other sources of configuration for your applet is not recommended because there may be multiple applet instances.
How you present the configuration graphically is entirely up to you. You can put
Gtk.Builder
files in your applet's directory to help you build the
configuration window. This can be exposed in a context menu.
Popover registration
Autohiding panels do not hide when a popover belonging to one is open. To
implement this, all root popovers that should keep the panel open must be
registered using the function
panorama_panel.track_popover(popover: Gtk.Popover)
which adds some signals to
make sure the panel doesn't disappear while the popover is open.
If you want the panel to stay open for some other reason, you can use the set
self.get_root().open_popovers
and add to it a unique integer identifier for
your reason to keep the panel open — normally, this should be a Python id()
for a relevant object to guarantee it is unique and easy to reproduce.
Other functions
The panorama_panel
module provides a few other functions to use (when asked
to pass an applet, pass self
):
get_panel_position(applet: Applet) -> Gtk.PositionType
: get the edge of the screen the panel is currently on (note that they may move, and you need to respond).
It also provides two dictionaries:
OPPOSITE_POSITION
: get the oppositeGtk.PositionType
for anotherGtk.PositionType
.POSITION_TO_ARROW
: get the correspondingGtk.ArrowType
for aGtk.PositionType
.
You can also override extra methods to get notified about other events:
set_panel_position(self, position: Gtk.PositionType)
: called when the panel is repositioned.make_draggable(self)
: if you have dangerous controllers, you can disable them when edit mode is enabled.restore_drag(self)
: restore the state when edit mode is disabled.
Of course, you can override any other Gtk.Box
methods as well.
Minimal example
This applet displays a configurable string. It doesn't expose a configuration
window. A more elaborate example with a configuration window, popovers and a
menu button can be found at applets/clock
.
import panorama_panel import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk class LabelApplet(panorama_panel.Applet): # Defining class properties name = "Label" description = "Show a text" def __init__(self, orientation=Gtk.Orientation.HORIZONTAL, config=None): super().__init__(orientation=orientation, config=config) if config is None: config = {} self.label = Gtk.Label() self.label.set_text(config.get("text", "Label")) self.append(self.label) def get_config(self): # It is not recommended to store a config dict in the applet, instead # generate it only when needed, to avoid duplicating state return {"text": self.label.get_text()}
1How to implement applets for the Panorama panel 2=============================================== 3 4The Panorama panel loads Python packages from 5`~/local/share/panorama-panel/applets` and `~/usr/share/panorama-panel/applets`. 6Each of the loaded packages can provide one or more subclasses of 7`panorama_panel.Applet`, which is itself a subclass of `Gtk.Box`. The 8`panorama_panel` module is automatically made available to all modules the panel 9imports this way. Your subclass can set the class attributes `name` and 10`description` to be presented in menus. 11 12Try to make your class name reasonably unique, because the panel identifies 13applets by their `__name__` and doesn't support multiple classes sharing one. 14 15Managing configuration 16---------------------- 17 18In the constructor you receive the configuration (a Python dictionary) as the 19third parameter. You also have to implement the `get_config(self)` method to 20return the configuration when asked to, that is, when the panel is saving its 21settings. This will be serialised to a YAML file. 22 23Using other sources of configuration for your applet is not recommended because 24there may be multiple applet instances. 25 26How you present the configuration graphically is entirely up to you. You can put 27`Gtk.Builder` files in your applet's directory to help you build the 28configuration window. This can be exposed in a context menu. 29 30Popover registration 31-------------------- 32 33Autohiding panels do not hide when a popover belonging to one is open. To 34implement this, all root popovers that should keep the panel open must be 35registered using the function 36`panorama_panel.track_popover(popover: Gtk.Popover)` which adds some signals to 37make sure the panel doesn't disappear while the popover is open. 38 39If you want the panel to stay open for some other reason, you can use the set 40`self.get_root().open_popovers` and add to it a unique integer identifier for 41your reason to keep the panel open — normally, this should be a Python `id()` 42for a relevant object to guarantee it is unique and easy to reproduce. 43 44Other functions 45--------------- 46 47The `panorama_panel` module provides a few other functions to use (when asked 48to pass an applet, pass `self`): 49 50* `get_panel_position(applet: Applet) -> Gtk.PositionType`: get the edge of the 51screen the panel is currently on (note that they may move, and you need to 52respond). 53 54It also provides two dictionaries: 55 56* `OPPOSITE_POSITION`: get the opposite `Gtk.PositionType` for another 57`Gtk.PositionType`. 58* `POSITION_TO_ARROW`: get the corresponding `Gtk.ArrowType` for a 59`Gtk.PositionType`. 60 61You can also override extra methods to get notified about other events: 62 63* `set_panel_position(self, position: Gtk.PositionType)`: called when the panel 64is repositioned. 65* `make_draggable(self)`: if you have dangerous controllers, you can disable 66them when edit mode is enabled. 67* `restore_drag(self)`: restore the state when edit mode is disabled. 68 69Of course, you can override any other `Gtk.Box` methods as well. 70 71Minimal example 72--------------- 73 74This applet displays a configurable string. It doesn't expose a configuration 75window. A more elaborate example with a configuration window, popovers and a 76menu button can be found at `applets/clock`. 77 78~~~python 79import panorama_panel 80 81import gi 82gi.require_version("Gtk", "4.0") 83 84from gi.repository import Gtk 85 86 87class LabelApplet(panorama_panel.Applet): 88# Defining class properties 89name = "Label" 90description = "Show a text" 91 92def __init__(self, orientation=Gtk.Orientation.HORIZONTAL, config=None): 93super().__init__(orientation=orientation, config=config) 94if config is None: 95config = {} 96self.label = Gtk.Label() 97self.label.set_text(config.get("text", "Label")) 98self.append(self.label) 99 100def get_config(self): 101# It is not recommended to store a config dict in the applet, instead 102# generate it only when needed, to avoid duplicating state 103return {"text": self.label.get_text()} 104~~~ 105