roundabout,
created on Sunday, 20 July 2025, 20:08:42 (1753042122),
received on Saturday, 26 July 2025, 07:40:05 (1753515605)
Author identity: vlad <vlad.muntoiu@gmail.com>
5893b796cbde2942c55a85db087aa6ed0b28a165
applets/clock/__init__.py
@@ -131,4 +131,4 @@ class ClockApplet(panorama_panel.Applet):
return False # Do not rerun the current timeout; a new one has been scheduled
def get_config(self):
return {"text": self.label.get_text()}
return {"formatting": self.formatting}
config.yaml
@@ -1,11 +1,12 @@
panels:
- position: top
monitor: 0
size: 40
applets:
left:
- LabelApplet:
text: "Hello, world"
right:
- ClockApplet:
formatting: "%T, %a %-d %b %Y"
- position: top
monitor: 0
size: 40
applets:
left:
- LabelApplet:
text: Hello, world
centre: []
right:
- ClockApplet:
formatting: '%T, %a %-d %b %Y'
main.py
@@ -35,9 +35,6 @@ def get_config_file():
return config_home / "panorama-panel" / "config.yaml"
panels = []
class ManagerWindow(Gtk.Window):
def __init__(self):
super().__init__()
@@ -59,9 +56,9 @@ class ManagerWindow(Gtk.Window):
self.connect("close-request", self.on_destroy)
def on_destroy(self, widget):
global manager_window
self.destroy()
manager_window = None
if self.get_application():
self.get_application().manager_window = None
def set_edit_mode(self, switch, value):
print(f"Editing is {value}")
@@ -74,9 +71,6 @@ class ManagerWindow(Gtk.Window):
panel.set_edit_mode(False)
manager_window = None
class AppletArea(Gtk.Box):
def __init__(self, orientation=Gtk.Orientation.HORIZONTAL):
super().__init__()
@@ -90,10 +84,13 @@ class AppletArea(Gtk.Box):
class Panel(Gtk.Window):
def __init__(self, monitor: Gdk.Monitor, position: Gtk.PositionType = Gtk.PositionType.TOP, size: int = 40):
super().__init__()
def __init__(self, application: Gtk.Application, monitor: Gdk.Monitor, position: Gtk.PositionType = Gtk.PositionType.TOP, size: int = 40, monitor_index: int = 0):
super().__init__(application=application)
self.set_default_size(800, size)
self.set_decorated(False)
self.position = position
self.monitor_index = monitor_index
self.size = size
Gtk4LayerShell.init_for_window(self)
@@ -161,24 +158,17 @@ class Panel(Gtk.Window):
def show_manager(self, _0=None, _1=None):
print("Showing manager")
global manager_window
if not manager_window:
manager_window = ManagerWindow()
manager_window.present()
if self.get_application():
if not self.get_application().manager_window:
self.get_application().manager_window = ManagerWindow()
self.get_application().manager_window.set_application(self.get_application())
self.get_application().manager_window.present()
def get_orientation(self):
box = self.get_first_child()
return box.get_orientation()
display = Gdk.Display.get_default()
monitors = display.get_monitors()
for i, monitor in enumerate(monitors):
geometry = monitor.get_geometry()
print(f"Monitor {i}: {geometry.width}x{geometry.height} at {geometry.x},{geometry.y}")
def get_all_subclasses(klass: type) -> list[type]:
subclasses = []
for subclass in klass.__subclasses__():
@@ -187,7 +177,6 @@ def get_all_subclasses(klass: type) -> list[type]:
return subclasses
def load_packages_from_dir(dir_path: Path):
loaded_modules = []
@@ -207,18 +196,6 @@ def load_packages_from_dir(dir_path: Path):
return loaded_modules
all_applets = list(chain.from_iterable(load_packages_from_dir(d) for d in get_applet_directories()))
print("Applets:")
subclasses = get_all_subclasses(panorama_panel.Applet)
applets_by_name = {}
for subclass in subclasses:
if subclass.__name__ in applets_by_name:
print(f"Name conflict for applet {subclass.__name__}. Only one will be loaded.", file=sys.stderr)
applets_by_name[subclass.__name__] = subclass
PANEL_POSITIONS = {
"top": Gtk.PositionType.TOP,
"bottom": Gtk.PositionType.BOTTOM,
@@ -227,34 +204,100 @@ PANEL_POSITIONS = {
}
with open(get_config_file(), "r") as config_file:
yaml_loader = yaml.YAML(typ="rt")
yaml_file = yaml_loader.load(config_file)
for panel_data in yaml_file["panels"]:
position = PANEL_POSITIONS[panel_data["position"]]
monitor_index = panel_data["monitor"]
monitor = monitors[monitor_index]
size = panel_data["size"]
panel = Panel(monitor, position, size)
panels.append(panel)
panel.show()
print(f"{size}px panel on {position} edge of monitor {monitor_index}")
for area_name, area in (("left", panel.left_area), ("centre", panel.centre_area), ("right", panel.right_area)):
applet_list = panel_data["applets"].get(area_name)
if applet_list is None:
continue
for applet in applet_list:
item = list(applet.items())[0]
AppletClass = applets_by_name[item[0]]
options = item[1]
applet_widget = AppletClass(orientation=panel.get_orientation(), config=options)
area.append(applet_widget)
PANEL_POSITIONS_REVERSE = {
Gtk.PositionType.TOP: "top",
Gtk.PositionType.BOTTOM: "bottom",
Gtk.PositionType.LEFT: "left",
Gtk.PositionType.RIGHT: "right",
}
loop = GLib.MainLoop()
loop.run()
class PanoramaPanel(Gtk.Application):
def __init__(self):
super().__init__(application_id="com.roundabout_host.panorama.panel")
self.display = Gdk.Display.get_default()
self.monitors = self.display.get_monitors()
self.applets_by_name = {}
self.panels = []
self.manager_window = None
def do_startup(self):
Gtk.Application.do_startup(self)
for i, monitor in enumerate(self.monitors):
geometry = monitor.get_geometry()
print(f"Monitor {i}: {geometry.width}x{geometry.height} at {geometry.x},{geometry.y}")
all_applets = list(chain.from_iterable(load_packages_from_dir(d) for d in get_applet_directories()))
print("Applets:")
subclasses = get_all_subclasses(panorama_panel.Applet)
for subclass in subclasses:
if subclass.__name__ in self.applets_by_name:
print(f"Name conflict for applet {subclass.__name__}. Only one will be loaded.", file=sys.stderr)
self.applets_by_name[subclass.__name__] = subclass
with open(get_config_file(), "r") as config_file:
yaml_loader = yaml.YAML(typ="rt")
yaml_file = yaml_loader.load(config_file)
for panel_data in yaml_file["panels"]:
position = PANEL_POSITIONS[panel_data["position"]]
monitor_index = panel_data["monitor"]
monitor = self.monitors[monitor_index]
size = panel_data["size"]
panel = Panel(self, monitor, position, size, monitor_index)
self.panels.append(panel)
panel.show()
print(f"{size}px panel on {position} edge of monitor {monitor_index}")
for area_name, area in (("left", panel.left_area), ("centre", panel.centre_area), ("right", panel.right_area)):
applet_list = panel_data["applets"].get(area_name)
if applet_list is None:
continue
for applet in applet_list:
item = list(applet.items())[0]
AppletClass = self.applets_by_name[item[0]]
options = item[1]
applet_widget = AppletClass(orientation=panel.get_orientation(), config=options)
area.append(applet_widget)
def do_activate(self):
Gio.Application.do_activate(self)
def save_config(self):
with open(get_config_file(), "w") as config_file:
yaml_writer = yaml.YAML(typ="rt")
data = {"panels": []}
for panel in self.panels:
panel_data = {
"position": PANEL_POSITIONS_REVERSE[panel.position],
"monitor": panel.monitor_index,
"size": panel.size,
"applets": {}
}
for area_name, area in (("left", panel.left_area), ("centre", panel.centre_area), ("right", panel.right_area)):
panel_data["applets"][area_name] = []
applet = area.get_first_child()
while applet is not None:
panel_data["applets"][area_name].append({
applet.__class__.__name__: applet.get_config(),
})
applet = applet.get_next_sibling()
data["panels"].append(panel_data)
yaml_writer.dump(data, config_file)
def do_shutdown(self):
print("Shutting down")
Gtk.Application.do_shutdown(self)
self.save_config()
if __name__ == "__main__":
app = PanoramaPanel()
app.run(sys.argv)