WIN10 Electron+Python界面開發(通信方式:thrift)

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
  • 步驟:

    1. 首先創建你的應用文件夾: app

    2. 進入文件夾 npm init 初始化

    3. 修改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"
      }
      
    4. 安裝npm第三方包:

      • npm install electron -g(如果安裝太慢或者安裝不了的話試試先安裝cnpm,再使用cnpm install electron -g)
      • npm install thrift
    5. 去github下載thrift.exe(其他平臺下載相應內容即可)

    6. 新建接口文件test.thrift:

      service userService {
          string test1(1:string name)
      }
      
    7. 生成各自的接口文件:

      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.jsonscript中加入"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 library enummodule, so you should uninstall enum34, which is no longer compatible with the enum in the standard library since enum.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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章