Skip to content

Instantly share code, notes, and snippets.

@ltddev
Created February 12, 2015 23:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ltddev/ab361b55124120f6f8a4 to your computer and use it in GitHub Desktop.
Save ltddev/ab361b55124120f6f8a4 to your computer and use it in GitHub Desktop.
Clock Gadget
# coding: utf-8
import ui, speech, console
svgInHtml='''
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 400 400" width="400" height="400" version="1.0">
<defs>
<linearGradient id="a" x1="0%" y1="100%" x2="0%" y2="0%">
<stop offset="0%" style="stop-color:#777799"/>
<stop offset="100%" style="stop-color:#ffffff"/>
</linearGradient>
<linearGradient id="b" x1="0%" y1="100%" x2="0%" y2="0%">
<stop offset="0%" style="stop-color:#ffffff"/>
<stop offset="25%" style="stop-color:#b6b6cc"/>
<stop offset="40%" style="stop-color:#515177"/>
<stop offset="48%" style="stop-color:#ffffff"/>
<stop offset="56%" style="stop-color:#ffffff"/>
<stop offset="75%" style="stop-color:#8b8baa"/>
<stop offset="98%" style="stop-color:#efeff4"/>
<stop offset="100%" style="stop-color:#fbfbfc"/>
</linearGradient>
<linearGradient id="c" x1="0%" y1="100%" x2="0%" y2="0%">
<stop offset="0%" style="stop-color:#ffffff"/>
<stop offset="100%" style="stop-color:#777799"/>
</linearGradient>
<radialGradient id="d" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
<stop offset="0%" style="stop-color:#ffffff"/>
<stop offset="40%" style="stop-color:#ffffff"/>
<stop offset="70%" style="stop-color:#e6e6ee"/>
<stop offset="92%" style="stop-color:#b6b6cc"/>
<stop offset="100%" style="stop-color:#636388"/>
</radialGradient>
<radialGradient id="e" cx="50%" cy="150%" r="200%" fx="50%" fy="150%">
<stop offset="0%" style="stop-color:#ffffff;stop-opacity:0"/>
<stop offset="59%" style="stop-color:#ffffff;stop-opacity:0"/>
<stop offset="60%" style="stop-color:#ffffff;stop-opacity:0.6"/>
<stop offset="70%" style="stop-color:#ffffff;stop-opacity:0.3"/>
<stop offset="100%" style="stop-color:#ffffff;stop-opacity:0.0"/>
</radialGradient>
</defs>
<g transform="translate(200 200)">
<circle cx="0" cy="0" r="200" fill="#cecedd"/>
<circle cx="0" cy="0" r="196" stroke="url(#a)" stroke-width="5" fill="url(#b)"/>
<circle cx="0" cy="0" r="170" stroke="url(#c)" stroke-width="4" fill="url(#d)"/>
<circle cx="0" cy="0" r="172" stroke="#ffffff" stroke-width="0.5" fill="none"/>
<circle cx="0" cy="0" r="193.5" stroke="#ffffff" stroke-width="0.5" fill="none"/>
<g id="O">
<polygon points="4,155 4,130 -4,130 -4,155" style="fill:#777799;stroke:#313155;stroke-width:1"/>
<polygon points="4,-155 4,-130 -4,-130 -4,-155" style="fill:#777799;stroke:#313155;stroke-width:1"/>
</g>
<g transform="rotate(30)"><use xlink:href="#O"/></g>
<g transform="rotate(60)"><use xlink:href="#O"/></g>
<g transform="rotate(90)"><use xlink:href="#O"/></g>
<g transform="rotate(120)"><use xlink:href="#O"/></g>
<g transform="rotate(150)"><use xlink:href="#O"/></g>
<polygon id="h" points="6,-80 6,18 -6,18 -6,-80" style="fill:#232344">
<animateTransform id="ht" attributeType="xml" attributeName="transform" type="rotate" from="000" to="000" begin="0" dur="86400s" repeatCount="indefinite"/>
</polygon>
<polygon id="m" points="3.5,-140 3.5,23 -3.5,23 -3.5,-140" style="fill:#232344">
<animateTransform id="mt" attributeType="xml" attributeName="transform" type="rotate" from="000" to="000" begin="0" dur="3600s" repeatCount="indefinite"/>
</polygon>
<polygon id="s" points="2,-143 2,25 -2,25 -2,-143" style="fill:#232344">
<animateTransform id="st" attributeType="xml" attributeName="transform" type="rotate" from="000" to="000" begin="0" dur="60s" repeatCount="indefinite"/>
</polygon>
<circle cx="0" cy="0" r="163" fill="url(#e)"/>
</g>
<script type="text/javascript"><![CDATA[
var d = new Date();
var s = d.getSeconds();
var m = d.getMinutes() + s/60;
var h = (d.getHours() % 12) + m/60 + s/3600;
document.getElementById('st').setAttribute('from',s*6);
document.getElementById('mt').setAttribute('from',m*6);
document.getElementById('ht').setAttribute('from',h*30);
document.getElementById('st').setAttribute('to',360+s*6);
document.getElementById('mt').setAttribute('to',360+m*6);
document.getElementById('ht').setAttribute('to',360+h*30);
]]></script>
</svg>
'''
announce1 = "The current local time"
announce2 = "is"
numbersOneToTwenty =["zero","one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve","thirteen","fourteen","fifteen","sixteen","seventeen","eighteen","nineteen","twenty"]
thirty = "thirty"
forty = "forty"
fifty = "fifty"
am = "A.M"
pm = "P.M"
ampm = ""
oclock = "oclock"
# Turn off tracing when not required for simple debug/trace
traceFlag = False
speechDelay = 0.05
locale = "en-US"
#==============================================================================
class ClockGadget:
def __init__(self):
view = ui.load_view()
# Set announce duration to a default of once per every 15 minutes.
#view['rootview']['announceFreqTextfield'].text = '15'
#view['rootview']['announceFreqSlider'].value = .25 # every 1/4 hour.
# cache away the current location (if location services on for Pythonista)
self.locationString = self.createCurrentLocationString()
if traceFlag: print 'location string in __init__ is: ' + self.globalLocationString
if traceFlag: print ' location string to say is ' + locationString
# flag used to track preference off by default until/if user turns on.
#self.isAutoAnnounceTime = False
#self.timesPerHour = 0
webview = view['rootview']['webview']
# Place the analog clock in a location that is sensible for a "gadget"
# and disable touch or multitouch gestures to prevent unwannted resizing
# or movement to the underlying clock itself which has no need to do so.
webview.height = 20
webview.width = 10
webview.frame= 15,25,120,125
webview.multitouch_enabled = False
webview.touch_enabled = False
view.present('sidebar')
webview.load_html(svgInHtml)
#==============================================================================
def speaknowButtonTapped(self, sender):
if sender.name == 'speakTimeButton':
# Get the say seconds preference
isSaySeconds = sender.superview['speakSecondsSwitch'].value
timeMessage = self.getCurrentLocalTimeAnnouncement(isSaySeconds)
#location = self.createCurrentLocationString()
#print ' in but tap loc =' + location
#timeMessage += locationString
speech.say(timeMessage,locale, speechDelay)
#==============================================================================
def switchTapped(self,sender):
if traceFlag: print "Switch was tapped"
if sender.name == 'speakSecondsSwitch':
if traceFlag: print "speakSecondsSwitch tapped...current value is->" + str(sender.value)
#==============================================================================
def createCurrentLocationString(self):
import location
location.start_updates()
coordinates = location.get_location()
addressDictionaries = location.reverse_geocode(coordinates)
mycity = addressDictionaries[0]['City']
mycountry = addressDictionaries[0]['Country']
# if we can't get city and country not much point to continue
if mycountry == None or mycity == None:
return ""
locationString = "in " + mycity + " " + mycountry
location.stop_updates()
if traceFlag: print 'returning location string ->>' + locationString
return locationString
#==============================================================================
def getCurrentLocalTimeAnnouncement(self, isSaySeconds):
from datetime import datetime
##todo maybe get time and pass in from another function since calc twice?
now = datetime.now().time()
nowHour = now.hour
nowMinutes = now.minute
nowSeconds = now.second
# Get the current local hour
hourString = self.getCurrentHourString(nowHour)
if traceFlag: print "hourString is-->" + hourString
# Get the local current minute
minuteString = self.getCurrentMinutesString(nowMinutes)
# If it is right on the hour, add "o'clock"; if not, bad English grammar to use it in this context.
if nowMinutes == 0:
hourString = hourString + " " + oclock
# If minutes less than 10 US english adds ' oh'begore saying minutes, eg. 'Two oh one'for 2:01, but make sure not to append 'owe' if its right on the hour. That is, when 'oclock' never 'owe' preventing 'nine oclock owe p.m.' phrases.
if nowMinutes > 0 and nowMinutes < 10:
minuteString = "owe " + minuteString # Use 'owe' which is real word not to confuse speech synth.
# Not using 24 hour system but instead a 12 hour one which uses a.m and p.m
if nowHour >= 12:
ampm = pm
else:
ampm = am
announcement = ""
seconds = "seconds"
if isSaySeconds:
# English grammar, if seconds is 1, agrrement rules say use singular.
if nowSeconds == 1:
seconds = "second"
announcement = announce1 + " " + self.locationString + " " +announce2 + " " + hourString + " " + minuteString + " " + ampm + " " + str(nowSeconds) + " " + seconds + " "
else:
announcement = announce1 + " " + self.locationString + " " + announce2 + " " + hourString + " " + minuteString + " " + ampm
if traceFlag: print announcement
return announcement
#==============================================================================
def getCurrentHourString(self, nowHour):
if traceFlag: print "nowHour parameter to function-->>" + str(nowHour)
hourString = ""
if nowHour == 13 or nowHour == 1:
hourString = numbersOneToTwenty[1]
elif nowHour == 14 or nowHour == 2:
hourString = numbersOneToTwenty[2]
elif nowHour == 15 or nowHour == 3:
hourString = numbersOneToTwenty[3]
elif nowHour == 16 or nowHour == 4:
hourString = numbersOneToTwenty[4]
elif nowHour == 17 or nowHour == 5:
hourString = numbersOneToTwenty[5]
elif nowHour == 18 or nowHour == 6:
hourString = numbersOneToTwenty[6]
elif nowHour == 19 or nowHour == 7:
hourString = numbersOneToTwenty[7]
elif nowHour == 20 or nowHour == 8:
hourString = numbersOneToTwenty[8]
elif nowHour == 21 or nowHour == 9:
hourString = numbersOneToTwenty[9]
elif nowHour == 22 or nowHour == 10:
hourString = numbersOneToTwenty[10]
elif nowHour == 23 or nowHour == 11:
hourString = numbersOneToTwenty[11]
elif nowHour == 12 or nowHour == 0:
hourString = numbersOneToTwenty[12]
if traceFlag: print "Hour string to return -->>" + hourString
return hourString
#==============================================================================
def getCurrentMinutesString(self, nowMinutes):
minuteString = ""
# Handles minutes between 1 and 20
if nowMinutes > 0 and nowMinutes < 21:
minuteString = numbersOneToTwenty[nowMinutes]
# Handles minutes between 21 and 29
if nowMinutes > 20 and nowMinutes < 30:
if nowMinutes == 21:
minuteString = numbersOneToTwenty[20] + " " + numbersOneToTwenty[1]
elif nowMinutes == 22:
minuteString = numbersOneToTwenty[20] + " " + numbersOneToTwenty[2]
elif nowMinutes == 23:
minuteString = numbersOneToTwenty[20] + " " + numbersOneToTwenty[3]
elif nowMinutes == 24:
minuteString = numbersOneToTwenty[20] + " " + numbersOneToTwenty[4]
elif nowMinutes == 25:
minuteString = numbersOneToTwenty[20] + " " + numbersOneToTwenty[5]
elif nowMinutes == 26:
minuteString = numbersOneToTwenty[20] + " " + numbersOneToTwenty[6]
elif nowMinutes == 27:
minuteString = numbersOneToTwenty[20] + " " + numbersOneToTwenty[7]
elif nowMinutes == 28:
minuteString = numbersOneToTwenty[20] + " " + numbersOneToTwenty[8]
elif nowMinutes == 29:
minuteString = numbersOneToTwenty[20] + " " + numbersOneToTwenty[9]
# Handle 30 minutes
if nowMinutes == 30:
minuteString = thirty
# Handles minutes between 31 and 39
if nowMinutes > 30 and nowMinutes < 40:
if nowMinutes == 31:
minuteString = thirty + " " + numbersOneToTwenty[1]
elif nowMinutes == 32:
minuteString = thirty + " " + numbersOneToTwenty[2]
elif nowMinutes == 33:
minuteString = thirty + " " + numbersOneToTwenty[3]
elif nowMinutes == 34:
minuteString = thirty + " " + numbersOneToTwenty[4]
elif nowMinutes == 35:
minuteString = thirty + " " + numbersOneToTwenty[5]
elif nowMinutes == 36:
minuteString = thirty + " " + numbersOneToTwenty[6]
elif nowMinutes == 37:
minuteString = thirty + " " + numbersOneToTwenty[7]
elif nowMinutes == 38:
minuteString = thirty + " " + numbersOneToTwenty[8]
elif nowMinutes == 39:
minuteString = thirty + " " + numbersOneToTwenty[9]
# Handles 40 minutes
if nowMinutes == 40:
minuteString = forty
if nowMinutes > 40 and nowMinutes < 50:
if nowMinutes == 41:
minuteString = forty + " " + numbersOneToTwenty[1]
elif nowMinutes == 42:
minuteString = forty + " " + numbersOneToTwenty[2]
elif nowMinutes == 43:
minuteString = forty + " " + numbersOneToTwenty[3]
elif nowMinutes == 44:
minuteString = forty + " " + numbersOneToTwenty[4]
elif nowMinutes == 45:
minuteString = forty + " " + numbersOneToTwenty[5]
elif nowMinutes == 46:
minuteString = forty + " " + numbersOneToTwenty[6]
elif nowMinutes == 47:
minuteString = forty + " " + numbersOneToTwenty[7]
elif nowMinutes == 48:
minuteString = forty + " " + numbersOneToTwenty[8]
elif nowMinutes == 49:
minuteString = forty + " " + numbersOneToTwenty[9]
# Handle 50 minutes
if nowMinutes == 50:
minuteString = fifty
# Handle minutes between 51 and 59
if nowMinutes > 50 and nowMinutes < 60:
if nowMinutes == 51:
minuteString = fifty + " " + numbersOneToTwenty[1]
elif nowMinutes == 52:
minuteString = fifty + " " + numbersOneToTwenty[2]
elif nowMinutes == 53:
minuteString = fifty + " " + numbersOneToTwenty[3]
elif nowMinutes == 54:
minuteString = fifty + " " + numbersOneToTwenty[4]
elif nowMinutes == 55:
minuteString = fifty + " " + numbersOneToTwenty[5]
elif nowMinutes == 56:
minuteString = fifty + " " + numbersOneToTwenty[6]
elif nowMinutes == 57:
minuteString = fifty + " " + numbersOneToTwenty[7]
elif nowMinutes == 58:
minuteString = fifty + " " + numbersOneToTwenty[8]
elif nowMinutes == 59:
minuteString = fifty + " " + numbersOneToTwenty[9]
if traceFlag: print "Minute string to return -->>" + minuteString
return minuteString
#==============================================================================
if traceFlag: console.clear()
ClockGadget()
[{"class":"View","attributes":{"name":"clock_gadget","tint_color":"RGBA(0.000000,0.478000,1.000000,1.000000)","background_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","enabled":true,"border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","flex":""},"frame":"{{0, 0}, {125, 233}}","nodes":[{"class":"View","attributes":{"enabled":true,"flex":"","name":"rootview","border_width":1,"alpha":0,"border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","uuid":"F9CA3888-3F34-4DCA-9C86-DDB5EEE7C867"},"frame":"{{0, 0}, {127, 230}}","nodes":[{"class":"WebView","attributes":{"name":"webview","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","uuid":"266E1FE4-C52A-4785-B092-498E9C313A96","enabled":true,"scales_to_fit":true,"flex":"WH"},"frame":"{{6, 6}, {115, 94}}","nodes":[]},{"class":"Button","attributes":{"font_size":11,"enabled":true,"flex":"","font_bold":false,"name":"speakTimeButton","uuid":"ADC2750C-A6F5-4311-8138-F368D068F9E9","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","border_width":1,"action":"self.speaknowButtonTapped","image_name":"ionicons-ios7-mic-32","title":"Say Now "},"frame":"{{12, 180}, {100.5, 35}}","nodes":[]},{"class":"Switch","attributes":{"enabled":true,"flex":"","name":"speakSecondsSwitch","value":false,"action":"self.switchTapped","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","uuid":"42117A61-0D45-465F-9711-4AEA39422352"},"frame":"{{12, 141}, {51, 31}}","nodes":[]},{"class":"Label","attributes":{"font_size":10,"enabled":true,"text":"Speak Seconds","flex":"","name":"speakSecondsLabel","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","text_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","alignment":"left","uuid":"039FAE80-19F0-4D9E-9BC1-EEAEFD306EE6"},"frame":"{{12, 123.5}, {80.5, 20}}","nodes":[]}]}]}]
@cclauss
Copy link

cclauss commented Feb 13, 2015

If you make this a repo instead of a gist, I would be willing to submit some pull requests to tighten up the code.

@cclauss
Copy link

cclauss commented Feb 13, 2015

Three ideas:

  • Use str.split() to build list of strings. (Shouldn't it be called numbersZeroToTwenty?)
numbersOneToTwenty = 'zero one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen twenty'.split()
  • Simplify getCurrentHourString().
def new_getCurrentHourString(self, nowHour):
    assert -1 < nowHour < 24, 'error: invalid hour {}.'.format(nowHour)
    return numbersOneToTwenty[nowHour % 12 or 12]
ampm = am if nowHour < 12 else pm 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment