By using this site, you agree to have cookies stored on your device, strictly for functional purposes, such as storing your session and preferences.

Dismiss

 implementing-applets.md

View raw Download
text/x-script.python • 4.23 kiB
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. Including your name or an organisation's name in the class name is one way to do this.

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.

When you want to write the configuration, emit the signal config-changed and the panel will save it to the 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 opposite Gtk.PositionType for another Gtk.PositionType.

  • POSITION_TO_ARROW: get the corresponding Gtk.ArrowType for a Gtk.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.

  • shutdown(self): when the panel is shutting down; use if you want to disconnect from services or similar.

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