from datetime import UTC, datetime
from libqtile.confreader import ConfigError
from libqtile.widget import base
from libqtile.widget.clock import Clock
[docs]
class VerticalClock(Clock):
"""
A simple but flexible text-based clock for vertical bars.
Unlike the ``Clock`` widget, ``VerticalClock`` will display text horizontally in the bar.
"""
orientations = base.ORIENTATION_VERTICAL
defaults = [
(
"format",
["%H", "%M"],
"A list of Python datetime format string. Each string is printed as a separate line.",
),
(
"foreground",
"fff",
"Text colour. A single string will be applied to all fields. "
"Alternatively, users can provide a list of strings with each colour being applied to the corresponding text format.",
),
(
"fontsize",
None,
"Font size. A single value will be applied to all fields. "
"Alternatively, users can provide a list of sizes with each size being applied to the corresponding text format.",
),
]
def __init__(self, **config):
Clock.__init__(self, **config)
self.add_defaults(VerticalClock.defaults)
self.layouts = []
def _to_list(self, value):
return [value] * len(self.format)
def _configure(self, qtile, bar):
base._Widget._configure(self, qtile, bar)
if self.fontsize is None:
self.fontsize = self._to_list(self.bar.size - self.bar.size / 5)
elif isinstance(self.fontsize, int):
self.fontsize = self._to_list(self.fontsize)
elif not isinstance(self.fontsize, list):
raise ConfigError("'fontsize' should be an integer or a list of integers.")
elif not all(isinstance(fontsize, int) for fontsize in self.fontsize):
raise ConfigError("'fontsize' should be of integers.")
elif len(self.fontsize) != len(self.format):
raise ConfigError("'fontsize' list should have same number of items as 'format'.")
if isinstance(self.foreground, str):
self.foreground = self._to_list(self.foreground)
elif not isinstance(self.foreground, list):
raise ConfigError("'foreground' should be a string or a list of strings.")
elif not all(isinstance(foreground, str) for foreground in self.foreground):
raise ConfigError("'foreground' list should be of strings.")
elif len(self.foreground) != len(self.format):
raise ConfigError("'foreground' list should have same number of items as 'format'.")
if self.padding is None:
self.padding = (sum(self.fontsize) / len(self.fontsize)) // 2
self.layouts = [
self.drawer.textlayout(
self.formatted_text,
fg,
self.font,
size,
self.fontshadow,
markup=self.markup,
)
for _, fg, size in zip(self.format, self.foreground, self.fontsize)
]
def calculate_length(self):
return sum(l.height + self.padding for l in self.layouts) + self.padding
def update(self, time):
for layout, fmt in zip(self.layouts, self.format):
layout.text = time.strftime(fmt)
layout.width = self.bar.size
self.draw()
@property
def can_draw(self):
return all(layout is not None for layout in self.layouts)
# adding .5 to get a proper seconds value because glib could
# theoreticaly call our method too early and we could get something
# like (x-1).999 instead of x.000
def poll(self):
if self.timezone:
now = datetime.now(UTC).astimezone(self.timezone)
else:
now = datetime.now(UTC).astimezone()
return now + self.DELTA
def draw(self):
if not self.can_draw:
return
offset = self.padding
self.drawer.clear(self.background or self.bar.background)
for layout in self.layouts:
self.drawer.ctx.save()
self.drawer.ctx.translate(0, offset)
layout.draw(0, 0)
offset += layout.height + self.padding
self.drawer.ctx.restore()
self.draw_at_default_position()
def finalize(self):
for layout in self.layouts:
layout.finalize()
layout = None
base._Widget.finalize(self)