| 
 | 1 | +import time  | 
 | 2 | +from picovector import ANTIALIAS_BEST, PicoVector, Polygon, Transform  | 
 | 3 | +from presto import Presto, Buzzer  | 
 | 4 | +from touch import Button  | 
 | 5 | + | 
 | 6 | +presto = Presto(ambient_light=True)  | 
 | 7 | +display = presto.display  | 
 | 8 | +WIDTH, HEIGHT = display.get_bounds()  | 
 | 9 | + | 
 | 10 | +# Centre points for the display  | 
 | 11 | +CX = WIDTH // 2  | 
 | 12 | +CY = HEIGHT // 2  | 
 | 13 | + | 
 | 14 | +# We'll need this for the touch element of the screen  | 
 | 15 | +touch = presto.touch  | 
 | 16 | + | 
 | 17 | +# Pico Vector  | 
 | 18 | +vector = PicoVector(display)  | 
 | 19 | +vector.set_antialiasing(ANTIALIAS_BEST)  | 
 | 20 | +t = Transform()  | 
 | 21 | + | 
 | 22 | +vector.set_font("Roboto-Medium.af", 96)  | 
 | 23 | +vector.set_font_letter_spacing(100)  | 
 | 24 | +vector.set_font_word_spacing(100)  | 
 | 25 | +vector.set_transform(t)  | 
 | 26 | + | 
 | 27 | +BLACK = display.create_pen(0, 0, 0)  | 
 | 28 | + | 
 | 29 | +# Setup the buzzer. The Presto piezo is on pin 43.  | 
 | 30 | +buzzer = Buzzer(43)  | 
 | 31 | + | 
 | 32 | + | 
 | 33 | +class Tomato(object):  | 
 | 34 | +    def __init__(self):  | 
 | 35 | + | 
 | 36 | +        self.hue = 0  | 
 | 37 | +        self.background = display.create_pen_hsv(self.hue, 0.8, 1.0)  # We'll use this one for the background.  | 
 | 38 | +        self.foreground = display.create_pen_hsv(self.hue, 0.5, 1.0)  # Slightly lighter for foreground elements.  | 
 | 39 | +        self.text_colour = display.create_pen_hsv(self.hue, 0.2, 1.0)  | 
 | 40 | + | 
 | 41 | +        # Time constants.  | 
 | 42 | +        # Feel free to change these to ones that work better for you.  | 
 | 43 | +        self.TASK = 25 * 60  | 
 | 44 | +        self.SHORT = 10 * 60  | 
 | 45 | +        self.LONG = 30 * 60  | 
 | 46 | + | 
 | 47 | +        # How long the completion alert should be played (seconds)  | 
 | 48 | +        self.alert_duration = 2  | 
 | 49 | +        self.alert_start_time = 0  | 
 | 50 | + | 
 | 51 | +        self.is_break_time = False  | 
 | 52 | +        self.start_time = 0  | 
 | 53 | +        self.tasks_complete = 0  | 
 | 54 | +        self.running = False  | 
 | 55 | +        self.paused = False  | 
 | 56 | +        self.time_elapsed = 0  | 
 | 57 | +        self.current_timer = self.TASK  | 
 | 58 | + | 
 | 59 | +        # We'll use a rect with rounded corners for the background.  | 
 | 60 | +        self.background_rect = Polygon()  | 
 | 61 | +        self.background_rect.rectangle(0, 0, WIDTH, HEIGHT, (10, 10, 10, 10))  | 
 | 62 | + | 
 | 63 | +        self.foreground_rect = Polygon()  | 
 | 64 | +        self.foreground_rect.rectangle(10, 10, WIDTH - 20, HEIGHT - 120, (10, 10, 10, 10))  | 
 | 65 | + | 
 | 66 | +        # Touch button  | 
 | 67 | +        self.start_button = Button(CX - 56, HEIGHT - 75, CX - 2, 50)  | 
 | 68 | +        x, y, w, h = self.start_button.bounds  | 
 | 69 | +        self.start = Polygon()  | 
 | 70 | +        self.start.rectangle(x, y, w, h, (10, 10, 10, 10))  | 
 | 71 | +        self.start_shadow = Polygon()  | 
 | 72 | +        self.start_shadow.rectangle(x + 3, y + 3, w, h, (10, 10, 10, 10))  | 
 | 73 | + | 
 | 74 | +    # Update the pens for the background, foreground and text elements based on the given hue.  | 
 | 75 | +    def update_pens(self, hue):  | 
 | 76 | +        self.hue = hue  | 
 | 77 | +        self.background = display.create_pen_hsv(self.hue, 0.8, 1.0)  | 
 | 78 | +        self.foreground = display.create_pen_hsv(self.hue, 0.5, 1.0)  | 
 | 79 | +        self.text_colour = display.create_pen_hsv(self.hue, 0.2, 1.0)  | 
 | 80 | + | 
 | 81 | +    def draw(self):  | 
 | 82 | + | 
 | 83 | +        # Clear the screen  | 
 | 84 | +        display.set_pen(BLACK)  | 
 | 85 | +        display.clear()  | 
 | 86 | + | 
 | 87 | +        # Draw the background rect with rounded corners  | 
 | 88 | +        display.set_pen(self.background)  | 
 | 89 | +        vector.draw(self.background_rect)  | 
 | 90 | + | 
 | 91 | +        # Draw the foreground rect, this is where we will show the time remaining.  | 
 | 92 | +        display.set_pen(self.foreground)  | 
 | 93 | +        vector.draw(self.foreground_rect)  | 
 | 94 | + | 
 | 95 | +        # Draw the button with drop shadow  | 
 | 96 | +        vector.draw(self.start_shadow)  | 
 | 97 | +        display.set_pen(self.text_colour)  | 
 | 98 | +        vector.draw(self.start)  | 
 | 99 | + | 
 | 100 | +        # Draw the button text, the text shown here depends on the current timer state  | 
 | 101 | +        vector.set_font_size(24)  | 
 | 102 | +        display.set_pen(self.foreground)  | 
 | 103 | + | 
 | 104 | +        if not self.running:  | 
 | 105 | +            if self.is_break_time:  | 
 | 106 | +                vector.text("Start Break", self.start_button.bounds[0] + 8, self.start_button.bounds[1] + 33)  | 
 | 107 | +            else:  | 
 | 108 | +                vector.text("Start Task", self.start_button.bounds[0] + 12, self.start_button.bounds[1] + 33)  | 
 | 109 | +        elif self.running and self.paused:  | 
 | 110 | +            vector.text("Resume", self.start_button.bounds[0] + 22, self.start_button.bounds[1] + 33)  | 
 | 111 | +        else:  | 
 | 112 | +            vector.text("Pause", self.start_button.bounds[0] + 32, self.start_button.bounds[1] + 33)  | 
 | 113 | + | 
 | 114 | +        display.set_pen(self.text_colour)  | 
 | 115 | +        text = self.return_string()  | 
 | 116 | +        vector.set_font_size(96)  | 
 | 117 | +        x, y, w, h = vector.measure_text(text, x=0, y=0, angle=None)  | 
 | 118 | +        tx = int(CX - (w // 2))  | 
 | 119 | +        ty = int(CY - (h // 2)) + 10  | 
 | 120 | +        vector.text(text, tx, ty)  | 
 | 121 | + | 
 | 122 | +    def run(self):  | 
 | 123 | +        self.stop_buzzer()  | 
 | 124 | + | 
 | 125 | +        if self.is_break_time:  | 
 | 126 | +            if self.tasks_complete < 4:  | 
 | 127 | +                self.current_timer = self.SHORT  | 
 | 128 | +                self.update_pens(0.55)  | 
 | 129 | +            else:  | 
 | 130 | +                self.current_timer = self.LONG  | 
 | 131 | +                self.update_pens(0.55)  | 
 | 132 | +        else:  | 
 | 133 | +            self.current_timer = self.TASK  | 
 | 134 | +            self.update_pens(0.0)  | 
 | 135 | + | 
 | 136 | +        if not self.running:  | 
 | 137 | +            self.reset()  | 
 | 138 | +            self.running = True  | 
 | 139 | +            self.start_time = time.time()  | 
 | 140 | +        elif self.running and not self.paused:  | 
 | 141 | +            self.paused = True  | 
 | 142 | +        elif self.running and self.paused:  | 
 | 143 | +            self.paused = False  | 
 | 144 | +            self.start_time = time.time() - self.time_elapsed  | 
 | 145 | + | 
 | 146 | +    def reset(self):  | 
 | 147 | +        self.start_time = 0  | 
 | 148 | +        self.time_elapsed = 0  | 
 | 149 | + | 
 | 150 | +    def start_buzzer(self):  | 
 | 151 | +        self.alert_start_time = time.time()  | 
 | 152 | +        buzzer.set_tone(150)  | 
 | 153 | + | 
 | 154 | +    def stop_buzzer(self):  | 
 | 155 | +        buzzer.set_tone(-1)  | 
 | 156 | +        self.alert_start_time = 0  | 
 | 157 | + | 
 | 158 | +    def update(self):  | 
 | 159 | + | 
 | 160 | +        if time.time() - self.alert_start_time >= self.alert_duration:  | 
 | 161 | +            self.stop_buzzer()  | 
 | 162 | + | 
 | 163 | +        if self.running and not self.paused:  | 
 | 164 | + | 
 | 165 | +            self.time_elapsed = time.time() - self.start_time  | 
 | 166 | + | 
 | 167 | +            if self.time_elapsed >= self.current_timer:  | 
 | 168 | +                self.running = False  | 
 | 169 | +                self.start_buzzer()  | 
 | 170 | +                if not self.is_break_time:  | 
 | 171 | +                    if self.tasks_complete < 4:  | 
 | 172 | +                        self.tasks_complete += 1  | 
 | 173 | +                    else:  | 
 | 174 | +                        self.tasks_complete = 0  | 
 | 175 | +                self.is_break_time = not self.is_break_time  | 
 | 176 | + | 
 | 177 | +    # Return the remaining time formatted in a string for displaying with vector text.  | 
 | 178 | +    def return_string(self):  | 
 | 179 | +        minutes, seconds = divmod(self.current_timer - self.time_elapsed, 60)  | 
 | 180 | +        return f"{minutes:02d}:{seconds:02d}"  | 
 | 181 | + | 
 | 182 | +    def pressed(self):  | 
 | 183 | +        return self.start_button.is_pressed()  | 
 | 184 | + | 
 | 185 | + | 
 | 186 | +# Create an instance of our timer object  | 
 | 187 | +timer = Tomato()  | 
 | 188 | + | 
 | 189 | +while True:  | 
 | 190 | + | 
 | 191 | +    if timer.pressed():  | 
 | 192 | +        while timer.pressed():  | 
 | 193 | +            touch.poll()  | 
 | 194 | +        timer.run()  | 
 | 195 | + | 
 | 196 | +    timer.draw()  | 
 | 197 | +    timer.update()  | 
 | 198 | +    presto.update()  | 
0 commit comments