Electron+Python界面开发(通过zerorpc)

2020/06 02 18:06

Python 开发GUI要么太繁琐要么太丑,而前端技术恰巧是最适合做漂亮UI的。所以考虑将Python和前端技术结合,通过进程通信和前端框架交流,打包成一个完整的桌面APP。教程分成两种实现方式,一个是zerorpc进程通信一个是http通信。

这篇教程介绍zerorpc的方式,流程如下:
















start
 |
 V
+--------------------+
|                    | start
|  electron          +-------------> +------------------+
|                    | sub process   |                  |
| (browser)          |               | python server    |
|                    |               |                  |
| (all html/css/js)  |               | (business logic) |
|                    |   zerorpc     |                  |
| (node.js runtime,  | <-----------> | (zeromq server)  |
|  zeromq client)    | communication |                  |
|                    |               |                  |
+--------------------+               +------------------+

Electron基础

详情见官方文档:electronjs.org/docs

如果你可以建一个网站,你就可以建一个桌面应用程序。 Electron 是一个使用 JavaScript, HTML 和 CSS 等 Web 技术创建原生程序的框架,它负责比较难搞的部分,你只需把精力放在你的应用的核心上即可。

有很多有名的App是用Electeon开发的,如:Skype和GitHub以及著名编辑器Atom,所以这个框架在水平上是被认可的。

Electron 可以让你使用纯 JavaScript 调用丰富的原生(操作系统) APIs 来创造桌面应用。 你可以把它看作一个专注于桌面应用的 Node. js 的变体,而不是 Web 服务器。

这不意味着 Electron 是某个图形用户界面(GUI)库的 JavaScript 版本。 相反,Electron 使用 web 页面作为它的 GUI,所以你能把它看作成一个被 JavaScript 控制的,精简版的 Chromium 浏览器。

安装

为你的新Electron应用创建一个新的空文件夹。 打开你的命令行工具,然后从该文件夹运行npm init。将package.json修改一下。

{
"name":"your-app",
"version":"0.1.0",
"main":"main.js",
"scripts":{
"start":"electron ."
}
}

现在,您需要安装electron。 我们推荐的安装方法是把它作为您 app 中的开发依赖项,这使您可以在不同的 app 中使用不同的 Electron 版本。 在您的app所在文件夹中运行下面的命令:


npm install --save-dev electron

使用

electron模块包含了Electron提供的所有API和功能,引入方法和普通Node.js模块一样:

constelectron=require('electron')

electron 模块所提供的功能都是通过命名空间暴露出来的。 比如说: electron.app负责管理Electron 应用程序的生命周期, electron.BrowserWindow类负责创建窗口。 下面是一个简单的main.js文件,它将在应用程序准备就绪后打开一个窗口:

const{app,BrowserWindow}=require('electron')

functioncreateWindow(){

// 创建浏览器窗口
win=newBrowserWindow({width:800,height:600})


// 然后加载应用的 index.html。
win.loadFile('index.html')
}

app.on('ready',createWindow)

您应当在 main.js 中创建窗口,并处理程序中可能遇到的所有系统事件。 下面我们将完善上述例子,添加以下功能:打开开发者工具、处理窗口关闭事件、在macOS用户点击dock上图标时重建窗口,添加后,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.
letwin

functioncreateWindow(){

// 创建浏览器窗口。
win=newBrowserWindow({width:800,height:600})


// 然后加载应用的 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 导入。

最后,创建你想展示的 index.html

<!DOCTYPE html>
<html>
<head>
<metacharset="UTF-8">
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>

      We are using node <script>document.write(process.versions.node)</script>
,
      Chrome <script>document.write(process.versions.chrome)</script>
,
      and Electron <script>document.write(process.versions.electron)</script>
.
    </body>
</html>

启动

在创建并初始化完成 main.js、 index.htmlpackage.json之后,您就可以在当前工程的根目录执行 npm start 命令来启动刚刚编写好的Electron程序了。

Python部分

安装pip install zerorpc。在项目根目录创建文件夹py,用于存放Python相关代码。新建一个python文件,命名为api.py。敲入如下demo。

importzerorpc

classHelloRPC(object):
defhello(self,name):
return"Hello, %s"%name

s=zerorpc.Server(HelloRPC())
s.bind("tcp://0.0.0.0:4242")
s.run()

然后命令行里运行python api.py。再另一个终端输入zerorpc tcp://localhost:4242 hello NXB,如果得到Hello,NXB则没有问题。

Electron部分

接着之前的main.js后面写。我们首先需要node.js能够调用Python进程。

constpath=require('path')

letpyProc=null
letpyPort=null

constcreatePyProc=()=>{
letport='4242'
letscript=path.join(__dirname,'py','api.py')
pyProc=require('child_process').spawn('python',[script,port])
if(pyProc!=null){
console.log('child process success')
}
}

constexitPyProc=()=>{
pyProc.kill()
pyProc=null
pyPort=null
}

app.on('ready',createPyProc)
app.on('will-quit',exitPyProc)

写完后运行npm start看看能不能启动python子程序按照之前的方式测试一下能不能通信。没问题的话继续。

修改我们的主页index.html,构建一个输入框。我们希望在输入框里输入字符,下方可以动态显示Hello,XX。

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<metacharset="UTF-8">
<title>Hello XX</title>
</head>
<body>
<inputid="name"></input>
<pid="result"color='black'></p>
</body>
<script>
require('./render.js')
</script>
</html>

在根目录下创建render.js用于监听输入框,将输入框的内容动态发送给python进程,并接收反回来的消息。


// renderer.js
varzerorpc=require("zerorpc");
varclient=newzerorpc.Client();
client.connect("tcp://127.0.0.1:4242");

letname=document.querySelector('#name')
letresult=document.querySelector('#result')
name.addEventListener('input',()=>{
client.invoke("hello",name.value,(error,res)=>{
if(error){
console.error(error)
}else{
result.textContent=res
}
})
})
name.dispatchEvent(newEvent('input'))

如果没问题的话应该是这样的效果:

打包

测试没问题之后我们需要将应用打包,因为别人电脑上不一定装了node.js或是python。首先要装个打包工具pip install pyinstaller

package.jsonscript中加入"build-python":"pyinstaller ./py/api.py --clean --distpath ./pydist"。然后运行npm run build-python编译一下。编译完了可以把根目录下生成的build文件夹和api.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', 'api.py')

// pyProc = require('child_process').spawn('python', [script, port] )letscript=path.join(__dirname,'pydist','api','api')
pyProc=require('child_process').execFile(script,[port])

运行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打包程序。最后会生成可执行文件,复制到别的电脑也可以运行。

--转载请注明: http://91o.cc/electronpython%e7%95%8c%e9%9d%a2%e5%bc%80%e5%8f%91%ef%bc%88%e9%80%9a%e8%bf%87zerorpc%ef%bc%89/