[Python作品]用Python與微軟Hyper-V製作跨平台APP:你一定要知道的Kivy(Andriod篇)

 


  哈嘍,各位朋友好,小弟這回又要來分享程序相關的資訊了~要看這篇文章前,建議先有點Python的基礎再看,會比較輕鬆喔~這邊就不教學安裝Python跟pip等方法,也不會特別說明Python語法的特別之處,要的話請找看看網路資源,或是看這篇文章[4]稍微對Python有基礎了解喔~(❛◡❛✿)


  這次要幫公司做個出差用的打卡APP,因為原本的打卡網頁不太好使用,要經過數道程序才能打到卡,同事都說爛,因此小弟想說來簡化這個流程。APP上面只要輸入帳號密碼,再按一個按鈕,便可以到打卡頁面、或是直接打卡。小弟之前趁有空的時候有去在職訓練網學程序(.NET MAUI[1]),是一個可以用C#來寫跨平台APP的架構。然而,寫起來坦白說還是有點麻煩,而且很久沒寫了,Visual Studio打開後直接發呆10分鐘。這時候,小弟無意間查到Kivy這個跨平台的架構,可以用Python這個敏捷開發的程式語言開發APP,簡直樂呆了!


  沒錯,Kivy最棒的好處就是,可以簡單上手(因為使用Python)、可跨平台、執行速度快,對像小弟一樣想要獨自開發APP的朋友來說,可說是一大福音啊!所以小弟查到一篇Kivy相關的中文文章[3],直接一頭熱就栽下去了。想當然耳,那篇文章所提到的坑,小弟也都遇到了。為了避免大家還要再踩一樣的坑,小弟寫了這篇文章,為了提醒自己,也造福人群(๑¯∀¯๑)


  截至寫文章的目前(2023/12/06),小弟雖然有串出Kivy對於Andriod的APK製作方法,但是對於iOS的IPA製作作法仍還在串聯中。由於公司大部分同仁是使用iOS系統的蘋果機,所以用Kivy打包成iOS這一哩路勢必得要進行,等小弟串成功後會在這篇文章作個交互連結,方便iOS開發的使用者參考(。A。)


 [Python作品]用Python與微軟Hyper-V製作跨平台APP:你一定要知道的Kivy(iOS篇)
https://hcyang1227.blogspot.com/2023/12/kivy-ios.html



☆Kivy語法與介紹


  Kivy的構成語法主要是Python(.py),主要扮演程式實作跟啟動的功能;也有Kivy專屬的中間轉譯語法(.kv),主要扮演UI建構的功能。這個操作模式,很像之前小弟也有演示過的Python與PyQt5的組合,將圖形介面設計與主程序控制分開,讓開發更有秩序,不會所有程式碼都亂成一團。


  使用Kivy,便可以創建在以下平台上運行的應用程式:1.桌上型電腦--macOS、Linux、BSD Unix、Windows 2.iOS裝置--iPad、iPhone 3.Android裝置--平板電腦、手機 4.支援 TUIO(有形使用者介面物件)的任何其他支援觸控的專業/自製設備。可以自由地編寫一次程式碼並使其在不同平台上按原樣運行。




☆安裝Kivy

  在官方文件中有詳細的安裝說明[12],但要在Windows上安裝Kivy,主要還是靠以下這行指令:


>>透過pip安裝Kivy
pip install kivy



  經常使用pip安裝各種插件的朋友,應該對這行指令不陌生。另外,編輯的環境也比較建議用大家常用的VS Code,有支援Python code與KV code編輯,也有許多插件可以下載幫助coding(ノ>ω<)ノ




☆.py部分撰寫方式


  以下這些撰寫方式,跟我們之前介紹過的Python與PyQt5其實非常類似。先來上一段簡單的程式碼[6]:


from kivy.app import App
from kivy.uix.widget import Widget

class GameScreen(Widget):
    pass

class MainGame(App):
    def build(self):
        return GameScreen()

if __name__ == '__main__':
    MainGame().run()


  前面我們必須先將kivy給import進Python裡,其中App是一定要import的,它代表程序的最外層框架,也是執行build的要點。Widget也是一個很常用到的UI元件,我們也可以將之import進來。


  接著,我們要創建程序最外層的class,也就是MainGame(App),繼承App的庫。在這個class裡面,一定會用到def build(self)的語法,這是啟用APP關鍵的方法。在啟用方法裡面,我們必須return一個元件。我們也可以寫成retrun Widget(),但更常用的寫法是,我們會return另一個class,並在另一個class裡面附加我們想要運行的腳本


  接下來,我們創建剛才提到的內層的class,也就是GameScreen(Widget),繼承Widget的庫。剛才的return我們寫成return GameScreen(),以連結class彼此之間的關聯。在這個GameScreen(Widget)裡面,因為目前我們沒有腳本,所以便只要鍵入pass便可。


  最後,為了要讓程序在開啟Python時啟用MainGame(App),我們還要再加兩行if __name__ == '__main__'跟MainGame().run(),讓MainGame(App)可以被創建。沒有加這一行,這程序開起來之後便靜悄悄的,什麼視窗還是事情都不會發生。


  寫完上述的code後,我們用Python執行除錯,便會跑出一個黑色的視窗。沒錯!我們的第一個程式便完成了!畫面是黑色的原因,是因為Kivy原本預設的視窗背景是黑色的。這個視窗其實還有一個看不到的元件叫做Widget,它理論上會被上下左右至中,出現在畫面的正中央,並且填滿整個背景,以符合視窗響應功能。如果想要看到畫面中有東西,可以試著import kivy.uix.button.Button,並且把GameScreen(Widget)改成GameScreen(Button),您便會看到一個填充滿整個視窗的按鈕,並且文字顯示在正中央。


  寫Kivy的Python主程序還有一個重點,就是檔名要命名成main.py,因為後續打包時,Buildozer只認main.py為主程序,沒有找到main.py的話,Buildozer編譯到一半就會報錯,然後您就踩坑了(〒︿〒)




☆.kv部分撰寫方式


跟Python很類似,我們也先來上一段簡單的程式碼[6]:


#:kivy 1.11.1

<GameScreen>:
    FloatLayout:
        pos: 0, 0
        size: root.width, root.height

        GridLayout:
            cols: 2
            pos_hint: {'x': .3, 'y': .3}
            size_hint: None, None
            size: root.width * .4, root.width * .4

            Button:
                text: 'Button 1'
            Button:
                text: 'Button 2'
            Button:
                text: 'Button 3'
            Button:
                text: 'Button 4'


  詳細的解說一下KV code,雖然它跟Python code看起來蠻不一樣的,但是都有個共通點:有冒號、有縮排。是的,雖然KV code是中間轉譯語法,但它的思路跟Python很類似,也不能馬虎。有冒號出現,便代表下一行是其UI物件的內容或是其他UI物件;有縮排的多少,便代表UI物件之間有主從或是母子排序之分。



  首先,#:kivy 1.11.1是一定要加的第一行註釋,沒有這一行,Kivy無法判斷這是KV code,也不知道要用哪一版的Kivy去翻譯它。然後,<GameScreen>:這一行跟Python code裡面的GameScreen(Widget)是有關連的,代表我今天要設計的UI介面,是掛在哪一個Python code的class下面。



  接下來,FloatLayout:與GridLayout:都是小弟在.NET MAUI有看過的語法,代表這應該是開發APP的共通語言,也就是「布局」的設定跟階層套用。



  剩下的內容直接翻譯成中文來解釋的話,就是,我們先創建FloatLayout UI元件,設定它的起點從(0,0)開始(注意!Kivy的0點在畫面的最左下角!),長寬設定跟根UI元件一樣長一樣寬,然後,我們在FloatLayout裡面創建GridLayout UI元件,設定它有兩排,置於畫面比例(0.3,0.3)的地方,長寬比例故意設成根UI元件的寬的0.4倍,最後,在GridLayout裡面創建4個Button UI元件,這4個Button分別命名成Button 1~Button 4。



  結果而言,這段程式碼會創建一個UI介面,如下圖所示,有四個按鈕在黑色的背景中間,當您拉伸視窗大小時,四個按鈕也會跟著放大縮小,也就是有著良好的視窗響應。



  寫Kivy的UI畫面(kv檔)還有一個重點,就是檔名要命名成Python裡面最基層class的名稱。上面的Python code裡我們有創建MainGame(App)這個class,這時候我們的kv檔也要命名成maingame.kv,否則執行主程序時,Kivy會沒辦法判定哪個是程序要載入的UI介面,然後就當作沒有UI介面執行程序,畫面開啟後便會是一片烏黑,然後您就踩坑了(☍﹏⁰。)




☆打包成APP的過程


  因為小弟熟悉的是Windows系統,開發的大概流程便會是:
  1.在Windows系統或是裡撰寫.py與.kv
  2.開啟虛擬硬碟(.vhdx)並且將.py與.kv整個專案放進虛擬硬碟
  3.開啟Ubuntu(用微軟的Hyper-V開啟),並預先掛載虛擬硬碟(.vhdx)到Ubuntu
  4.透過Buildozer打包APP


  首先要先說明,為什麼我們選用可能會比較不熟悉的Ubuntu系統,在該系統裡面打包APP。有些系統指令庫,比方說"sh",在Windows系統是完全沒有的,但是打包工具像是python-for-android[5]或是Buildozer[7],會仰賴這些Linux指令庫,所以我們不得不把打包這件事放到Ubuntu等Linux系統上來執行。


  跟一開始看的文章作者[3]一樣,打包過程也是小弟最窒礙難行的一里路,路上的坑滿滿的。因為像是python-for-android,小弟嘗試用Windows安裝跟使用時,就發現有些系統指令是Windows沒有的,所以改到Hyper-V的Ubuntu去安裝python-for-android。安裝完打了一下指令,奇怪,怎麼找不到這個API的p4a指令?然後又去看它的官方文件,下載了Andriod的NDK跟SDK,裝了老半天,然後又說SDK的指令找不到、沒辦法執行等等。搞了一整天,一點進展都沒有。最後只好選擇Buildozer,結果不久就解決了...(´・Å・`)




☆使用Hyper-V虛擬機器安裝Ubuntu


  我們要先解說一下,什麼是Hyper-V。Hyper-V可讓您建立虛擬硬碟、虛擬交換器,以及許多其他虛擬裝置,這些全都可以加入虛擬機器。Hyper-V適用於64位元版的Windows10專業版、企業版和教育版,但不適用於家用版。虛擬化可以讓您 1.執行需要舊版Windows或非Windows作業系統的軟體,試用其他作業系統 2.透過 Hyper-V,要建立或移除不同的作業系統,都非常容易 3.使用多部虛擬機器在多個作業系統上測試軟體,這些虛擬機器可在匯出後匯入任何其他Hyper-V系統中,包括Azure[8]。



  安裝方法可以參考這一篇文章[9]。過程中,小弟使用Hyper-V快速建立的方式來安裝Ubuntu 22.04,這是一個微軟有魔改過的版本,可以很好的被安裝跟執行,可以調整解析度與使用增強模式複製貼上。只是安裝完之後的壞處是,硬碟大小會是固定的,並被侷限在12GB。這一點可以再看這一篇文章[10]來解決,將硬碟大小提升到更大的大小。


  另外,因為小弟使用增強模式時,會發生無法登入xRDP的窘境,登入帳密正確卻沒辦法登入Ubuntu。所以,小弟只好捨棄使用增強模式,要設定請在菜單的「檢視」中取消勾選增強模式。這也是一個小坑,光是取消增強模式的功能,就費了小弟一些力氣找;一開始甚至不知道xRDP,以為只是個普通的登入視窗,然後差點就要放棄用Hyper-V這個工具了(´,_ゝ`)




☆使用Hyper-V掛載虛擬硬碟(.vhdx)


  這部分可以先看一下這一篇文章[11]。主要的方法是,透過「電腦管理」裡面的「磁碟管理」來增加虛擬硬碟,建立VHDX,建立完後初始化硬碟,將Ubuntu需要的檔案複製到VHDX,操作分離VHDX,打開Hyper-V設置,選擇剛才創建的VHDX添加為SCSI控制器的硬碟,打開虛擬機Ubuntu,便可看到共用的虛擬硬碟了。


  用這個方法主要是因為小弟在使用Hyper-V的增強模式時試不出來,不然直接用增強模式的剪貼簿剪下貼上虛擬機跟實體機之間的資料,就不用這麼麻煩了。因為開啟Hyper-V的虛擬機時會占用外掛的VHDX,使用Windows操作開啟VHDX也會占用該VHDX,所以必須記得,必須互相切換兩者狀態,開Hyper-V時就不用檔案總管開VHDX,反之亦然




☆用Buildozer將專案打包成APK(Andriod)


  在Buildozer的官方文件[7]裡有特別寫到,Buildozer依賴它的夥伴項目:python-for-android和Kivy for iOS。它讓框架建立應用程式變得更容易,可以獨立使用-即使與其他GUI框架一起使用。


  我們可以試著拿Kivy官方的PongGame打包[12],原因是這個遊戲範例比較可以確定自己的打包結果會不會閃退或是打不開、功能是否正常等等。按照官方步驟,將main.py跟pong.kv建立起來,然後透過VHDX儲存到虛擬硬碟裡面。然後,打開Hyper-V並啟動Ubuntu,接下開啟Ubuntu的終端機(Terminal),來順著以下的步驟安裝Buildozer。


>>安裝Cython
sudo apt-get install git libssl-dev cython3

>>解決Cython的問題
cd /bin/ && sudo gedit cython
在打開的編輯器裡鍵入cython3 $@,然後儲存
sudo chmod 755 cython
cd ~

>>Java版本老舊,升級到java11[13]
sudo update-alternatives --config java

>>安裝Buildozer
sudo git clone https://github.com/kivy/buildozer.git
sudo apt install -y git zip unzip openjdk-8-jdk python3-pip autoconf libtool pkg-config zlib1g-dev libncurses5-dev libncursesw5-dev libtinfo5 cmake libffi-dev
cd ..
cd buildozer/
sudo python3 setup.py install

>>移動到專案資料夾,並生成Buildozer初始檔
buildozer init

>>使用Buildozer執行剛才產生的初始檔
buildozer --verbose android debug



參考資料:

[1] 微軟 - 什麼是 .NET MAUI?
https://learn.microsoft.com/zh-tw/dotnet/maui/what-is-maui?view=net-maui-8.0

[2] Kivy: The Open Source Python App Development Framework.
https://kivy.org/

[3] Medium - Python for Android 踩雷心得
https://medium.com/@k1992313/python-for-android-%E8%B8%A9%E9%9B%B7%E5%BF%83%E5%BE%97-f07ac9c106ac

[4] [Python作品]Python程設初體驗 ~之~ OpenCV分析監視器影像抓老鼠
https://hcyang1227.blogspot.com/2018/04/pypython-opencv.html

[5] python-for-android
https://python-for-android.readthedocs.io/en/latest/quickstart/

[6] Kivy Basics in 60 MINUTES
https://www.youtube.com/watch?v=9JH8r8mz0g4

[7] Buildozer
https://github.com/kivy/buildozer

[8] 簡介 Windows 10 上的 Hyper-V
https://learn.microsoft.com/zh-tw/virtualization/hyper-v-on-windows/about/

[9] Windows10 — Hyper-V安裝Ubuntu教學
https://medium.com/%E4%BC%81%E9%B5%9D%E4%B9%9F%E6%87%82%E7%A8%8B%E5%BC%8F%E8%A8%AD%E8%A8%88/windows10-hyper-v%E5%AE%89%E8%A3%9Dubuntu%E6%95%99%E5%AD%B8-9dad101dc1aa

[10] 擴增Hyper-V 快速建立 Ubuntu虛擬機器的硬碟容量
https://medium.com/@lag945/%E6%93%B4%E5%A2%9Ehyper-v-%E5%BF%AB%E9%80%9F%E5%BB%BA%E7%AB%8B-ubuntu%E8%99%9B%E6%93%AC%E6%A9%9F%E5%99%A8%E7%9A%84%E7%A1%AC%E7%A2%9F%E5%AE%B9%E9%87%8F-1b2b819f6c35

[11] win10 Hyper_v中Ubuntu虚拟机和主机共享文件
https://blog.csdn.net/u013617648/article/details/72969429

[12] Kivy's documentation
https://kivy.org/doc/stable/

[13] JAVA -version still showing 1.8 after updating to 11 on LINUX [closed]
https://stackoverflow.com/questions/72290092/java-version-still-showing-1-8-after-updating-to-11-on-linux

沒有留言:

張貼留言