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

 __init__.py

View raw Download
text/x-script.python • 5.1 kiB
Python script, ASCII text executable
        
            
1
import os
2
from pathlib import Path
3
import panorama_panel
4
5
import gi
6
gi.require_version("Gtk", "4.0")
7
8
from gi.repository import Gtk, GLib, Gio, Gdk
9
10
11
SECOND_PLACEHOLDERS = ("%c", "%s", "%S", "%T", "%X")
12
13
14
module_directory = Path(__file__).resolve().parent
15
16
17
@Gtk.Template(filename=str(module_directory / "panorama-clock-options.ui"))
18
class ClockOptions(Gtk.Window):
19
__gtype_name__ = "ClockOptions"
20
format_entry: Gtk.Entry = Gtk.Template.Child()
21
22
def __init__(self, **kwargs):
23
super().__init__(**kwargs)
24
25
self.connect("close-request", lambda *args: self.destroy())
26
27
28
class ClockApplet(panorama_panel.Applet):
29
name = "Clock"
30
description = "Read the current time and date"
31
32
def __init__(self, orientation=Gtk.Orientation.HORIZONTAL, config=None):
33
super().__init__(orientation=orientation, config=config)
34
if config is None:
35
config = {}
36
self.button = Gtk.MenuButton()
37
self.button.set_has_frame(False) # flat look
38
self.label = Gtk.Label()
39
self.button.set_child(self.label)
40
41
# Create the monthly calendar
42
self.popover = Gtk.Popover()
43
self.track_popover(self.popover)
44
self.calendar = Gtk.Calendar()
45
self.calendar.set_show_week_numbers(True)
46
self.popover.set_child(self.calendar)
47
self.button.set_popover(self.popover)
48
49
self.append(self.button)
50
51
self.formatting = config.get("formatting", "%c")
52
# Some placeholders require second precision, but not all of them. If not required,
53
# use minute precision
54
self.has_second_precision = any(placeholder in self.formatting for placeholder in SECOND_PLACEHOLDERS)
55
self.next_update = None
56
self.set_time()
57
58
self.context_menu = self.make_context_menu()
59
self.track_popover(self.context_menu)
60
61
right_click_controller = Gtk.GestureClick()
62
right_click_controller.set_button(3)
63
right_click_controller.connect("pressed", self.show_context_menu)
64
65
self.add_controller(right_click_controller)
66
67
action_group = Gio.SimpleActionGroup()
68
options_action = Gio.SimpleAction.new("options", None)
69
options_action.connect("activate", self.show_options)
70
action_group.add_action(options_action)
71
self.insert_action_group("applet", action_group)
72
73
self.options_window = None
74
75
def make_context_menu(self):
76
menu = Gio.Menu()
77
menu.append("Clock _options", "applet.options")
78
context_menu = Gtk.PopoverMenu.new_from_model(menu)
79
context_menu.set_has_arrow(False)
80
context_menu.set_parent(self)
81
context_menu.set_halign(Gtk.Align.START)
82
context_menu.set_flags(Gtk.PopoverMenuFlags.NESTED)
83
return context_menu
84
85
def show_context_menu(self, gesture, n_presses, x, y):
86
rect = Gdk.Rectangle()
87
rect.x = int(x)
88
rect.y = int(y)
89
rect.width = 1
90
rect.height = 1
91
92
self.context_menu.set_pointing_to(rect)
93
self.context_menu.popup()
94
95
def update_formatting(self, entry):
96
self.formatting = entry.get_text()
97
# Some placeholders require second precision, but not all of them. If not required,
98
# use minute precision
99
self.has_second_precision = any(placeholder in self.formatting for placeholder in SECOND_PLACEHOLDERS)
100
if self.next_update is not None:
101
GLib.source_remove(self.next_update)
102
self.next_update = None
103
self.set_time()
104
105
def show_options(self, _0=None, _1=None):
106
if self.options_window is None:
107
self.options_window = ClockOptions()
108
self.options_window.format_entry.set_text(self.formatting)
109
self.options_window.format_entry.connect("changed", self.update_formatting)
110
111
def reset_window(*args):
112
self.options_window = None
113
114
self.options_window.connect("close-request", reset_window)
115
self.options_window.present()
116
117
def set_time(self):
118
datetime = GLib.DateTime.new_now_local()
119
formatted_time = datetime.format(self.formatting)
120
if formatted_time is not None:
121
self.label.set_text(datetime.format(self.formatting))
122
else:
123
self.label.set_text("Invalid time formatting")
124
return False
125
126
if self.has_second_precision:
127
current_ms = GLib.DateTime.new_now_local().get_microsecond() // 1000
128
self.next_update = GLib.timeout_add(1000 - current_ms + 1, self.set_time) # 1ms is added to ensure the clock is updated
129
else:
130
now = GLib.DateTime.new_now_local()
131
current_ms = now.get_second() * 1000 + now.get_microsecond() // 1000
132
self.next_update = GLib.timeout_add(60000 - current_ms + 1, self.set_time)
133
return False # Do not rerun the current timeout; a new one has been scheduled
134
135
def get_config(self):
136
return {"formatting": self.formatting}
137
138
def set_panel_position(self, position):
139
self.popover.set_position(panorama_panel.OPPOSITE_POSITION[position])
140
self.button.set_direction(panorama_panel.POSITION_TO_ARROW[panorama_panel.OPPOSITE_POSITION[position]])
141