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: topmonitor: 0size: 40applets: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_windowself.destroy() manager_window = Noneif self.get_application(): self.get_application().manager_window = Nonedef 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 = Noneclass 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 = sizeGtk4LayerShell.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_windowif 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__] = subclassPANEL_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:continuefor 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)