Merge pull request #670 from nklever/android-compass

Compass demo
This commit is contained in:
Mathieu Virbel 2012-10-07 21:56:05 -07:00
commit 80bc8931c0
5 changed files with 251 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -0,0 +1,24 @@
Compass
=======
Demonstrate the combination of the Android Magnetic Field Sensor and the Kivy functionality viewing a Compass.
Kivy Python-For-Android
-----------------------
Please look at the lastest docs of the
`Kivy Python-For-Android Project <http://python-for-android.readthedocs.org/en/latest/>`__
Building an APK
---------------
::
./distribute.sh -m "pyjnius kivy"
::
./build.py --package org.test.compass --name compass \
--version 1.0 --dir ~/code/kivy/examples/android/compass debug

View File

@ -0,0 +1,3 @@
title=Compass
author=Nik Klever
orientation=portrait

View File

@ -0,0 +1,223 @@
import kivy
kivy.require('1.3.0')
import math
import time
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.uix.scatter import Scatter
from kivy.clock import Clock
from kivy.vector import Vector
from kivy.core.window import Window
from kivy.graphics import Color, Ellipse, Rectangle, Triangle
from kivy.logger import Logger
from jnius import autoclass
Logger.info('COMPASS: STARTED')
def LoggerDisplayMetrics(metrics):
"""
Logging all values of the Java Android DisplayMetrics class
to get some more information about the metrics of Android devices
"""
display = {'Default':metrics.DENSITY_DEFAULT,
'Device':metrics.DENSITY_DEVICE,
'High':metrics.DENSITY_HIGH,
'Low':metrics.DENSITY_LOW,
'Medium':metrics.DENSITY_MEDIUM,
'XHIGH':metrics.DENSITY_XHIGH,
'density':metrics.density,
'densityDpi':metrics.densityDpi,
'heightPixels':metrics.heightPixels,
'scaledDensity':metrics.scaledDensity,
'widthPixels':metrics.widthPixels,
'xdpi':metrics.xdpi,
'ydpi':metrics.ydpi}
for (k,v) in display.items():
Logger.info('COMPASS: display %s = %s'%(k,v))
class CompassWidget(FloatLayout):
def __init__(self, **kwargs):
"""
Constructor of the CompassWidget class
"""
super(CompassWidget, self).__init__(**kwargs)
def build(self,pos,size):
"""
building the background of the CompassWidget with a circle of a
compass windrose image
(Source of this image: Wikipedia - http://en.wikipedia.org/wiki/Compass_rose)
"""
with self.canvas:
self.pos = pos
self.size = size
self.windrose = Ellipse(source="500px-Windrose.svg.png", pos=pos, size=size)
class NeedleWidget(Scatter):
def __init__(self, **kwargs):
"""
Constructor of the NeedleWidget class
do_rotation, do_translation, do_scale are all set to False
to indicate that the scatter widget can onlybe updated from the
app and not by an interaction with your fingers
"""
super(NeedleWidget, self).__init__(**kwargs)
self.do_rotation = False
self.do_translation = False
self.do_scale = False
self.auto_bring_to_front = True
def rotateNeedle(self,angle):
"""
rotate the NeedleWidget with the parameter angle
90 is subtracted because North of the windrose is at 90 degrees
of the device
"""
self.rotation = angle - 90
def build(self,center,needleSize):
"""
building the needle with two Triangles of different color
"""
self.pos = center - needleSize/2.
self.size = needleSize
self.size_hint = [None, None]
with self.canvas:
Color(1., 0, 0)
needleTP1 = Vector(needleSize[0]/2.,needleSize[1])
needleTP2 = Vector(needleSize[0]/2.,0)
needleTP3 = Vector(-needleSize[0],needleSize[1]/2.)
needlePoints = (needleTP1[0],needleTP1[1],
needleTP2[0],needleTP2[1],
needleTP3[0],needleTP3[1])
self.needleT1 = Triangle(points=needlePoints)
Color(0.5, 0.5, 0.5)
needleTP3 = Vector(2*needleSize[0],needleSize[1]/2.)
needlePoints = (needleTP1[0],needleTP1[1],
needleTP2[0],needleTP2[1],
needleTP3[0],needleTP3[1])
self.needleT2 = Triangle(points=needlePoints)
class CompassApp(App):
def __init__(self, **kwargs):
"""
Constructor of the Compass App
1) The Java Android API DisplayMetrics is called to get
information about the densityDpi factor of the Android device
2) The Kivy Python-For-Android Android API is called to
get access to the hardware sensors of the Android device
"""
super(CompassApp, self).__init__(**kwargs)
DisplayMetrics = autoclass('android.util.DisplayMetrics')
metrics = DisplayMetrics()
metrics.setToDefaults()
LoggerDisplayMetrics(metrics)
self.densityDpi = metrics.densityDpi
Hardware = autoclass('org.renpy.android.Hardware')
self.hw = Hardware()
Logger.info('COMPASS: Hardware Objects: %s'%(str(dir(self.hw))))
Logger.info('COMPASS: Hardware Sensors\n%s\n'%(self.hw.getHardwareSensors()))
def viewCompass(self, *largs):
"""
viewCompass calls the readSensor method of the
magneticFieldSensor instance of the generic3AxisSensor, it reads the
3-tuple value of the magnetic field
the declination angle is computed as the angle of the magnetic field
vector in the x,y-plane and the unity-vector of the y-axis.
afterwards the rotateNeedle function rotates the needle as given
by the declination angle parameter
"""
(x, y, z) = self.hw.magneticFieldSensor.readSensor()
declination = Vector(x,y).angle((0,1))
#Logger.info('COMPASS: viewCompass x=%s y=%s z=%s declination=%s'%(x,y,z,declination))
self.needle.rotateNeedle(declination)
def stopApp(self,*largs):
"""
this function is called when pushed the stopButton, disables
the magneticFieldSensor and stops the app
"""
self.hw.magneticFieldSensor.changeStatus(False)
Logger.info('COMPASS: stop largs '+str(largs))
self.stop()
def build(self):
"""
Building all together:
1) Creating the parent widget and clearing it to white background color
2) Defining a suitable position and size of the CompassWidget, the
needleSize and the stopButtonHeight depending on the densityDpi value
given by DisplayMetrics
3) Creating an instance of the CompassWidget and adding it to the
parent widget and calling the appropriate build function
4) Creating an instance of the NeedleWidget and adding it also to the
parent widget and calling the appropriate build function
5) Creating an instance of a Button widget and adding it as stopButton
also to the parent widget and bind it with the stopApp function
6) Calling the instance method changeStatus of the magneticFieldSensor
instance with parameter True to enable the magnetic field sensor
and additionally calling the function schedule_interval of the Clock
class for a repeated call of the function viewCompass every second.
"""
parent = FloatLayout(size=(500,500))
Window.clearcolor = (1, 1, 1, 1)
if self.densityDpi == 240:
CompassPos = Vector(50., 200.)
CompassSize = Vector(400., 400.)
needleSize = Vector(100., 60.)
stopButtonHeight = 60
elif self.densityDpi == 320:
CompassPos = Vector(75., 300.)
CompassSize = Vector(600., 600.)
needleSize = Vector(150., 90.)
stopButtonHeight = 90
else:
Logger.info('COMPASS: widget size should be adopted - minimum used for densityDpi=%s'%(str(self.densityDpi)))
CompassPos = Vector(50., 200.)
CompassSize = Vector(400., 400.)
needleSize = Vector(100., 60.)
stopButtonHeight = 60
self.Compass = CompassWidget()
parent.add_widget(self.Compass)
self.Compass.build(pos=CompassPos,size=CompassSize)
self.needle = NeedleWidget()
parent.add_widget(self.needle)
self.needle.build(center=CompassPos+CompassSize/2.,needleSize=needleSize)
self.stopButton = Button(text='Stop', pos_hint={'right':1}, size_hint=(None,None), height=stopButtonHeight)
parent.add_widget(self.stopButton)
self.stopButton.bind(on_press=self.stopApp)
self.hw.magneticFieldSensor.changeStatus(True)
Clock.schedule_interval(self.viewCompass, 1.)
return parent
if __name__ in ('__main__', '__android__'):
CompassApp().run()

View File

@ -0,0 +1 @@
1323529642.46