Created
February 12, 2015 23:48
-
-
Save ltddev/ab361b55124120f6f8a4 to your computer and use it in GitHub Desktop.
Clock Gadget
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[{"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":[]}]}]}] |
Three ideas:
- Use
str.split()
to build list of strings. (Shouldn't it be callednumbersZeroToTwenty
?)
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]
- Use conditional expressions to increase readability and save space.
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
If you make this a repo instead of a gist, I would be willing to submit some pull requests to tighten up the code.