diff options
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/pomodoro.py | 220 |
1 files changed, 122 insertions, 98 deletions
diff --git a/examples/pomodoro.py b/examples/pomodoro.py index 2094448..df15be4 100644 --- a/examples/pomodoro.py +++ b/examples/pomodoro.py @@ -1,130 +1,154 @@ """ -REQUIREMENTS ------------- -* libnotify -* python-gobject - - -INSTALLATION ------------- -Please add the following lines in your ~/.i3/config file:: - - bindsym $mod+minus exec echo "stop" > ~/.i3/pomodoro.log - bindsym $mod+equal exec echo "pause" > ~/.i3/pomodoro.log - bindsym $mod+plus exec echo "start" > ~/.i3/pomodoro.log +Pomodoro countdown on i3bar originally written by @Fandekasp (Adrien Lemaire) +Requires: + - libnotify + - python-gobject + - python-keybinder """ -from datetime import timedelta from gi.repository import Notify -import os.path -from time import time +from gi.repository import GObject as gobject +#FIXME: keybinder gobject collides with Notify but its not blocking +import keybinder +import gtk -LOG_FILE = os.path.join(os.path.expanduser('~'), '.i3', 'pomodoro.log') -LONG_REST = 1800 # 60 * 30 -NB_REST = 4 -POMO = 1500 # 60 * 25 -POSITION = 0 -SHORT_REST = 300 # 60 * 5 +from threading import Thread +from time import time +from time import sleep +KEY_PAUSE = '<Mod1>B' #Alt + B(reak) +KEY_START = '<Mod1>P' #Alt + P(omodoro) +KEY_STOP = '<Mod1>R' #Alt + R(eset) +MAX_BREAKS = 4 +POSITION = 0 +TIMER_POMODORO = 25 * 60 +TIMER_BREAK = 5 * 60 +TIMER_LONG_BREAK = 15 * 60 -class Py3status: +pomo_thread = None +gobject.threads_init() + +class Pomodoro(Thread): """ - Pomodoro method + Handles Pomodoro and Break countdowns """ - pomo = POMO - rest = SHORT_REST - nb_rest = NB_REST + def __init__(self): + """ + Well, default values + """ + Thread.__init__(self) + self.kill = False + self.callback('stop') @property def response(self): - return { - 'cached_until' : time(), - 'full_text': '', - 'name': 'pomodoro', - } - - def pomodoro(self, json, i3status_config): """ - This function will update a timer in the i3status bar for 25 minutes, - then print a red "Break time" message for 5 minutes. + Return the response full_text string """ - status = self.get_status() + return '%s (%d)' % (self.prefix, self.timer) + def callback(self, status): + """ + Called on a keybinder event + """ + self.status = status if status == 'stop': - self.pomo = POMO - self.rest = SHORT_REST - self.nb_rest = NB_REST - return (POSITION, self.response) + self.prefix = 'Pomodoro' + self.status = 'stop' + self.timer = TIMER_POMODORO + self.breaks = 1 - # Else run normally - if not self.pomo: - if status != 'pause': - self.rest -= 1 - timer = self.rest - self.full_text = 'Break time' - color = 'color_bad' - else: - if status != 'pause': - self.pomo -= 1 - timer = self.pomo - self.full_text = 'Pomodoro' - color = 'color_degraded' - - if status == 'pause': - color = 'color_good' + elif status == 'start': + self.prefix = 'Pomodoro' + self.timer = TIMER_POMODORO - if not timer: - # Pomodoro or Rest is finished, send notification - self.send_notification() + elif status == 'pause': + self.prefix = 'Break #%d' % self.breaks + if self.breaks > MAX_BREAKS: + self.timer = TIMER_LONG_BREAK + self.breaks = 1 + else: + self.breaks += 1 + self.timer = TIMER_BREAK - response = self.response - response.update({ - 'color': i3status_config[color], - 'full_text': '{}: {}'.format( - self.full_text, str(timedelta(seconds=timer))[2:]), - }) - - return (POSITION, response) + def stop(self): + """ + Exit nicely + """ + self.kill = True - def get_status(self): + def notify(self): """ - Read the log file and get the status value. Value is modifying via - i3 key mapping. + Print a visual message + """ + try: + Notify.init('') + notification = Notify.Notification.new ( + '<b>Attention:</b>', + '{} is finished'.format(self.response), + 'dialog-information', + ) + notification.show() + except Exception as err: + pass - Value can be: - * start - * pause - * stop (default) + def run(self): + """ + Infinite loop setting the keybinder and waiting for key events """ - if not os.path.isfile(LOG_FILE): - return 'stop' + bindings = { + 'pause': KEY_PAUSE, + 'start': KEY_START, + 'stop' : KEY_STOP, + } + for status, keystr in bindings.items(): + keybinder.bind(keystr, self.callback, status) - with open(LOG_FILE, 'r') as f: - status = f.read() + while not self.kill: + gtk.main_iteration(block=False) + if self.status != 'stop' and self.timer > 0: + self.timer -= 1 + if self.timer == 0: + self.notify() + sleep(1) - return status.rstrip('\n') + for status, keystr in bindings.items(): + try: + keybinder.unbind(keystr) + except Exception as e: + pass - def send_notification(self): +class Py3status: + """ + Main py3status class which spawns a thread for Pomodoro keybinder + """ + def kill(self): """ - Print a visual message + Exit nicely """ + pomo_thread.stop() - Notify.init('Pomodoro') - notification = Notify.Notification.new ( - '<b>Attention:</b>', - '{} is finished'.format(self.full_text), - 'dialog-information', - ) - notification.show() + def pomodoro(self, json, i3status_config): + """ + Pomodoro response handling + """ + response = {'full_text' : '', 'name' : 'pomodoro'} + + global pomo_thread - if not self.rest: - # Rest is finished, reinitialize data - self.nb_rest -= 1 - self.pomo = POMO - self.rest = SHORT_REST + if not pomo_thread: + pomo_thread = Pomodoro() + pomo_thread.start() + else: + response['full_text'] = pomo_thread.response + + if pomo_thread.status == 'start': + response['color'] = i3status_config['color_good'] + elif pomo_thread.status == 'pause': + response['color'] = i3status_config['color_degraded'] + else: + response['color'] = i3status_config['color_bad'] - if not self.nb_rest: - # Number of small rest reached, go for long rest - self.rest = LONG_REST - self.nb_rest = NB_REST + response['cached_until'] = time() + return (POSITION, response) |
