diff options
-rw-r--r--[-rwxr-xr-x] | clock.py | 277 |
1 files changed, 271 insertions, 6 deletions
@@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python # -*- coding: utf-8 -*- # # Code released in the Public Domain. You can do whatever you want @@ -31,9 +31,12 @@ color code: - Days dark red: #B20008 - Months purple: #5E008C - Years brown: #9A5200 +- Hours draggable dark blue: #00588C +- Minutes draggable dark green: #008009 +- Seconds draggable dark red: #B20008 -An analog clock is also very helpfull to determine where the North is when you +An analog clock is also very helpful to determine where the North is when you don't have a compass! Check http://www.wikihow.com/Find-True-North-Without-a-Compass And knowing where the True North is, you can build a Sun Clock! @@ -84,7 +87,7 @@ except ImportError: OLD_TOOLBAR = True import math -from datetime import datetime +import datetime import threading import re @@ -101,6 +104,11 @@ from timewriter import TimeWriter import dbus import os +# LOGGING MODULE FOR DEBUGGING - WILL BE REMOVED WHEN COMPLETE +import logging +logging.basicConfig(level=logging.DEBUG) +# END LOGGING + # The display modes of the clock _MODE_SIMPLE_CLOCK = 0 _MODE_NICE_CLOCK = 1 @@ -310,6 +318,17 @@ class ClockActivity(activity.Activity): button.connect("toggled", self._speak_time_clicked_cb) display_toolbar.insert(button, -1) + # A separator between the two groups of buttons + separator = gtk.SeparatorToolItem() + separator.set_draw(True) + display_toolbar.insert(separator, -1) + + # Another button to speak aloud the time + button = ToggleToolButton("change-time") + button.set_tooltip(_('Drag the hands to change the time')) + button.connect("toggled", self._change_time_clicked_cb) + display_toolbar.insert(button, -1) + def _make_display(self): """Prepare the display of the clock. @@ -378,6 +397,11 @@ class ClockActivity(activity.Activity): self._speak_time = button.get_active() if self._speak_time: self._write_and_speak(True) + + def _change_time_clicked_cb(self, button): + """The user clicked the "change time" button to drag the hands + """ + self._clock.enable_dragging(button.get_active()) def _minutes_changed_cb(self, clock): """Minutes have changed on the clock face: we have to update @@ -491,7 +515,10 @@ class ClockFace(gtk.DrawingArea): self.initialized = False # The time on the clock face - self._time = datetime.now() + self._time = datetime.datetime.now() + self._time_off_h = 0 + self._time_off_m = 0 + self._time_off_s = 0 self._old_minute = self._time.minute # Update the clock only when the widget is active to save @@ -519,18 +546,39 @@ class ClockFace(gtk.DrawingArea): # XO Medium Red self._COLOR_SECONDS = "#E6000A" + # XO Dark Blue + self._COLOR_HOURS_DRAG = "#00588C" + + # XO Dark Green + self._COLOR_MINUTES_DRAG = "#008009" + + # XO Dark Red + self._COLOR_SECONDS_DRAG = "#B20008" + # White self._COLOR_WHITE = "#FFFFFF" # Black self._COLOR_BLACK = "#000000" + + # Some dragging variables + self._dragging_enabled = False + self._old_mouse_x = 0 + self._old_mouse_y = 0 + self._dragging_hand = -1 # gtk.Widget signals self.connect("expose-event", self._expose_cb) self.connect("size-allocate", self._size_allocate_cb) + # We want to be notified when the mouse is clicked, moved, + # or released, so that the hands can be dragged. + self.connect("button_press_event", self._mouse_press_cb) + self.connect("motion_notify_event", self._mouse_move_cb) + self.connect("button_release_event", self._mouse_release_cb) + # The masks to capture the events we are interested in - self.add_events(gdk.EXPOSURE_MASK | gdk.VISIBILITY_NOTIFY_MASK) + self.add_events(gdk.EXPOSURE_MASK | gdk.VISIBILITY_NOTIFY_MASK | gdk.BUTTON_PRESS_MASK | gdk.POINTER_MOTION_MASK | gdk.BUTTON_RELEASE_MASK) # Define a new signal to notify the application when minutes # change. If the user wants to display the time in full @@ -781,6 +829,12 @@ class ClockFace(gtk.DrawingArea): int(self._center_y + self._radius * 0.5 * - math.cos(math.pi / 6 * hours + math.pi / 360 * minutes))) cr.stroke() + cr.set_source_rgba(*style.Color(self._COLOR_HOURS_DRAG).get_rgba()) + cr.line_to(int(self._center_x + self._radius * 0.45 * + math.sin(math.pi / 6 * hours + math.pi / 360 * minutes)), + int(self._center_y + self._radius * 0.45 * + - math.cos(math.pi / 6 * hours + math.pi / 360 * minutes))) + cr.stroke() # Minute hand: # The minute hand is rotated 6 degrees (pi/30 r) per minute @@ -793,6 +847,17 @@ class ClockFace(gtk.DrawingArea): math.sin(math.pi / 30 * minutes)), int(self._center_y + self._radius * 0.7 * - math.cos(math.pi / 30 * minutes))) + cr.set_source_rgba(*style.Color(self._COLOR_MINUTES_DRAG).get_rgba()) + cr.line_to(int(self._center_x + self._radius * 0.65 * + math.sin(math.pi / 30 * minutes)), + int(self._center_y + self._radius * 0.65 * + - math.cos(math.pi / 30 * minutes))) + cr.stroke() + cr.set_source_rgba(*style.Color(self._COLOR_MINUTES_DRAG).get_rgba()) + cr.line_to(int(self._center_x + self._radius * 0.65 * + math.sin(math.pi / 30 * minutes)), + int(self._center_y + self._radius * 0.65 * + - math.cos(math.pi / 30 * minutes))) cr.stroke() # Seconds hand: @@ -807,6 +872,12 @@ class ClockFace(gtk.DrawingArea): int(self._center_y + self._radius * 0.8 * - math.cos(math.pi / 30 * seconds))) cr.stroke() + cr.set_source_rgba(*style.Color(self._COLOR_SECONDS_DRAG).get_rgba()) + cr.line_to(int(self._center_x + self._radius * 0.75 * + math.sin(math.pi / 30 * seconds)), + int(self._center_y + self._radius * 0.75 * + - math.cos(math.pi / 30 * seconds))) + cr.stroke() def _draw_numbers(self): """Draw the numbers of the hours. @@ -844,7 +915,9 @@ font_desc="Sans Bold 40">%d</span></markup>') % (i + 1) """Called every seconds to update the time value. """ # update the time and force a redraw of the clock - self._time = datetime.now() + self._time = datetime.datetime.now() + time_offset = datetime.timedelta(hours=self._time_off_h,minutes=self._time_off_m,seconds=self._time_off_s) + self._time += time_offset gobject.idle_add(self._redraw_canvas) @@ -871,6 +944,9 @@ font_desc="Sans Bold 40">%d</span></markup>') % (i + 1) save resources. """ return self._active + + def enable_dragging(self, enable): + self._dragging_enabled = enable def _set_active(self, active): """Set the activity state of the clock face. When Sugar @@ -885,5 +961,194 @@ font_desc="Sans Bold 40">%d</span></markup>') % (i + 1) # And update again the clock every seconds. gobject.timeout_add(1000, self._update_cb) + + def _mouse_press_cb(self, widget, event): + """Handler to drag the clock hands or bars. Hands work + only at the ends. + """ + + if self._dragging_enabled: + if self._mode == _MODE_NICE_CLOCK: + self._find_dragging_hand(event.x, event.y) + elif self._mode == _MODE_SIMPLE_CLOCK: + self._find_dragging_hand(event.x, event.y) + elif self._mode == _MODE_DIGITAL_CLOCK: + self._drag_bar(event.x, event.y) + else: + msg = "Unknown display mode: %d." % self._mode + raise ValueError(msg) + + def _mouse_move_cb(self, widget, event): + + if (self._dragging_enabled) & (self._dragging_hand != -1): + if self._mode == _MODE_NICE_CLOCK: + self._drag_hand(event.x, event.y) + elif self._mode == _MODE_SIMPLE_CLOCK: + self._drag_hand(event.x, event.y) + elif self._mode == _MODE_DIGITAL_CLOCK: + self._drag_bar(event.x, event.y) + else: + msg = "Unknown display mode: %d." % self._mode + raise ValueError(msg) + + def _mouse_release_cb(self, widget, event): + + self._dragging_hand = -1 + + def _find_dragging_hand(self, x, y): + + # Initialize some variables + hours = self._time.hour + minutes = self._time.minute + seconds = self._time.second + + # Absolute X location of the end of the hour, minute, and + # second hands + lst_x = [int(self._center_x + self._radius * 0.5 * + math.sin(math.pi / 6 * hours + math.pi / 360 * minutes)), + int(self._center_x + self._radius * 0.7 * + math.sin(math.pi / 30 * minutes)), + int(self._center_x + self._radius * 0.8 * + math.sin(math.pi / 30 * seconds))] + + # Y locations + lst_y = [int(self._center_y + self._radius * 0.5 * + - math.cos(math.pi / 6 * hours + math.pi / 360 * minutes)), + int(self._center_y + self._radius * 0.7 * + - math.cos(math.pi / 30 * minutes)), + int(self._center_y + self._radius * 0.8 * + - math.cos(math.pi / 30 * seconds))] + + # Find the nearest hand to the mouse + closest_x = min(lst_x, key=lambda z:abs(z-x)) + closest_y = min(lst_y, key=lambda z:abs(z-y)) + + # If we're not sure which one, quit + if lst_x.index(closest_x) != lst_y.index(closest_y): + return + + # Only drag 30 px or less from the end, meaning 15px radius + if abs(closest_x - lst_x[lst_x.index(closest_x)]) > 15: + return + if abs(closest_y - lst_y[lst_y.index(closest_y)]) > 15: + return + + self._dragging_hand = lst_x.index(closest_x) + self._old_mouse_x = x + self._old_mouse_y = y + + def _drag_hand(self, x, y): + + logging.info("_drag_hand") + + old_x = int(self._old_mouse_x) + old_y = int(self._old_mouse_y) + x = int(x) + y = int(y) + dist = math.sqrt(2 * self._radius**2) + hand = self._dragging_hand + logging.debug("old x: %s",str(old_x)) + logging.debug("old y: %s",str(old_y)) + logging.debug("x: %s",str(x)) + logging.debug("y: %s",str(y)) + logging.debug("dist: %s",str(dist)) + + # What eigth of the clock is the hand in? + if hand == 0: # Hour + hour = self._time.hour + if hour > 11: + hour -= 12 + eigth = round(((hour * 60 + self._time.minute) / 97.5) + 0.5) + elif hand == 1: # Minute + eigth = round((self._time.minute / 7.5) + 0.5) + elif hand == 2: # Second + eigth = round((self._time.second / 7.5) + 0.5) + logging.debug("Eigth: %s",str(eigth)) + + # What direction is the mouse moving in? + if (((eigth < 5) & (y > old_y)) | ((eigth > 4) & (y < old_y))) | ((((eigth > 6) | (eigth < 3)) & (x > old_x)) | (((eigth < 7) & (eigth > 2)) & (x < old_x))): + direction = 1 # clockwise + elif (((eigth < 5) & (y < old_y)) | ((eigth > 4) & (y > old_y))) | ((((eigth > 6) | (eigth < 3)) & (x < old_x)) | (((eigth < 7) & (eigth > 2)) & (x < old_x))): + direction = 0 # counterclockwise + logging.debug("Dir: %s",str(direction)) + + # Change the time + change_by = math.sqrt( (x - old_x)**2 + (y - old_y)**2 ) + logging.debug("change_by: %s",str(change_by)) + if hand == 0: + change_by = round(change_by / (dist / 2700)) + if not direction: + change_by = change_by * (-1) + logging.info("Hour hand being dragged.") + logging.debug("Second to %s from %s",str(change_by),str(self._time.second)) + self._time_off_s += change_by + elif hand == 1: + change_by = round(change_by / (dist / 900)) + if not direction: + change_by = change_by * (-1) + logging.info("Minute hand being dragged.") + logging.debug("Second to %s from %s",str(change_by),str(self._time.second)) + self._time_off_s += change_by + elif hand == 2: + change_by = round(change_by / (dist / 15)) + if not direction: + change_by = change_by * (-1) + logging.info("Second hand being dragged.") + logging.debug("Second to %s from %s",str(change_by),str(self._time.second)) + self._time_off_s += change_by + + # Update the clock and self._time + self._update_cb() + + self._old_mouse_x = x + self._old_mouse_y = y + + def _drag_bar(self, orig_x, y): + + logging.info("_drag_bar") + logging.debug("mouse x: %s",str(orig_x)) + logging.debug("y: %s",str(y)) + logging.debug("self._radius: %s",str(self._radius)) + + # Initialize some variables + hours = self._time.hour + minutes = self._time.minute + seconds = self._time.second + x = orig_x - round(self._center_x - self._radius) + logging.debug("x: %s",str(x)) + + self._dragging_hand = 0 + + # The Y positions of the bars + lst_y = [round(self._center_y - 0.75 * self._radius), + round(self._center_y - 0.60 * self._radius), + round(self._center_y - 0.45 * self._radius)] + + # Find the bar the mouse is on + closest_y = min(lst_y, key=lambda z:abs(z-y)) + + # Make sure we're actually touching a bar + if abs(closest_y - lst_y[lst_y.index(closest_y)]) > round(0.15 * self._radius)/2: + return + + dragging = lst_y.index(closest_y) + logging.debug("dragging: %s",str(dragging)) + + # Find and set the new time + if dragging == 0: + new_time = int((24 * x) / (2 * self._radius)) + logging.debug("Hour to %s",str(new_time)) + self._time_off_h = new_time - hours + elif dragging == 1: + new_time = int((60 * x) / (2 * self._radius)) + logging.debug("Minute to %s",str(new_time)) + self._time_off_m = new_time - minutes + elif dragging == 2: + new_time = int((60 * x) / (2 * self._radius)) + logging.debug("Second to %s",str(new_time)) + self._time_off_s = new_time - seconds + + # Update the clock and self._time + self._update_cb() active = property(_get_active, _set_active) |