Source code for i3pystatus.timer

import time

from i3pystatus import IntervalModule
from i3pystatus.core.command import execute
from i3pystatus.core.util import TimeWrapper


class TimerState:
    stopped = 0
    running = 1
    overflow = 2


[docs]class Timer(IntervalModule): """ Timer module to remind yourself that there probably is something else you should be doing right now. Main features include: - Set custom time interval with click events. - Different output formats triggered when remaining time is less than `x` seconds. - Execute custom python function or external command when timer overflows (or reaches zero depending on how you look at it). .. rubric:: Available formatters Time formatters are available to show the remaining time. These include ``%h``, ``%m``, ``%s``, ``%H``, ``%M``, ``%S``. See :py:class:`.TimeWrapper` for detailed description. The ``format_custom`` setting allows you to display different formats when certain amount of seconds is remaining. This setting accepts list of tuples which contain time in seconds, format string and color string each. See the default settings for an example: - ``(0, "+%M:%S", "#ffffff")`` - Use this format after overflow. White text with red background set by the urgent flag. - ``(60, "-%M:%S", "#ffa500")`` - Change color to orange in last minute. - ``(3600, "-%M:%S", "#00ff00")`` - Hide hour digits when remaining time is less than one hour. Only first matching rule is applied (if any). .. rubric:: Callbacks Module contains three mouse event callback methods: - :py:meth:`.start` - Default: Left click starts (or adds) 5 minute countdown. - :py:meth:`.increase` - Default: Upscroll/downscroll increase/decrease time by 1 minute. - :py:meth:`.reset` - Default: Right click resets timer. Two new event settings were added: - ``on_overflow`` - Executed when remaining time reaches zero. - ``on_reset`` - Executed when timer is reset but only if overflow occurred. These settings accept either a python callable object or a string with shell command. Python callbacks should be non-blocking and without any arguments. Here is an example that plays a short sound file in 'loop' every 60 seconds until timer is reset. (``play`` is part of ``SoX`` - the Swiss Army knife of audio manipulation) :: on_overflow = "play -q /path/to/sound.mp3 pad 0 60 repeat -" on_reset = "pkill -SIGTERM -f 'play -q /path/to/sound.mp3 pad 0 60 repeat -'" """ interval = 1 on_leftclick = ["start", 300] on_rightclick = "reset" on_upscroll = ["increase", 60] on_downscroll = ["increase", -60] settings = ( ("format", "Default format that is showed if no ``format_custom`` " "rules are matched."), ("format_stopped", "Format showed when timer is inactive."), "color", "color_stopped", "format_custom", ("overflow_urgent", "Set urgent flag on overflow."), "on_overflow", "on_reset", ) format = '-%h:%M:%S' format_stopped = "T" color = "#00ff00" color_stopped = "#ffffff" format_custom = [ (0, "+%M:%S", "#ffffff"), (60, "-%M:%S", "#ffa500"), (3600, "-%M:%S", "#00ff00"), ] overflow_urgent = True on_overflow = None on_reset = None def init(self): self.compare = 0 self.state = TimerState.stopped if not self.format_custom: self.format_custom = [] def run(self): if self.state is not TimerState.stopped: diff = self.compare - time.time() if diff < 0 and self.state is TimerState.running: self.state = TimerState.overflow if self.on_overflow: if callable(self.on_overflow): self.on_overflow() else: execute(self.on_overflow) fmt = self.format color = self.color for rule in self.format_custom: if diff < rule[0]: fmt = rule[1] color = rule[2] break urgent = self.overflow_urgent and self.state is TimerState.overflow self.output = { "full_text": format(TimeWrapper(abs(diff), fmt)), "color": color, "urgent": urgent, } else: self.output = { "full_text": self.format_stopped, "color": self.color_stopped, }
[docs] def start(self, seconds=300): """ Starts timer. If timer is already running it will increase remaining time instead. :param int seconds: Initial time. """ if self.state is TimerState.stopped: self.compare = time.time() + abs(seconds) self.state = TimerState.running elif self.state is TimerState.running: self.increase(seconds)
[docs] def increase(self, seconds): """ Change remaining time value. :param int seconds: Seconds to add. Negative value subtracts from remaining time. """ if self.state is TimerState.running: new_compare = self.compare + seconds if new_compare > time.time(): self.compare = new_compare
[docs] def reset(self): """ Stop timer and execute ``on_reset`` if overflow occurred. """ if self.state is not TimerState.stopped: if self.on_reset and self.state is TimerState.overflow: if callable(self.on_reset): self.on_reset() else: execute(self.on_reset) self.state = TimerState.stopped