[Mud-builder-commits] r280 - in trunk/packages: . horizon.pkg
jaffa at garage.maemo.org
jaffa at garage.maemo.org
Wed Jul 15 01:37:38 EEST 2009
Author: jaffa
Date: 2009-07-15 01:37:34 +0300 (Wed, 15 Jul 2009)
New Revision: 280
Added:
trunk/packages/horizon.pkg/
trunk/packages/horizon.pkg/Makefile
trunk/packages/horizon.pkg/framework.py
trunk/packages/horizon.pkg/horizon
trunk/packages/horizon.pkg/horizon-128.png
trunk/packages/horizon.pkg/horizon-26.png
trunk/packages/horizon.pkg/horizon-40.png
trunk/packages/horizon.pkg/horizon-48.png
trunk/packages/horizon.pkg/horizon-64.png
trunk/packages/horizon.pkg/horizon.desktop
trunk/packages/horizon.pkg/horizon.service
trunk/packages/horizon.pkg/main.py
trunk/packages/horizon.pkg/mud.xml
trunk/packages/horizon.pkg/providers.py
Log:
Add Horizon source package, which tests accelerometers
Added: trunk/packages/horizon.pkg/Makefile
===================================================================
--- trunk/packages/horizon.pkg/Makefile (rev 0)
+++ trunk/packages/horizon.pkg/Makefile 2009-07-14 22:37:34 UTC (rev 280)
@@ -0,0 +1,9 @@
+compile:
+ echo No compilation step necessary
+
+install:
+ dh_install main.py providers.py framework.py usr/lib/python2.5/site-packages/horizon
+ dh_install horizon usr/bin
+
+clean:
+ echo No clean step necesary
Added: trunk/packages/horizon.pkg/framework.py
===================================================================
--- trunk/packages/horizon.pkg/framework.py (rev 0)
+++ trunk/packages/horizon.pkg/framework.py 2009-07-14 22:37:34 UTC (rev 280)
@@ -0,0 +1,198 @@
+#! /usr/bin/env python
+#
+# Cairo Gtk/Hildon/OSSO framework (c) Andrew Flegg 2009
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Released under the Artistic Licence
+# Based on the version at http://www.tortall.net/mu/wiki/PyGTKCairoTutorial.
+#
+import pygtk
+pygtk.require('2.0')
+import gtk, gobject, cairo
+
+global has_hildon, has_osso, osso_context, debug
+debug = False # Change this line to enable debug logging
+try:
+ import hildon
+ has_hildon = True
+except ImportError:
+ has_hildon = False
+
+try:
+ import osso
+ has_osso = True
+ try:
+ f = open('/etc/maemo_version', 'r')
+ if (f.readline().startswith('5.0')):
+ print "*** Disabling OSSO functionality due to Maemo 5 bug #4782"
+ has_osso = False
+ except:
+ pass
+except ImportError:
+ has_osso = False
+
+# Create a GTK+ widget on which we will draw using Cairo
+class Screen(gtk.DrawingArea):
+ """This class provides a mechanism for doing graphical programs in a
+ power-efficient and simple way. Subclasses should override {{draw}},
+ and potentially {{set_visible}}."""
+
+ # Tracks whether we should do screen updates
+ _visible = None
+ paused = False
+
+ # Draw in response to an expose-event, this isn't done in run() for
+ # reasons which aren't clear
+ __gsignals__ = { "expose-event": "override" }
+
+
+ # -----------------------------------------------------------------------
+ @property
+ def version(self):
+ """Returns the version number which should be given to DBUS, displayed
+ in about boxes etc. This can be overridden in subclasses."""
+ return "0.01"
+
+
+ # -----------------------------------------------------------------------
+ @property
+ def display_name(self):
+ """The name of the application. This is displayed in the titlebar, about
+ boxes etc. This can be overridden in subclasses, but defaults
+ to the class' name."""
+ return self.__class__.__name__
+
+
+ # -----------------------------------------------------------------------
+ @property
+ def dbus_name(self):
+ """The DBUS name of the application. This defaults to the class name
+ in lowercase prefixed with {{org.maemo.}}"""
+ return 'org.maemo.' + self.__class__.__name__.lower()
+
+
+ # -----------------------------------------------------------------------
+ @property
+ def visible(self):
+ """Whether the application is visible. This should be used to indicate
+ if timers should continue to fire, etc."""
+ return self._visible
+
+
+ # -----------------------------------------------------------------------
+ def set_visible(self, value):
+ """Update the _visible_ property."""
+ self._visible = value
+
+
+ # -----------------------------------------------------------------------
+ def do_window_state_event(self, window, event):
+ """Handle normal GTK+ window state events. If the window is withdrawn
+ or minimised, the application is no longer visible."""
+ self.set_visible(not event.new_window_state &
+ (gtk.gdk.WINDOW_STATE_ICONIFIED
+ |gtk.gdk.WINDOW_STATE_WITHDRAWN))
+
+
+ # -----------------------------------------------------------------------
+ def do_focus_in_event(self, widget, event):
+ """If the application has focus given to it, assume that it is now
+ visible. This is necessary as Maemo 4 does not always provide the
+ correct event handling."""
+ self.set_visible(True)
+
+
+ # -----------------------------------------------------------------------
+ def do_general_event(self, widget, event):
+ """A general event handler which will, on Hildon, check whether the
+ application is visible."""
+ if (debug):
+ print "%s, state = %d, paused = %d" % (event.type, self.window.get_state(), self.paused)
+ if (has_hildon):
+ topmost = self.app_window.get_property('is-topmost')
+ if (debug):
+ print " topmost = %d" % (topmost)
+ self.set_visible(not self.paused and topmost)
+
+
+ # -----------------------------------------------------------------------
+ def do_property_event(self, widget, event):
+ """Track property change events. Ideally, this would be used on Hildon
+ to check the 'is-topmost' property; however this event does not
+ get fired correctly on Maemo 4."""
+ self.do_general_event(widget, event)
+ if (debug):
+ print " property = %s" % (event.atom)
+
+
+ # -----------------------------------------------------------------------
+ def do_expose_event(self, event):
+ """Handle the 'expose' event, which signals a portion of the viewport
+ needs redrawing. This sets up a Cairo context and delegates to the
+ {{draw}} method."""
+ self.do_general_event(event.window, event)
+ cr = self.window.cairo_create()
+ 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):
+ """Draw the Cairo display. This should be overridden in subclasses."""
+
+ # Fill the background with gray
+ cr.set_source_rgb(0.5, 0.5, 0.5)
+ cr.rectangle(0, 0, width, height)
+ cr.fill()
+
+
+# ---------------------------------------------------------------------------
+# Create and set-up the application, window, event handlers etc.
+def run(widget):
+ if (has_hildon):
+ print "+++ Hildon, yay!\n"
+ widget.app = hildon.Program()
+ window = hildon.Window()
+ gtk.set_application_name(widget.display_name)
+ else:
+ print "--- No Hildon, sorry\n"
+ window = gtk.Window()
+ window.set_title(widget.display_name)
+
+ widget.app_window = window
+ window.resize(800, 480)
+ window.add(widget)
+
+ window.connect("delete-event", gtk.main_quit)
+ window.connect("window-state-event", widget.do_window_state_event)
+ window.connect("focus-in-event", widget.do_focus_in_event)
+ window.connect("property-notify-event", widget.do_property_event)
+
+ if (has_osso):
+ print "+++ Have osso, yay!\n"
+ try:
+ osso_context = osso.Context(widget.dbus_name, widget.version, False)
+ device = osso.DeviceState(osso_context)
+ device.set_device_state_callback(state_change, system_inactivity=True, user_data=widget)
+ except:
+ print "*** Failed to initialise OSSO context. Power management disabled..."
+ has_osoo = False
+
+ window.present()
+ widget.show()
+ gtk.main()
+
+# ---------------------------------------------------------------------------
+def state_change(shutdown, save_unsaved_data, memory_low, system_inactivity, message, widget):
+ """Handle OSSO-specific DBUS callbacks, in this case whether the system has
+ been paused."""
+ if (debug):
+ print "State change (%s): shutdown = %d, save = %d, low mem = %d, paused = %d" % (
+ message, shutdown, save_unsaved_data, memory_low, system_inactivity)
+ widget.set_visible(not system_inactivity)
+ widget.paused = system_inactivity
+
+
+if __name__ == "__main__":
+ run(Screen)
Added: trunk/packages/horizon.pkg/horizon
===================================================================
--- trunk/packages/horizon.pkg/horizon (rev 0)
+++ trunk/packages/horizon.pkg/horizon 2009-07-14 22:37:34 UTC (rev 280)
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+PYTHON=`which python2.5` || PYTHON=`which python`
+exec $PYTHON /usr/lib/python2.5/site-packages/horizon/main.py "$@"
\ No newline at end of file
Added: trunk/packages/horizon.pkg/horizon-128.png
===================================================================
(Binary files differ)
Property changes on: trunk/packages/horizon.pkg/horizon-128.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/packages/horizon.pkg/horizon-26.png
===================================================================
(Binary files differ)
Property changes on: trunk/packages/horizon.pkg/horizon-26.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/packages/horizon.pkg/horizon-40.png
===================================================================
(Binary files differ)
Property changes on: trunk/packages/horizon.pkg/horizon-40.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/packages/horizon.pkg/horizon-48.png
===================================================================
(Binary files differ)
Property changes on: trunk/packages/horizon.pkg/horizon-48.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/packages/horizon.pkg/horizon-64.png
===================================================================
(Binary files differ)
Property changes on: trunk/packages/horizon.pkg/horizon-64.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/packages/horizon.pkg/horizon.desktop
===================================================================
--- trunk/packages/horizon.pkg/horizon.desktop (rev 0)
+++ trunk/packages/horizon.pkg/horizon.desktop 2009-07-14 22:37:34 UTC (rev 280)
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Version=1.0
+Encoding=UTF-8
+Name=Horizon
+Icon=horizon
+Exec=/usr/bin/horizon
+X-Osso-Service=org.maemo.horizon
+Type=Application
Added: trunk/packages/horizon.pkg/horizon.service
===================================================================
--- trunk/packages/horizon.pkg/horizon.service (rev 0)
+++ trunk/packages/horizon.pkg/horizon.service 2009-07-14 22:37:34 UTC (rev 280)
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.maemo.horizon
+Exec=/usr/bin/horizon
Added: trunk/packages/horizon.pkg/main.py
===================================================================
--- trunk/packages/horizon.pkg/main.py (rev 0)
+++ trunk/packages/horizon.pkg/main.py 2009-07-14 22:37:34 UTC (rev 280)
@@ -0,0 +1,165 @@
+#!/usr/bin/env python
+#
+# Main class for `Horizon' - a false horizon display using accelerometer
+# information. (c) Andrew Flegg 2009
+# Released under the Artistic Licence
+
+import providers
+import framework
+from framework import Screen
+
+import gobject
+import cairo
+import gtk
+from math import pi, sin, atan2, degrees
+
+class Horizon(Screen):
+ """Create and manage a Cairo display using an external information
+ system providing accelerometer information.
+
+ This uses the `Screen' framework as described in the Cairo tutorials
+ at http://www.tortall.net/mu/wiki/PyGTKCairoTutorial.
+ """
+
+ provider = None
+
+ rotation = None
+ boundary = None
+ freq = 10
+
+ ground = None
+ sky = None
+
+ # -----------------------------------------------------------------------
+ def __init__(self, provider):
+ """Initialise the information provider. This is responsible for
+ having a {{position}} method which returns an x, y, z tuple."""
+ Screen.__init__(self)
+ self.provider = provider
+
+ # -----------------------------------------------------------------------
+ def set_visible(self, visible):
+ """Override visibility setter to re-enable timeout."""
+ if (visible == self.visible):
+ return
+
+ super(Horizon, self).set_visible(visible)
+ print "Visibility change: %d" % (visible)
+ if (visible):
+ self.update_display()
+ gobject.timeout_add(self.freq, Horizon.update_display, self)
+
+ # -----------------------------------------------------------------------
+ def show(self):
+ """Override initial display to create update schedule and reused
+ gradient fills."""
+ Screen.show(self)
+
+ self.ground = cairo.LinearGradient(0, -0.5, 0, 0.5)
+ self.ground.add_color_stop_rgb(0, 87/255.0, 153/255.0, 10/255.0)
+ self.ground.add_color_stop_rgb(1, 0, 123/255.0, 0)
+
+ self.sky = cairo.LinearGradient(0, -0.5, 0, 0.5)
+ self.sky.add_color_stop_rgb(0, 0, 107/255.0, 214/255.0)
+ self.sky.add_color_stop_rgb(1, 30/255.0, 148/255.0, 1)
+
+ self.set_visible(True)
+
+ # -----------------------------------------------------------------------
+ def update_display(self):
+ """Call the configured position provider's {{position}} method which
+ returns x, y, z tuple. The values should be in the range -1000 - 1000."""
+
+ (x, y, z) = self.provider.position()
+ boundary = max(min(z / 1000.0, 1), -1)
+ rotation = atan2(x, y) - pi
+ if (rotation < -pi):
+ rotation += 2*pi
+
+ if (self.rotation <> rotation or self.boundary <> boundary):
+ self.rotation = rotation
+ self.boundary = boundary
+ self.window.invalidate_region(
+ gtk.gdk.region_rectangle((0, 0,
+ self.window.get_size()[0],
+ self.window.get_size()[1])),
+ True)
+
+ return self.visible
+
+ # -----------------------------------------------------------------------
+ def draw(self, cr, width, height):
+ """Responsible for the actual drawing of: the ground, sky, false
+ horizon, height bar and roll display."""
+
+ cr.save()
+ cr.scale(height, height)
+ cr.translate(0.5 * width / height, 0.5)
+ cr.rotate(self.rotation)
+
+ # -- Draw the ground...
+ #
+ cr.set_source(self.ground)
+ cr.rectangle(-2, self.boundary, 4, 2)
+ cr.fill()
+
+ # -- Draw the sky...
+ #
+ cr.rectangle(-2, self.boundary - 2, 4, 2)
+ cr.set_source(self.sky)
+ cr.fill()
+
+ # -- Draw the false horizon...
+ #
+ cr.set_source_rgba(1, 1, 1, 0.8)
+ cr.set_line_width(0.004)
+
+ cr.move_to(-0.14, 0)
+ cr.line_to(-0.03, 0)
+ cr.move_to(0.03, 0)
+ cr.line_to(0.14, 0)
+ cr.stroke()
+
+ # -- Draw the height bars...
+ #
+ bar_height = self.boundary / 4.0
+ cr.move_to(-0.04, bar_height - 0.25)
+ cr.line_to(0, bar_height - 0.25)
+ cr.line_to(0, bar_height + 0.25)
+ cr.line_to(0.04, bar_height + 0.25)
+ for i in range(1, 10):
+ y = (bar_height - 0.25) + (i * 0.05)
+ cr.move_to(-0.018, y)
+ cr.line_to(0.018, y)
+ cr.stroke()
+
+ # -- Draw the down arrow...
+ #
+ cr.set_line_width(0.003)
+ cr.move_to(-0.02, 0.45)
+ cr.line_to(0.02, 0.45)
+ cr.line_to(0, 0.5)
+ cr.close_path()
+ cr.stroke()
+
+ # -- Show the telemetry...
+ #
+ cr.restore()
+ cr.set_source_rgba(1, 1, 1, 0.8)
+ cr.select_font_face("sans-serif")
+ cr.set_font_size(32)
+ angle = unicode(round(degrees(self.rotation), 1)) + u'\u00b0'
+ (x, y, w, h, xa, ya) = cr.text_extents(angle)
+ cr.move_to(width - 40 - w, 40)
+ cr.show_text(angle)
+
+
+if __name__ == "__main__":
+ provider = None
+ if (providers.NokiaAccelerometer.available()):
+ provider = providers.NokiaAccelerometer()
+ else:
+ provider = providers.Demo()
+
+ framework.run(Horizon(provider))
+
Added: trunk/packages/horizon.pkg/mud.xml
===================================================================
--- trunk/packages/horizon.pkg/mud.xml (rev 0)
+++ trunk/packages/horizon.pkg/mud.xml 2009-07-14 22:37:34 UTC (rev 280)
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<package>
+ <fetch type="command">
+ <command>
+ PKGDIR=horizon-0.0.3
+ mkdir "${PKGDIR}"
+ cp "${MUD_PACKAGES_DIR}/horizon.pkg/"*.py "${PKGDIR}/"
+ cp "${MUD_PACKAGES_DIR}/horizon.pkg/"{Makefile,horizon} "${PKGDIR}/"
+ </command>
+ </fetch>
+ <deb>
+ <depends>python-runtime | python2.5-runtime</depends>
+ <section>user/graphics</section>
+ <maintainer>Andrew Flegg <andrew at bleb.org></maintainer>
+ <description>An artificial horizon and spirit level.
+ This application shows the pitch and roll of an accelerometer
+ enabled device. In the absence of accelerometers, it shows a
+ twisty turny path through the sky.
+ .
+ The application can also be used as a spirit level, showing the
+ pitch angle in degrees.
+ </description>
+ <display-name>Horizon</display-name>
+ </deb>
+</package>
+
Added: trunk/packages/horizon.pkg/providers.py
===================================================================
--- trunk/packages/horizon.pkg/providers.py (rev 0)
+++ trunk/packages/horizon.pkg/providers.py 2009-07-14 22:37:34 UTC (rev 280)
@@ -0,0 +1,45 @@
+#
+# Provider information sources for `Horizon' - a false horizon display using
+# accelerometer information. (c) Andrew Flegg 2009
+# Released under the Artistic Licence
+
+import os.path
+from math import sin, cos, pi
+
+class Dummy:
+ """One of the simplest providers: returns dead-on, flat."""
+ def position(self):
+ return (0, -1000, 0)
+
+
+class Demo:
+ """A demonstration provider which will take the user on a tour through
+ the air."""
+ x = 0
+ y = -1000
+ z = 0
+
+ def position(self):
+ self.x -= 2
+ self.y += 1
+ self.z += 2
+ return (sin(self.x / 150.0 * pi) * 150,
+ cos(self.y / 150.0 * pi) * 200,
+ sin(self.z / 150.0 * pi) * 300)
+
+
+class NokiaAccelerometer:
+ """An accelerometer provider which actually reads an RX-51's
+ accelerometers, based on http://wiki.maemo.org/Accelerometers"""
+
+ global ACCELEROMETER_PATH
+ ACCELEROMETER_PATH = '/sys/class/i2c-adapter/i2c-3/3-001d/coord'
+
+ def position(self):
+ f = open(ACCELEROMETER_PATH, 'r')
+ return [int(w) for w in f.readline().split()]
+
+ @classmethod
+ def available(cls):
+ return os.path.isfile(ACCELEROMETER_PATH)
+
More information about the Mud-builder-commits
mailing list