Created
May 1, 2018 09:28
-
-
Save oskar456/93521849f74e41fb7eab313d0b5b57ad to your computer and use it in GitHub Desktop.
PyGTK Cairo LED studio clock
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
#! /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