耐着性子鑽研了一下sl4a與QPython之類,取得了些經驗,彙報以下:html
本文使用的apk安裝包以下: QPython: QPython70.apk;sl4a+Python:sl4a_r6.apk;PythonForAndroid_r4.apk。安裝與使用方法十分簡單,網上參考不少,本文再也不重複安裝方法與HelloWorld之類。前端
I. sl4a+Pythonpython
1. 重要參考資料:android
(1)Pro Android Python with SL4A(PDF)(2) SL4A API HELP;(3) 如何使用Webview:Webviews with SL4A: A Call and Two Hooksweb
2. 如下一例,演示如何經過sl4a api取得Gps與羅盤信息,相關技術資料詳見詳見參考資料(2)中LocationFacade;SensorManagerFacade章節編程
# -*- coding: utf-8 -*- import android import time from math import radians droid = android.Android() droid.startSensingTimed(1, 250) droid.startLocating() while 1: gpsdata = droid.readLocation().result s6data = droid.sensorsReadOrientation().result if len(gpsdata)>0: print gpsdata['gps']['bearing'] #取得Gps導向(bearing)(角度) if len(s6data)>0: print s6data[0] #取得羅盤方位角(azimuth)(弧度) time.sleep(0.5) droid.stopLocating() droid.stopSensing()
3. 關於使用webview作UI,請參考資料(1)與(3),以下提供一個例子(簡單指北針),演示Python後端如何與Webview通訊,不過此例僅含Python向Webview單向發送數據:webview做爲前端,用svg作了一個簡單的指北針,每0.5秒根據Python後端讀取的方位角數據更新一次。canvas
import android import time from math import radians droid = android.Android() droid.webViewShow('file:///sdcard/sl4a/scripts/compassSVGDrawing.html') droid.startSensingTimed(1, 250) while 1: s6data = droid.sensorsReadOrientation().result if len(s6data)>0: try: az = s6data[0] droid.eventPost('dataout', str(az)) except: pass time.sleep(0.5) droid.stopSensing() # compassSVGDrawing.html ''' <!DOCTYPE html> <html> <body bgcolor="#000000"> <svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg"> <circle cx="45" cy="45" r="40" stroke="green" stroke-width="3" fill="black"/> <line id="azim" x1="45" y1="5" x2="45" y2="45" stroke="green" stroke-width="3"/> </svg> <script> var droid = new Android(); #注意此行代碼與以後的Callback是Python後端與Webview通訊的關鍵技術細節 droid.registerCallback('dataout', function(e) { var az = String(-1*180 * parseFloat(e.data) / Math.PI); var cmd = "rotate(" + az + " 45 45)" document.getElementById("azim").setAttribute("transform", cmd); }); </script> </body> </html> '''
4. SL4A與藍牙後端
手頭有個藍牙GPS,就拿來操練了一下,嘗試了一下讀取藍牙設備,沒想到在個人華碩FonePad上實驗很順利,效果至關不錯。
api
import android import time droid = android.Android() droid.toggleBluetoothState(True) result = droid.bluetoothConnect('00001101-0000-1000-8000-00805F9B34FB', '00:02:76:C9:92:44') #SSP之默認UUID與藍牙設備物理地址 print repr(result) if result: while True: message = droid.bluetoothReadLine().result print message droid.toggleBluetoothState(False)
II. Qpython服務器
QPython安裝僅需一個apk,界面也比sl4a豐富一些,內容已然至關成熟,已經整合了很多好東西,我嘗試了其中的androidhelper(基於sl4a)、kivy(出色的圖形庫)、bottle(精悍的web架構)。
1. 先詳細說說QPython與sl4a+Python方案的區別:
(1)QPython整合了sl4a api,以下是QPython調用sl4a api的代碼:
import androidhelper droid = androidhelper.Android()
(2)需強調的一點,QPython目前沒有實現sl4a與webview的內在通訊機制(上文中的指北針代碼不適用於QPython),我認爲這是個不足,不過QPython可以製做功能至關的web app,且可使用kivy製做精良的圖形界面、遊戲之類的,這是其優點所在。
(3)QPython內置了bottle web架構,也能夠從QPypi安裝其餘一些流行的web工具,對於製做web app、服務器之類而言確定更有優點。
(4) 二者對webview的調用功能有別,細節尚不清楚,兼容性有差別,在個人平板上,QPython在支持svg方面與sla4相比較表現出一些差別,不支持 width ="100%"這樣的設置,使用sl4a時卻支持。若使用sl4a調用Webview顯示含canvas的網頁,其畫質十分粗糙。
2. kivy
據個人感覺,QPython中的kivy真是個好東西,作出來的界面很漂亮,很穩定,就是貌似不太好學。鑑於我尚未只知其一;不知其二,貼幾個對我有所啓發的代碼和我本身作的一個例子,均在Android設備上測試經過:
(1) 使用StackLayout:
注意#1,這部分使用的語言是kivy語言,對kivy核心元素,即widget,安排佈局、設定屬性。注意以下代碼中<ScreenUI>(#二、#4),其性質是一個用戶定義的widget,繼承StackLayout,若是無需對StackLayout 編程,則不須要在kivy語言中與Python中專門定義<ScreenUI>,使用StackLayout就能夠。ScreenUI這個widget也就成爲了一個root,能夠用來控制widget的行爲與屬性(#三、#5)。
from kivy.app import App from kivy.uix.boxlayout import BoxLayout from kivy.uix.label import Label from kivy.lang import Builder from kivy.uix.textinput import TextInput from kivy.uix.stacklayout import StackLayout from kivy.uix.button import Button Builder.load_string(""" #1 <ScreenUI>: #2 orientation: 'lr-bt' Button: text: root.text #3 size: 100, 100 size_hint: None, None Button: text: 'Button 2' size_hint: None, None size: 200, 100""") class ScreenUI(StackLayout): #4 text = "Button 1" #5 class WidgetApp(App): def build(self): app = ScreenUI() return app if __name__ == '__main__': WidgetApp().run()
(2) 旋轉圖片:嘗試旋轉三張重疊圖片,角度可控,將角度顯示在一個標籤
from kivy.app import App from kivy.uix.image import Image from kivy.lang import Builder from kivy.properties import NumericProperty from kivy.uix.floatlayout import FloatLayout Builder.load_string(''' [Title@Label] pos_hint: {'center_x': .5, 'y': .3} text: ctx.text font_size: 16 <RotationWid>: FloatLayout: Title: text: root.message Image: source: 'kivy.png' canvas.before: PushMatrix Rotate: angle: root.angle origin: self.center canvas.after: PopMatrix Image: source: 'Red.png' canvas.before: PushMatrix Rotate: angle: root.angle origin: self.center canvas.after: PopMatrix Image: source: 'tinyCompass.png' canvas.before: PushMatrix Rotate: angle: root.angle origin: self.center canvas.after: PopMatrix ''') class RotationWid(FloatLayout): angle = NumericProperty(-45) message = '45 degrees' class RotationApp(App): def build(self): return RotationWid() RotationApp().run()
(3)經過旋轉圖片實現的指北針
from kivy.app import App from kivy.uix.image import Image from kivy.clock import Clock from kivy.lang import Builder from kivy.properties import NumericProperty from math import pi import androidhelper import time droid = androidhelper.Android() droid.startSensingTimed(1, 250) Builder.load_string(''' <RotateCompass>: source: 'Red.png' size: 256,256 canvas.before: PushMatrix Rotate: angle: root.angle origin: self.center canvas.after: PopMatrix ''') class RotateCompass(Image): angle = NumericProperty(0) def __init__(self, **kwargs): super(RotateCompass,self).__init__(**kwargs) Clock.schedule_interval(self.my_callback, 1) #clock時間處理,週期爲1秒,動做定義在my_callback中,週期性給angle賦值 def my_callback(self,dt): s6data = droid.sensorsReadOrientation().result if len(s6data)>0: self.angle = 180 * s6data[0] / pi class RotationApp(App): def build(self): return RotateCompass() RotationApp().run() droid.stopSensing()
3. QPython Web App
QPython只給了一個簡單的例子,資料少的可憐。嘗試了一下,也整出一羅盤來。用XMLHttpRequest與長輪詢(long polling)這個解決方法實現網頁客戶端與服務器的通訊。
#qpy:webapp:Compass_Long_Polling #qpy:fullscreen #qpy://localhost:8080/ #此行必須 """ This is a sample for qpython webapp """ from bottle import route, run import androidhelper import signal, os import time code = ''' <html> <body> <center> <svg width="200" height="200" xmlns="http://www.w3.org/2000/svg"> <circle cx="100" cy="100" r="80" stroke="navy" stroke-width="5" fill="none"/> <line id="azim" x1="100" y1="20" x2="100" y2="100" stroke="navy" stroke-width="5"/> <text id="degree" x="0" y="20" stroke="navy">90</text> </svg><br> <button onclick="shutdown()"> shut down</button> </center> <script> var az; var deg; var url = "http://localhost:8080/" var xhr; xhr=new XMLHttpRequest(); xhr.onreadystatechange=function() { if (xhr.readyState==4) { if (xhr.status==200) { az=parseFloat(xhr.responseText); if(az>=0){ deg = az*180/Math.PI; }else{ deg = 360+az*180/Math.PI; } deg=String(deg.toFixed(2)); az = String(-180 * az / Math.PI); var cmd = "rotate(" + az + " 100 100)"; document.getElementById("degree").textContent="^"+deg; document.getElementById("azim").setAttribute("transform", cmd); } xhr.open("GET",url+"azimuth",true); xhr.send(); } } xhr.open("GET",url+"azimuth",true); xhr.send(); function shutdown() { var xhr = new XMLHttpRequest(); xhr.open("GET", url+"shutdown", false); xhr.send(null); } </script> </body> </html> ''' droid = androidhelper.Android() droid.startSensingTimed(1, 250) @route('/') def index(): return code @route('/azimuth') def azimuth(): time.sleep(0.3) s6data = droid.sensorsReadOrientation().result if len(s6data)>0: return str(s6data[0]) @route("/shutdown") def shutdown(): droid.stopSensing() os.kill(os.getpid(), signal.SIGTERM) run(host='localhost', port=8080)
4. jQuery Mobile
嘗試了一下jQuery Mobile,也就是拷貝到某個子目錄後觀察了一下例子,在QPython中使用沒問題,暫沒用它幹什麼,很少說了。