WIN10 Electron+Python界面開發(通信方式:thrift)
Python做界面開發要麼繁瑣要麼太醜,同時Python客戶端開發人員又是非常稀少的。而WEB前端工程師一抓一大把,同時WEB前端所開發出來的界面及交互效果都是非常美觀的,同時有的軟件可能客戶端也需要,WEB端也需要,甚至移動端也需要,在要求美觀的同時,有沒有一個解決方案就能適應所有平臺的呢?
沒錯,目前最好的解決方案就是做WEB開發,首先其本身能在WEB上使用,對於移動端來講,打包WEBAPP的工具一搜一大堆,而想同時能滿足客戶端的需求? 那就可以使用Electron了。
Electron也已經比較成熟了,目前很多界面每隔的桌面程序都是Electron開發的,比如說:Github、Skype、Atom、VSCode等。
而此篇博文主要分享Electron+Python的方式做界面開發。網上也有比較多的教程,但大部分教程裏Electron和Python的通信方式要麼是Http,要麼是zerorpc,Http太笨重,且不太適合客戶端程序;npm的zerorpc安裝過程太繁瑣,太多版本問題,反正我用npm安裝zerorpc搞了好幾天都沒弄好,因此最終換其他通信方案(期間也試過谷歌的gRPC),比較後最終選擇Thrift,除了性能優勢外,安裝及配置都比較簡單。
流程如下:
start
|
V
+--------------------+
| | start
| electron +-------------> +------------------+
| | sub process | |
| (browser) | | python server |
| | | |
| (all html/css/js) | | (business logic) |
| | thrift | |
| (node.js runtime, | <-----------> | (thrift server) |
| thrift client) | communication | |
| | | |
+--------------------+ +------------------+
安裝配置
-
系統:win10
-
開發環境:
- Python: 3.7
- node: v10.15.3
- npm: 6.9.0
-
python環境:
pip install thrift
-
步驟:
-
首先創建你的應用文件夾: app
-
進入文件夾
npm init
初始化 -
修改
package.json
文件,參考:{ "name": "ele_test", "version": "1.0.0", "description": "", "main": "main.js", "dependencies": { "thrift": "^0.12.0" }, "devDependencies": {}, "scripts": { "start": "electron ." }, "author": "", "license": "ISC" }
-
安裝npm第三方包:
npm install electron -g
(如果安裝太慢或者安裝不了的話試試先安裝cnpm
,再使用cnpm install electron -g
)- npm install thrift
-
去github下載thrift.exe(其他平臺下載相應內容即可)
-
新建接口文件
test.thrift
:service userService { string test1(1:string name) }
-
生成各自的接口文件:
thrift -out 存儲路徑 --gen 接口語言 thrift接口文件名
-
開發
編寫客戶端(前端)相關文件:
在app目錄下新建文件main.js
:
const {app, BrowserWindow} = require('electron')
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win
function createWindow () {
// 創建瀏覽器窗口。
win = new BrowserWindow({width: 800, height: 600, webPreferences:{nodeIntegration:true}})
// 然後加載應用的 index.html。
win.loadFile('index.html')
// 打開開發者工具
win.webContents.openDevTools()
// 當 window 被關閉,這個事件會被觸發。
win.on('closed', () => {
// 取消引用 window 對象,如果你的應用支持多窗口的話,
// 通常會把多個 window 對象存放在一個數組裏面,
// 與此同時,你應該刪除相應的元素。
win = null
})
}
// Electron 會在初始化後並準備
// 創建瀏覽器窗口時,調用這個函數。
// 部分 API 在 ready 事件觸發後才能使用。
app.on('ready', createWindow)
// 當全部窗口關閉時退出。
app.on('window-all-closed', () => {
// 在 macOS 上,除非用戶用 Cmd + Q 確定地退出,
// 否則絕大部分應用及其菜單欄會保持激活。
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
// 在macOS上,當單擊dock圖標並且沒有其他窗口打開時,
// 通常在應用程序中重新創建一個窗口。
if (win === null) {
createWindow()
}
})
// 在這個文件中,你可以續寫應用剩下主進程代碼。
// 也可以拆分成幾個文件,然後用 require 導入。
const path=require('path')
let pyProc = null
let pyPort = null
const createPyProc = () => {
// let port = '4242'
let script = path.join(__dirname, 'py', 'thrift_server.py')
pyProc = require('child_process').spawn('python', [script])
if (pyProc != null) {
console.log('child process success')
}
}
const exitPyProc = () => {
pyProc.kill()
pyProc = null
pyPort = null
}
app.on('ready', createPyProc)
app.on('will-quit', exitPyProc)
新建文件render.js
:
// renderer.js
var thrift = require('thrift');
// 調用win10下thrift命令自動生成的依賴包
var userService = require('./gen-nodejs/userService.js');
var ttypes = require('./gen-nodejs/test_types.js');
var thriftConnection = thrift.createConnection('127.0.0.1', 8000);
var thriftClient = thrift.createClient(userService,thriftConnection);
thriftConnection.on("error",function(e)
{
console.log(e);
});
/* var client = new zerorpc.Client();
client.connect("tcp://127.0.0.1:4242"); */
let name = document.querySelector('#name')
let result = document.querySelector('#result')
name.addEventListener('input', () => {
var dic = {name: name.value}
dic = JSON.stringify(dic)
thriftClient.test1(dic, (error, res) => {
if(error) {
console.error(error)
} else {
result.textContent = res
}
})
})
name.dispatchEvent(new Event('input'))
新建index.html
:
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello XX</title>
</head>
<body>
<input id="name" ></input>
<p id="result" color='black'></p>
</body>
<script>
require('./render.js')
// import './render.js'
</script>
</html>
編寫服務端相關文件
在app目錄下新建目錄py,
進入py目錄,新建文件thrift_server.py
:
import json
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from thrift.server import TServer
from gen_py.test import userService
class Test:
def test1(self, dic):
print("one")
dic = json.loads(dic)
return f'Hello, {dic["name"]}!'
if __name__ == "__main__":
port = 8000
ip = "127.0.0.1"
# 創建服務端
handler = Test() # 自定義類
processor = userService.Processor(handler) # userService爲python接口文件自動生成
# 監聽端口
transport = TSocket.TServerSocket(ip, port) # ip與port位置不可交換
# 選擇傳輸層
tfactory = TTransport.TBufferedTransportFactory()
# 選擇傳輸協議
pfactory = TBinaryProtocol.TBinaryProtocolFactory()
# 創建服務端
server = TServer.TThreadedServer(processor, transport, tfactory, pfactory)
print("start server in python")
server.serve()
print("Done")
啓動
運行npm start
即可運行
打包
測試沒問題之後我們需要將應用打包,因爲別人電腦上不一定裝了node.js或是python。首先要裝個打包工具pip install pyinstaller
。
在package.json
的script
中加入"build-python":"pyinstaller ./py/thrift_server.py --clean"
。然後運行npm run build-python編譯一下
。編譯完了可以把根目錄下生成的build
文件夾和thrift_server.spec
刪了。如果中間報錯 AttributeError: module 'enum' has no attribute 'IntFlag'
,就運行pip uninstall enum34
把enum34刪了。
This is likely caused by the package
enum34
. Since python 3.4 there’s a standard libraryenum
module, so you should uninstallenum34
, which is no longer compatible with the enum in the standard library sinceenum.IntFlag
was added in python 3.6
之前子進程是通過調用python命令運行的,現在我們要換成生成的可執行程序。修改main.js
:
// let script = path.join(__dirname, 'py', 'thrift_server.py')
// pyProc = require('child_process').spawn('python', [script])
let script = path.join(__dirname, 'py', 'dist','thrift_server')
pyProc = require('child_process').execFile(script)
運行npm start
可以查看效果。
在根目錄運行npm install electron-packager --save-dev
安裝Electron打包模塊。然後將"pack-app": "./node_modules/.bin/electron-packager . --overwrite --ignore=py$"
寫入package.json
的script中。
運行npm run pack-app
打包程序。最後會生成可執行文件,複製到別的電腦也可以運行。
代碼已上傳至Github