Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorScimonster <tehalmightyscimonster@gmail.com>2013-01-06 13:48:22 (GMT)
committer Scimonster <tehalmightyscimonster@gmail.com>2013-01-06 13:48:22 (GMT)
commit79237927679f32a26f2a02215d05dcb8833098b4 (patch)
tree8a79ae73a1b55f372f381f96df1c3c95cc5b550f
parent9a2f4b5de9beda76045c4466c03abe1daf19f1a6 (diff)
Added the adjustable time feature for the Google Code-in 2012. http://www.google-melange.com/gci/task/view/google/gci2012/8084205
-rw-r--r--[-rwxr-xr-x]clock.py277
1 files changed, 271 insertions, 6 deletions
diff --git a/clock.py b/clock.py
index 48ff674..e2bcb50 100755..100644
--- a/clock.py
+++ b/clock.py
@@ -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)