自從去年底 (2024/11) 開始用 python 開發 gui 程式,寫啊寫功能越來越豐富了,已經有一些很好用的亮點,可不可以把它打包成單一執行檔呢?這樣可以拿去服務其他的使用者,成為可向外推廣的產品,真是很棒的想法。稍微研究一下 PyInstaller 的用法,功能很強大,目前想到的產品化需求,全部可以辦到。
平時用開發環境在執行程式,因為環境會一直變動,開發越久就越肥大,久而久之就忘了如何從無到有,用最小必要條件來執行,需要來回想歸納一下,讓程式成功執行所需要的流程和條件:
- 安裝 python 執行環境,並安裝程式有用到的相依套件。
- 大部分的套件是在雲端託管平台,只要 pip install 就可以安裝了;但也有少部分,如富邦證券提供的 sdk,是一個 whl 檔,分不同的 os 平台。開發階段在本地安裝當然沒問題,但若要作為「網站後台」部署到雲端伺服器,則 requirements.txt 必須有這一行「--find-links wheels」,意思是到 wheels 目錄尋找安裝檔,而安裝檔必須在佈署時一併上傳。而本次打包需求為 gui application 則無這層顧慮,PyIntaller 會自動把所有用到的套件都納入,也不需要 requirements.txt,此功能令人激賞。
- 資源檔們。就是程式執行會用到的靜態檔案。如本系統用到 google firestore 文件資料庫和 google storage 檔案庫,兩者都需要讀取一個 serviceAccountKey.json 靜態檔,作爲帳戶識別和認證。此帳號相關資訊和金鑰,與使用者無關,屬於系統底層的重要參數。這種資源檔,若開發時很隨意的放在程式所在目錄,或本地特定寫死的目錄,打包程式是不會知道的,因此必須特別處理。我的作法是在專案中開立一個目錄 resource 存放所有資源檔,打包時加入「--add-data "resource:resource"」告訴 PyInstaller 要包含 resource 裡所有的檔案。語法中 mac and windows 稍有不同,windows 需要用分號「--add-data "resource;resource"」,意思是從 resource 目錄打包檔案,當使用者執行時,再把它解壓縮放置於 resource 目標目錄。依照這樣的設計,當程式要取用時就不可 hard code 靜態路徑,而是要判斷是開發環境還是分發後的環境,再動態指定路徑,所以一個小小的公用程式是必須的:
def resource_path(filename: str) -> str:
try:
# PyInstaller 打包後會設置 _MEIPASS
base_path = sys._MEIPASS
except AttributeError:
base_path = os.path.abspath(".")
return os.path.join(base_path, "resource", filename) - 使用者需要自定義的檔案們。包含使用者自己的帳號密碼,也許存在某個 ini 檔或是 python 所慣用的 .env 隱藏檔,還有存取券商帳號最重要的「憑證檔」,還有我,作為開發廠商,為了管理使用權限的「授權檔」,通常是加密過後文字檔,這些通通都「不能」打包進入安裝包。不然取得安裝包的人,也許就存取到別人的資料,或是變成永久免費使用,那問題就嚴重了,安全是無可妥協的。這類檔案,在開發階段我選擇放置在專案根目錄,而分發階段就由使用者自行放置在執行檔所在目錄。所以再寫一段小程式判斷開發階段或分發階段,與前述取得 resource 位置,方法類似,異曲同工:
def get_project_root():
if getattr(sys, 'frozen', False):
return os.path.dirname(sys.executable)
else:
return os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
以上已經萬事俱備,可以來執行打包了,把指令寫成可重複使用的 .sh 或是 .bat 是合理的做法:
-------------------mac------------
#!/bin/bash
echo "🔧 開始打包..."
# 清除舊的 build
rm -rf dist build
# 打包 main.py,包含 resource 資料夾
pyinstaller main.py \
--add-data "resource:resource" \
--noconfirm \
--icon=nm.ico \
--clean \
--onefile \
--name "RiskManager"
echo "✅ 打包完成!請查看 dist/RiskManager"
-----------windows----------------------
@echo off
echo 🔧 打包開始...
REM 建立一個乾淨的打包環境
rmdir /s /q dist
rmdir /s /q build
REM 使用 PyInstaller 打包 main.py,包含 resource 資料夾
pyinstaller main.py ^
--add-data "resource;resource" ^
--noconfirm ^
--icon=nm.ico ^
--clean ^
--onefile ^
--name "RiskManager"
echo ✅ 打包完成!請查看 dist\RiskManager.exe
pause
從僅僅只是自己用的開心,走向產品化道路,這是艱難的過程。技術面主要的關卡有使用者認證,授權管理,和本文所提的佈署分發問題。Web 和 Application 各有不同的技術考量,我目前的題目牽涉到存取個人金融帳戶的敏感資料,用 Application 可以將個人帳密和憑證都保留在本地,可能是比較好的切入點,後續在設計好安全機制之後,也會開發 Web 版的功能。
此外關於產品定位,是更難的問題。可以自行獨立開發是幸福的事,總是發散式的多方嘗試,但產品則需要明確定義目標用戶,和所需強調的亮點,所能解決的痛點。經過一番取捨,終於把安裝檔瘦身到 60 幾 Mega,另外製作產品說明文件如下:
這次要玩真的了。
Newman 2025/8/7
導覽頁:紐曼的技術筆記-索引