roundabout,
created on Sunday, 20 July 2025, 10:30:58 (1753007458),
received on Saturday, 26 July 2025, 07:40:05 (1753515605)
Author identity: vlad <vlad.muntoiu@gmail.com>
b6ed74922ffa0511460b8765f47a716772d42a20
applets/clock/__init__.py
@@ -1,14 +1,28 @@
import os
from pathlib import Path
import panorama_panel
import gi
gi.require_version("Gtk", "4.0")
from gi.repository import Gtk, GLib
from gi.repository import Gtk, GLib, Gio, Gdk
SECOND_PLACEHOLDERS = ("%c", "%s", "%S", "%T", "%X")
module_directory = Path(__file__).resolve().parent
@Gtk.Template(filename=str(module_directory / "panorama-clock-options.ui"))
class ClockOptions(Gtk.Window):
__gtype_name__ = "ClockOptions"
format_entry: Gtk.Entry = Gtk.Template.Child()
def __init__(self, **kwargs):
super().__init__(**kwargs)
class ClockApplet(panorama_panel.Applet):
name = "Clock"
description = "Read the current time and date"
@@ -35,19 +49,78 @@ class ClockApplet(panorama_panel.Applet):
# Some placeholders require second precision, but not all of them. If not required,
# use minute precision
self.has_second_precision = any(placeholder in self.formatting for placeholder in SECOND_PLACEHOLDERS)
self.next_update = None
self.set_time()
self.context_menu = self.make_context_menu()
right_click_controller = Gtk.GestureClick()
right_click_controller.set_button(3)
right_click_controller.connect("pressed", self.show_context_menu)
self.add_controller(right_click_controller)
action_group = Gio.SimpleActionGroup()
options_action = Gio.SimpleAction.new("options", None)
options_action.connect("activate", self.show_options)
action_group.add_action(options_action)
self.insert_action_group("applet", action_group)
self.options_window = None
def make_context_menu(self):
menu = Gio.Menu()
menu.append("Clock _options", "applet.options")
context_menu = Gtk.PopoverMenu.new_from_model(menu)
context_menu.set_has_arrow(False)
context_menu.set_parent(self)
context_menu.set_halign(Gtk.Align.START)
context_menu.set_flags(Gtk.PopoverMenuFlags.NESTED)
return context_menu
def show_context_menu(self, gesture, n_presses, x, y):
rect = Gdk.Rectangle()
rect.x = int(x)
rect.y = int(y)
rect.width = 1
rect.height = 1
self.context_menu.set_pointing_to(rect)
self.context_menu.popup()
def update_formatting(self, entry):
self.formatting = entry.get_text()
# Some placeholders require second precision, but not all of them. If not required,
# use minute precision
self.has_second_precision = any(placeholder in self.formatting for placeholder in SECOND_PLACEHOLDERS)
if self.next_update is not None:
GLib.source_remove(self.next_update)
self.next_update = None
self.set_time()
def show_options(self, _0=None, _1=None):
if not self.options_window:
self.options_window = ClockOptions()
self.options_window.format_entry.set_text(self.formatting)
self.options_window.format_entry.connect("changed", self.update_formatting)
self.options_window.present()
def set_time(self):
datetime = GLib.DateTime.new_now_local()
self.label.set_text(datetime.format(self.formatting))
formatted_time = datetime.format(self.formatting)
if formatted_time is not None:
self.label.set_text(datetime.format(self.formatting))
else:
self.label.set_text("Invalid time formatting")
return False
if self.has_second_precision:
current_ms = GLib.DateTime.new_now_local().get_microsecond() // 1000
GLib.timeout_add(1000 - current_ms + 1, self.set_time) # 1ms is added to ensure the clock is updated
self.next_update = GLib.timeout_add(1000 - current_ms + 1, self.set_time) # 1ms is added to ensure the clock is updated
else:
now = GLib.DateTime.new_now_local()
current_ms = now.get_second() * 1000 + now.get_microsecond() // 1000
GLib.timeout_add(60000 - current_ms + 1, self.set_time)
self.next_update = GLib.timeout_add(60000 - current_ms + 1, self.set_time)
return False # Do not rerun the current timeout; a new one has been scheduled
def get_config(self):
applets/clock/panorama-clock-options.cmb
@@ -0,0 +1,6 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<!DOCTYPE cambalache-project SYSTEM "cambalache-project.dtd">
<!-- Created with Cambalache 0.96.1 -->
<cambalache-project version="0.96.0" target_tk="gtk-4.0">
<ui template-class="ClockOptions" filename="panorama-clock-options.ui" sha256="d183407b347a36fc200e673b53bfbfff8682eca6dfe7d89bcaacca565f7248a8"/>
</cambalache-project>
applets/clock/panorama-clock-options.ui
@@ -0,0 +1,70 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.96.1 -->
<interface>
<!-- interface-name panorama-clock-options.ui -->
<requires lib="gtk" version="4.0"/>
<template class="ClockOptions" parent="GtkWindow">
<property name="default-height">320</property>
<property name="default-width">576</property>
<property name="title" translatable="yes">Clock options</property>
<child>
<object class="GtkScrolledWindow">
<child>
<object class="GtkViewport">
<child>
<object class="GtkBox">
<property name="margin-bottom">32</property>
<property name="margin-end">32</property>
<property name="margin-start">32</property>
<property name="margin-top">32</property>
<property name="orientation">vertical</property>
<property name="spacing">4</property>
<child>
<object class="GtkFrame">
<property name="label-widget">
<object class="GtkLabel">
<property name="label" translatable="yes">Formatting</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
</property>
<child>
<object class="GtkListBox">
<property name="activate-on-single-click">False</property>
<property name="selection-mode">none</property>
<child>
<object class="GtkListBoxRow">
<child>
<object class="GtkBox">
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Time format
<span font_weight="light">Using <tt>strftime()</tt> syntax. Note: using seconds may increase system load.</span></property>
<property name="use-markup">True</property>
<property name="wrap">True</property>
</object>
</child>
<child>
<object class="GtkEntry" id="format_entry">
<property name="placeholder-text">strftime format</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</template>
</interface>