Skip to content

Instantly share code, notes, and snippets.

@oskar456
Created May 1, 2018 09:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oskar456/93521849f74e41fb7eab313d0b5b57ad to your computer and use it in GitHub Desktop.
Save oskar456/93521849f74e41fb7eab313d0b5b57ad to your computer and use it in GitHub Desktop.
PyGTK Cairo LED studio clock
#! /usr/bin/env python2
import pygtk
pygtk.require('2.0')
import gtk
import glib
import cairo
import math
import datetime
# Create a GTK+ widget on which we will draw using Cairo
class Screen(gtk.DrawingArea):
# Draw in response to an expose-event
__gsignals__ = { "expose-event": "override" }
# Handle the expose-event by drawing
def do_expose_event(self, event):
# Create the cairo context
cr = self.window.cairo_create()
# Restrict Cairo to the exposed area; avoid extra work
cr.rectangle(event.area.x, event.area.y,
event.area.width, event.area.height)
cr.clip()
self.draw(cr, *self.window.get_size())
def draw(self, cr, width, height):
# Fill the background with gray
cr.set_source_rgb(0.5, 0.5, 0.5)
cr.rectangle(0, 0, width, height)
cr.fill()
class FullscreenToggler(object):
def __init__(self, window, keysym=gtk.keysyms.F11):
self.window = window
self.keysym = keysym
self.window_is_fullscreen = False
self.window.connect_object('window-state-event',
FullscreenToggler.on_window_state_change,
self)
def on_window_state_change(self, event):
self.window_is_fullscreen = bool(
gtk.gdk.WINDOW_STATE_FULLSCREEN & event.new_window_state)
def toggle(self, event):
if event.keyval == self.keysym:
if self.window_is_fullscreen:
self.window.unfullscreen()
else:
self.window.fullscreen()
# GTK mumbo-jumbo to show the widget in a window and quit when it's closed
def run(Widget, fullscreen=False):
window = gtk.Window()
window.set_default_size(500, 500)
window.connect("delete-event", gtk.main_quit)
toggler = FullscreenToggler(window)
window.connect_object('key-press-event', FullscreenToggler.toggle, toggler)
widget = Widget()
widget.show()
window.add(widget)
window.present()
if fullscreen:
window.fullscreen()
gtk.main()
## Do all your testing in Shapes ##
def packfont(font):
pfont = []
for glyph in font:
pchar = []
for line in glyph.split():
for char in line.strip():
pchar.append(True if char == '*' else False)
pfont.append(pchar)
return tuple(pfont)
class LEDclock(Screen):
"""
A widget generating precise LED-like studio clock.
"""
font = ("""
-***-
*---*
*---*
*---*
*---*
*---*
-***-
""","""
--*--
-**--
--*--
--*--
--*--
--*--
-***-
""","""
-***-
*---*
----*
-***-
*----
*----
*****
""","""
-***-
*---*
----*
--**-
----*
*---*
-***-
""","""
---*-
--**-
-*-*-
*--*-
*****
---*-
---*-
""","""
*****
*----
*----
****-
----*
*---*
-***-
""","""
--**-
-*---
*----
****-
*---*
*---*
-***-
""","""
*****
----*
---*-
--*--
-*---
-*---
-*---
""","""
-***-
*---*
*---*
-***-
*---*
*---*
-***-
""","""
-***-
*---*
*---*
-****
----*
*---*
-***-
"""
)
pfont = packfont(font)
radius = 22
grid = 60
led_on = cairo.SolidPattern(0.8,0,0)
led_off = cairo.SolidPattern(0.2,0,0)
def __init__(self):
super(LEDclock, self).__init__()
self.time = datetime.datetime.now()
self.on_timer()
def on_timer(self):
time = datetime.datetime.now()
msecstowait = 999 - time.microsecond/1000
#print "waiting", msecstowait, "microsecond", time.microsecond
glib.timeout_add(msecstowait, self.on_timer)
if self.time.second != time.second:
self.time = time
self.queue_draw()
return False
def _draw_matrix(self, cr, digit):
"""
Draws a 7x5 dot matrix digit. Origin is the central dot.
"""
assert 0 <= digit < 10
ledmatrix = self.pfont[digit]
for i,led in enumerate(ledmatrix):
x = (i%5 - 2) * self.grid
y = (i/5 - 3) * self.grid
cr.set_source(self.led_on if led else self.led_off)
cr.arc(x, y, self.radius, 0, 2*math.pi)
cr.fill()
def _draw_clock_digits(self, cr):
"""
Draw dot-matrix digits.
"""
#draw big digits
cr.save()
cr.translate(-10*self.grid, 0)
self._draw_matrix(cr, self.time.hour/10)
cr.translate(6*self.grid, 0)
self._draw_matrix(cr, self.time.hour%10)
cr.translate(8*self.grid, 0)
self._draw_matrix(cr, self.time.minute/10)
cr.translate(6*self.grid, 0)
self._draw_matrix(cr, self.time.minute%10)
cr.restore()
#draw small digits
cr.save()
cr.translate(0, 8*self.grid)
cr.scale(0.7, 0.7)
cr.translate(-3*self.grid, 0)
self._draw_matrix(cr, self.time.second/10)
cr.translate(6*self.grid, 0)
self._draw_matrix(cr, self.time.second%10)
cr.restore()
#draw central colon
cr.set_source(self.led_on)
cr.arc(0, -self.grid, self.radius, 0, 2*math.pi)
cr.fill()
cr.arc(0, self.grid, self.radius, 0, 2*math.pi)
cr.fill()
def _draw_radial_leds(self, cr, distance, angle, data):
"""
Draws radial led scale.
@param distance Scale radius
@param angle Angle between LEDs -- in radians
@param data An itreable of bool data representing LED status
"""
cr.save()
for led in data:
cr.set_source(self.led_on if led else self.led_off)
cr.arc(0, -distance, self.radius, 0, 2*math.pi)
cr.fill()
cr.rotate(angle)
cr.restore()
def _draw_led_scale(self, cr, mode=1):
"""
Draws circular scale with LEDs.
"""
if mode==4:
points = (True if i <= self.time.second/5 else False for i in range(12))
else:
points = (True for i in range(12))
self._draw_radial_leds(cr, 13.8*self.grid, math.pi/6, points)
if mode==3:
ledcircle = (True if i == self.time.second else False for i in range(60))
elif mode==2:
ledcircle = (True if 0 <= (self.time.second - i)%60 < 3 else False for i in range(60))
elif mode==4:
ledcircle = (True if i%5==0 or i <= self.time.second else False for i in range(60))
else:
ledcircle = (True if i <= self.time.second else False for i in range(60))
self._draw_radial_leds(cr, 15*self.grid, math.pi/30, ledcircle)
def draw(self, cr, width, height):
cr.set_source_rgb(0, 0, 0)
cr.rectangle(0, 0, width, height)
cr.fill()
#set up resolution independent canvas with
#origin in the middle and radius of 1000 points
diameter = min(width, height)
cr.translate(width/2.0, height/2.0)
cr.scale(diameter/2000.0, diameter/2000.0)
self._draw_clock_digits(cr)
self._draw_led_scale(cr, 1)
if __name__ == "__main__":
import sys
if len(sys.argv) > 1 and sys.argv[1] == 'full':
run(LEDclock, True)
else:
run(LEDclock)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment