SL4A、QPython學習筆記

耐着性子鑽研了一下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中使用沒問題,暫沒用它幹什麼,很少說了。

相關文章
相關標籤/搜索