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 • 3.92 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.

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 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.

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
15
Managing configuration
16
----------------------
17
18
In the constructor you receive the configuration (a Python dictionary) as the
19
third parameter. You also have to implement the `get_config(self)` method to
20
return the configuration when asked to, that is, when the panel is saving its
21
settings. This will be serialised to a YAML file.
22
23
Using other sources of configuration for your applet is not recommended because
24
there may be multiple applet instances.
25
26
How 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
28
configuration window. This can be exposed in a context menu.
29
30
Popover registration
31
--------------------
32
33
Autohiding panels do not hide when a popover belonging to one is open. To
34
implement this, all root popovers that should keep the panel open must be
35
registered using the function
36
`panorama_panel.track_popover(popover: Gtk.Popover)` which adds some signals to
37
make sure the panel doesn't disappear while the popover is open.
38
39
If 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
41
your reason to keep the panel open — normally, this should be a Python `id()`
42
for a relevant object to guarantee it is unique and easy to reproduce.
43
44
Other functions
45
---------------
46
47
The `panorama_panel` module provides a few other functions to use (when asked
48
to pass an applet, pass `self`):
49
50
* `get_panel_position(applet: Applet) -> Gtk.PositionType`: get the edge of the
51
screen the panel is currently on (note that they may move, and you need to
52
respond).
53
54
It 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
61
You can also override extra methods to get notified about other events:
62
63
* `set_panel_position(self, position: Gtk.PositionType)`: called when the panel
64
is repositioned.
65
* `make_draggable(self)`: if you have dangerous controllers, you can disable
66
them when edit mode is enabled.
67
* `restore_drag(self)`: restore the state when edit mode is disabled.
68
69
Of course, you can override any other `Gtk.Box` methods as well.
70
71
Minimal example
72
---------------
73
74
This applet displays a configurable string. It doesn't expose a configuration
75
window. A more elaborate example with a configuration window, popovers and a
76
menu button can be found at `applets/clock`.
77
78
~~~python
79
import panorama_panel
80
81
import gi
82
gi.require_version("Gtk", "4.0")
83
84
from gi.repository import Gtk
85
86
87
class LabelApplet(panorama_panel.Applet):
88
# Defining class properties
89
name = "Label"
90
description = "Show a text"
91
92
def __init__(self, orientation=Gtk.Orientation.HORIZONTAL, config=None):
93
super().__init__(orientation=orientation, config=config)
94
if config is None:
95
config = {}
96
self.label = Gtk.Label()
97
self.label.set_text(config.get("text", "Label"))
98
self.append(self.label)
99
100
def 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
103
return {"text": self.label.get_text()}
104
~~~
105