# Copyright (c) 2017 Zordsdavini
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import subprocess
from time import time
from datetime import datetime, timedelta
from . import base
[docs]class Pomodoro(base.ThreadPoolText):
"""Pomodoro technique widget"""
orientations = base.ORIENTATION_HORIZONTAL
defaults = [
("fmt", "%s", "fmt"),
("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
notified = False
end_time = datetime.now()
time_left = None
pomodoros = 1
def __init__(self, **config):
base.ThreadPoolText.__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
}
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 = "%i:%i:%s" % (time_left.seconds // 3600, time_left.seconds % 3600 // 60, time_left.seconds % 60)
return self.prefix[self.status] + time_string
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",
"Please continue on %s! End Time: " % status + self.end_time.strftime("%H:%M")
)
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):
subprocess.Popen(['notify-send', "Pomodoro", message, '-u', urgent, '-t', '5000'])
def poll(self):
return self.fmt % self._get_text()
def button_press(self, x, y, button):
"""What to do when press a mouse button over the Pomodoro widget.
LEFT BUTTON: pause/resume current status or start
RIGHT BUTTON: toggle activity
"""
if button == 1:
self._toggle_break()
elif button == 3:
self._toggle_active()
base.ThreadedPollText.button_press(self, x, y, button)