0

Pie Chart Clock Geeklet for Geektool

Here is another fun clock Geeklet for you to use with GeekTool on the Mac.  This one displays three pie chart layers that gives you sort of an abstract looking clock

Here's how it looks on my desktop:



The script creates the three layers separately so you can position them any way you like.  In my example I stacked the images on top of each other with decreasing levels of transparency.

This Geeklet uses ImageMagick so make sure you have that installed before trying to set this up.  If you're not sure how, just install MacPorts and then type this in terminal

sudo port install imagemagick

Once you have imagemagick ready to go, drag a shell module from the GeekTools preference pane to the desktop and past this script into the command field

#!/usr/bin/env python

# File pie_chart_clock.py
# Created 9/5/2011
# Scott McClung
# Generates pie chart images based on the current system time
# Pie chart calculations the build_slices function were taken from piechart.py 
# written by Gregory Pittman. He has a detailed explanation of drawing .svg pie
# charts at http://wiki.scribus.net/canvas/Making_a_Pie_Chart
##########################################
from __future__ import division

import os,time,math

# ============= CONFIG ==============
# location of ImageMagick
converter='/opt/local/bin/convert'
# this is the filename/path that will be used for the resulting images
hours_img_file='$HOME/pie_chart_clock_hrs.png'
minutes_img_file='$HOME/pie_chart_clock_mins.png'
seconds_img_file='$HOME/pie_chart_clock_secs.png'
# you can change these values to determine the size of the hour
# and minute pie charts
hours_radius=240
minutes_radius=160
seconds_radius=80
# colors for the time slice of each chart
hours_color='white'
minutes_color='white'
seconds_color='white'
# color for the background
# enter "none" for a transparent background
hours_background='none'
minutes_background='none'
seconds_background='none'
# add shadow under the pie charts by entering a value for the shadow depth
# for no shadow set the depth value to 0
hours_shadow_depth = 10
minutes_shadow_depth = 4
seconds_shadow_depth = 4

# ===================================

# populate the current time values
hrs=int(time.strftime("%I"))
mins=int(time.strftime("%M"))
secs=int(time.strftime("%S"))
# the two slices of each pie are the current time slice and the
# slice representing the time left to complete the circle
hrs_slices = [hrs,(12-hrs)]
mins_slices = [mins,(60-mins)]
secs_slices = [secs,(60-secs)]
# There are always two slices of the pies and we'll display one or the other based
# the time of day.  We'll put both of them in a list for now.
hrs_path = []
mins_path = []
secs_path = []

def build_slices(radius, slices, parity):
 path = []
 total = 0
 i = 0
 seg = 0
 startx = radius + 20  # The screen x-origin: center of pie chart (a little padding is added to radius)
 starty = radius + 20  # The screen y-origin: center of pie chart (a little padding is added to radius)
 lastx = radius    # Starting coordinates of 
 lasty = 0     # the first arc

 for n in slices:
  total = total + n  # we have to do this ahead, since we need the total for the next for loop

 for n in slices:
  arc = "0"                   # default is to draw short arc (< 180 degrees)
  seg = n/total * 360 + seg   # this angle will be current plus all previous
  if ((n/total * 360) > 180): # just in case this piece is > 180 degrees
   arc = "1"
  radseg = math.radians(seg)  # we need to convert to radians for cosine, sine functions
  nextx = math.cos(radseg) * radius
  nexty = (math.sin(radseg) * radius)
  if (n == 0 and not parity):
   arc = "1"
   nexty -= 1
  # The weirdly placed minus signs [eg, (-(lasty))] are due to the fact that
  # our calculations are for a graph with positive Y values going up, but on the
  # screen positive Y values go down.
  path.append("path 'M "+str(startx)+","+str(starty) + " l "+str(lastx)+","+str(-(lasty))+" a" + str(radius) + "," + str(radius) + " 0 " + arc + ",0 "+str(nextx - lastx)+","+str(-(nexty - lasty))+ " z'")
  
  # We are writing the XML commands one segment at a time, so we abandon old points
  # we don't need anymore, and nextx becomes lastx for the next segment
  lastx = nextx
  lasty = nexty
  i += 1

 return path

def create_shadow(depth):
 if (depth>0):
  shadow = " \( +clone -background black -shadow 60x"+str(depth)+"+"+str(depth)+"+"+str(depth)+" \) +swap -background none -mosaic "
 else:
  shadow = ""
 return shadow

def select_path(parity,time,arr_path):
 if(parity):
  return arr_path[0]
 elif(time == 0):
  return arr_path[0]
 else:
  return arr_path[1]

# build hours pie
parity = bool(time.strftime("%p")=="AM")
hrs_path = build_slices(hours_radius,hrs_slices,parity)
hrs_img_size = str((hours_radius + 20) * 2)
hrs_path_command = select_path(parity,hrs,hrs_path)

# build minutes pie
parity = not bool(hrs & 1)
mins_path = build_slices(minutes_radius,mins_slices, parity)
mins_img_size = str((minutes_radius + 20) * 2)
mins_path_command = select_path(parity,mins,mins_path)

#build seconds pie
parity = not bool(mins & 1)
secs_path = build_slices(seconds_radius,secs_slices, parity)
secs_img_size = str((seconds_radius + 20) * 2)
secs_path_command = select_path(parity,secs,secs_path)


# Draw the images using ImageMagick
# the nature of the pie chart math results in the path drawing the pie charts
# counter clockwise and starting from the 15 minute mark.  The simplest solution
# is to just have ImageMagik rotate and flop the image after it's drawn.
command_string = converter
command_string += " -size "+hrs_img_size+"x"+hrs_img_size+" xc:"+hours_background+" -fill "+hours_color+" -stroke none -draw \""+hrs_path_command+"\" -rotate \"-90\" -flop "+ create_shadow(hours_shadow_depth) + hours_img_file
os.system(command_string)

command_string = converter
command_string += " -size "+mins_img_size+"x"+mins_img_size+" xc:"+minutes_background+" -fill "+minutes_color+" -stroke none -draw \""+mins_path_command+"\" -rotate \"-90\" -flop " + create_shadow(minutes_shadow_depth) + minutes_img_file
os.system(command_string)

command_string = converter
command_string += " -size "+secs_img_size+"x"+secs_img_size+" xc:"+seconds_background+" -fill "+seconds_color+" -stroke none -draw \""+secs_path_command+"\" -rotate \"-90\" -flop " + create_shadow(seconds_shadow_depth) + seconds_img_file
os.system(command_string)

Set it to update as frequently as you like.  I don't recommend setting to update too often if you're using a laptop as it will shorten your battery life a bit.  I find running it every 5 or 10 seconds still has the visual effect I want but doesn't chew up too many cycles.

Then drag 3 image modules to the desktop and point one to each of the three images created in your home directory.
  • pie_chart_clock_hrs.png
  • pie_chart_clock_mins.png
  • pie_chart_clock_secs.png

Set them to update on the same schedule as your shell module and then position and size to taste.

You can change the file names and location, pie radius, pie slice color, background color, and shadow depth by updating the values in the CONFIG section of the script if you'd like a different look and feel.

Thanks to Gregory Pittman and his demo on generating SVG pie charts with python.  You can see his work here: http://wiki.scribus.net/canvas/Making_a_Pie_Chart  Could not have figured this out without that tutorial.

If you installed ImageMagick using a different method than MacPorts, you will probably need to update the CONFIG section to point to the correct installation location for your system.

Feel free to leave some feedback or post a screenshot of your GeekTool setup!

Cheers!
Scott