from datetime import datetime, timedelta
from time import time
from libqtile.command.base import expose_command
from libqtile.utils import send_notification
from libqtile.widget import base
[docs]
class Pomodoro(base.InLoopPollText):
"""Pomodoro technique widget"""
defaults = [
("num_pomodori", 4, "Number of pomodori to do in a cycle"),
("length_pomodori", 25, "Length of one pomodori in minutes"),
("length_short_break", 5, "Length of a short break in minutes"),
("length_long_break", 15, "Length of a long break in minutes"),
("color_inactive", "ff0000", "Colour then pomodoro is inactive"),
("color_active", "00ff00", "Colour then pomodoro is running"),
("color_break", "ffff00", "Colour then it is break time"),
("notification_on", True, "Turn notifications on"),
("prefix_inactive", "POMODORO", "Prefix when app is inactive"),
("prefix_active", "", "Prefix then app is active"),
("prefix_break", "B ", "Prefix during short break"),
("prefix_long_break", "LB ", "Prefix during long break"),
("prefix_paused", "PAUSE", "Prefix during pause"),
(
"update_interval",
1,
"Update interval in seconds, if none, the "
"widget updates whenever the event loop is idle.",
),
]
STATUS_START = "start"
STATUS_INACTIVE = "inactive"
STATUS_ACTIVE = "active"
STATUS_BREAK = "break"
STATUS_LONG_BREAK = "long_break"
STATUS_PAUSED = "paused"
status = "inactive"
paused_status = None
end_time = datetime.now()
time_left = None
pomodoros = 1
def __init__(self, **config):
base.InLoopPollText.__init__(self, "", **config)
self.add_defaults(Pomodoro.defaults)
self.prefix = {
"inactive": self.prefix_inactive,
"active": self.prefix_active,
"break": self.prefix_break,
"long_break": self.prefix_long_break,
"paused": self.prefix_paused,
}
self.add_callbacks(
{
"Button1": self.toggle_break,
"Button3": self.toggle_active,
}
)
def tick(self):
self.update(self.poll())
return self.update_interval - time() % self.update_interval
def _update(self):
if self.status in [self.STATUS_INACTIVE, self.STATUS_PAUSED]:
return
if self.end_time > datetime.now() and self.status != self.STATUS_START:
return
if self.status == self.STATUS_ACTIVE and self.pomodoros == self.num_pomodori:
self.status = self.STATUS_LONG_BREAK
self.end_time = datetime.now() + timedelta(minutes=self.length_long_break)
self.pomodoros = 1
if self.notification_on:
self._send_notification(
"normal",
"Please take a long break! End Time: " + self.end_time.strftime("%H:%M"),
)
return
if self.status == self.STATUS_ACTIVE:
self.status = self.STATUS_BREAK
self.end_time = datetime.now() + timedelta(minutes=self.length_short_break)
self.pomodoros += 1
if self.notification_on:
self._send_notification(
"normal",
"Please take a short break! End Time: " + self.end_time.strftime("%H:%M"),
)
return
self.status = self.STATUS_ACTIVE
self.end_time = datetime.now() + timedelta(minutes=self.length_pomodori)
if self.notification_on:
self._send_notification(
"critical",
"Please start with the next Pomodori! End Time: "
+ self.end_time.strftime("%H:%M"),
)
return
def _get_text(self):
self._update()
if self.status in [self.STATUS_INACTIVE, self.STATUS_PAUSED]:
self.layout.colour = self.color_inactive
return self.prefix[self.status]
time_left = self.end_time - datetime.now()
if self.status == self.STATUS_ACTIVE:
self.layout.colour = self.color_active
else:
self.layout.colour = self.color_break
time_string = f"{time_left.seconds // 3600:d}:{time_left.seconds % 3600 // 60:02d}:{time_left.seconds % 60:02d}"
return self.prefix[self.status] + time_string
[docs]
@expose_command()
def toggle_break(self):
if self.status == self.STATUS_INACTIVE:
self.status = self.STATUS_START
return
if self.paused_status is None:
self.paused_status = self.status
self.time_left = self.end_time - datetime.now()
self.status = self.STATUS_PAUSED
if self.notification_on:
self._send_notification("low", "Pomodoro has been paused")
else:
self.status = self.paused_status
self.paused_status = None
self.end_time = self.time_left + datetime.now()
if self.notification_on:
if self.status == self.STATUS_ACTIVE:
status = "Pomodoro"
else:
status = "break"
self._send_notification(
"normal",
f"Please continue on {status}! End Time: " + self.end_time.strftime("%H:%M"),
)
[docs]
@expose_command()
def toggle_active(self):
if self.status != self.STATUS_INACTIVE:
self.status = self.STATUS_INACTIVE
if self.notification_on:
self._send_notification("critical", "Pomodoro has been suspended")
else:
self.status = self.STATUS_START
def _send_notification(self, urgent, message):
send_notification("Pomodoro", message, urgent=urgent)
def poll(self):
return self._get_text()