写在前面

注: 如果您是第二次阅读本文, 推荐直接阅读 快速开始 章节以快速复现最终运行效果.

本文所涉及文件已存放在网盘空间: https://www.jianguoyun.com/p/DdlvwhwQ7t6sBxjJkcoB 欢迎下载使用.

概述

本文是基于一个 python 开发者的角度, 尝试使用 electron 来开发桌面应用.

请注意本文只是一个面向新手的文章. 内容涉及 如何让 electron 调用起 python 模块, 以及 python 的数据如何传递到 electron 界面展示出来.

背景

本文假设读者已具备以下能力:

  1. 有一定的 python 基础
  2. 有少量的 javascript 知识 (能够写出 hello world 的程度)
  3. 会用 npm init 初始化项目
  4. 完成过 electron 的 hello world 类项目的启动

本文假设读者已经做好以下准备:

  1. 安装 nodejs, npm, python 3
    1. 安装模块 (推荐安装在测试项目目录下):
      1. nodejs 模块: electron, python-shell
      2. python 模块: flask
  2. 编辑器. vscode, pycharm, webstorm 都可以
  3. 创建本次测试项目的目录, 比如我的项目路径为 “D:/workspace/test_room/electron_with_python/”, 后面将简称为 “electron_with_python” 或者 “本项目”

正文

1. 初始化项目

通过 npm init 初始化项目. 得到的项目目录结构如下:

D:/workspace/test_room/electron_with_python/
	node_modules/  # 这是 npm 在本项目的模块安装目录
	venv/  # 这是 python 在本项目的虚拟环境 (包含编译器, 本项目安装的模块等)
	index.html
	main.js
	package.json

然后在本项目中通过 npm installpip install 来安装 electron, python-shell, flask 模块.

index.html, main.js, package.json 的源码见下面. 我尽量在保持易于阅读的基础上最简化 (注: 并非最终代码), 代码如下:

index.html

<h1>Welcome</h1>
<h3>This is a python-electron app.</h3>

main.js

// 导入模块
const { app, BrowserWindow } = require("electron")

// 创建窗口
function createWindow() {
    let win = new BrowserWindow({
        // 设置一个宽为800, 高为600的窗口
        width: 800,
        height: 600
    })
    // 加载本地的 index.html
    win.loadFile("index.html")
}

// 启动
app.on("ready", createWindow)

package.json (注意 devDependencies 中要有 electron 和 python-shell 两个模块)

{
  "name": "electron_with_python",
  "version": "1.0.0",
  "description": "Python + Flask + Electron 项目演示",
  "main": "main.js",
  "scripts": {
    "start": "electron ."
  },
  "author": "Likianta",
  "license": "ISC",
  "devDependencies": {
    "electron": "^4.1.3",
    "python-shell": "^1.0.7"
  }
}

在命令行 cd 到本项目路径, 输入 npm start 启动, 大家看到的效果应该和下图一样:

1554906638332.png

2. 加入 python-shell

python-shell 是一个 nodejs 模块, 所以可以在 main.js 中导入并使用.

python-shell 用于调用 python 写的 py 文件, 所以我们先在项目下新建一个 “hello.py” 文件:

# ./hello.py

# 里面就一句话
print("hello from python")

然后修改 main.js 文件:

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

// 创建窗口
function createWindow() {
    let win = new BrowserWindow({
        width: 800,
        height: 600
    })
    win.loadFile("index.html")
}

// 从 python-shell 导入一个 PythonShell 对象 (注意大小写)
const {PythonShell}  = require("python-shell")
// PythonShell 主要有 run() 和 runString() 两个方法, 这里我们用 run()
// run() 第一个参数是要调用的 py 文件的路径
// 第二个参数是可选配置 (一般传 null)
// 第三个参数是回调函数
PythonShell.run(
	"hello.py", null, function (err, results) {
        if (err) throw err
        console.log('hello.py finished')
        console.log('results', results)
    }
)

// 启动
app.on("ready", createWindow)

启动效果如下, 可以看到 main.js 中的两条 console.log() 都被触发成功:

[外链图片转存失败(img-BBICF1p5-1563031004213)(https://i.loli.net/2019/04/11/5cae1716b40c2.png)]

这意味着到了这里, 我们已经实现了 electron 调用 python 模块的功能.

3. 加入 flask

大家对 flask 可能比较陌生, 目前只需知道它是一个 python web 框架 (微服务) 就可以了.

既然是 web 框架, 就肯定有端口, 路由, html 渲染模板等东西.

在项目下新建一个 engine.py, 代码如下:

from flask import Flask, render_template

'''
导入的这两个模块, Flask 是主体类, render_template 是渲染模板.
'''

# 首先创建一个变量 app, 用于初始化 flask 启动核心
app = Flask(__name__)
'''
感兴趣的可以看一下它的源码, 当我们把 __name__ 传进去后, Flask 的实例化行为:
    (flask 源码) app.py -> Flask.__init__()
        (flask 源码) helpers.py -> _PackageBoundObject.__init__()
可以看到, Flask 会根据你传的 __name__ 定位到程序 (engine.py) 所在的根路径.
这个根路径的用处和 render_template 有关. 后面会讲.
'''


@app.route('/')
def homepage():
    # 将本函数绑定到路由根地址, 这样我们访问主地址时, 就能看到这个页面
    home = 'flask_welcome.html'  # 还未创建, 接下来会写
    return render_template(home)


if __name__ == "__main__":
    # 启动, 启动后访问 http://127.0.0.1:5858 查看
    app.run(host='127.0.0.1', port=5858)

再在项目下创建一个 “flask_welcome.html” 文件, 代码如下:

<h1>Welcome to Flask</h1>
<h3>This is a homepage rendered by flask.</h3>

现在我们尝试直接运行这个 py 文件看看效果:

20190410230305.gif

不幸的是, 访问主页就报错了.

报错的原因在 pycharm 控制台可以看到, 显示 jinja2 没有找到模板文件:

1554908841182.png

(这也是我喜欢 python 的原因之一, 报错容易诊断, 定位错误源方便.)

jinja2 模块是 flask 默认使用的模板渲染引擎, 也就是 render_template(home) 这个函数, 之前我们还没有讲, 现在补充知识:

Flask 默认会在程序根目录下的 templates 文件夹寻找要渲染的模板文件, 所以这里传入的模板文件的路径应是相对于 templates 目录的.

因此解决方法有两种, 要么想办法让 Flask 类的 template_folder 成员不要用 “templates” 作为模板目录入口, 要么就创建一个 templates 目录, 把 “flask_welcome.html” 放到里面.

第一种方法用一个自定义类继承于 Flask, 就可以任你发挥了, 当然这里我只演示第二种方法.

现在我们确认一下目录结构应该如下面所示:

D:/workspace/test_room/electron_with_python/
	node_modules/
	templates/
		flask_welcome.py
	venv/
	engine.py
	hello.py
	index.html
	main.js
	package.json

重新运行 engine.py, 在浏览器访问 http://127.0.0.1:5858/, 当如下图所示:

[外链图片转存失败(img-IS3WOL2g-1563031004214)(https://i.loli.net/2019/04/11/5cae171deed22.png)]

4. python, flask, electron 混合与实现

现在我们有了前三步的基础, 终于可以融会贯通了.

将 main.js 中的 “index.html” 改为 “templates/flask_welcome.html”, 把 “hello.py” 改为调用 “engine.py”, 来看看效果如何吧:

// main.js

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

// 创建窗口
function createWindow() {
    let win = new BrowserWindow({
        width: 800,
        height: 600
    })
    // 加载 "templates/flask_welcome.html"
    win.loadFile("templates/flask_welcome.html")
}

// 使用 python-shell 调用 engine.py
const {PythonShell}  = require("python-shell")
PythonShell.run(
	"engine.py", null, function (err, results) {
        if (err) throw err
        console.log('engine.py is running')
        console.log('results', results)
    }
)

// 启动
app.on("ready", createWindow)

[外链图片转存失败(img-dgAbTzHF-1563031004215)(https://i.loli.net/2019/04/11/5cae1720e1f0c.gif)]

以上就是 python + flask + electron 的项目演示.


快速开始

注: 本节内容适合二次阅读的读者快速复习和复现.

  1. 初始化项目

    1. 创建项目: “D:/workspace/test_room/electron_with_python/”

    2. 项目目录结构:

      D:/workspace/test_room/electron_with_python/
      	node_modules/
      	templates/
      		flask_welcome.py
      	venv/
      	engine.py
      	hello.py
      	index.html
      	main.js
      	package.json
      
    3. 安装模块:

      1. npm install … : electron, python-shell
      2. pip install … : flask
  2. 创建代码文件: 请从此网盘链接下载: https://www.jianguoyun.com/p/DdlvwhwQ7t6sBxjJkcoB
    下载此压缩包: “electron_with_python_20190411_000145.zip”
    (PS: 为保持压缩包体积, node_modules 和 venv 里的内容我都清空了.)

  3. 打开命令行, cd 到项目目录

  4. 输入 npm start, 运行本项目

  5. 效果应与下图一致:
    1554912444291.png


扩展阅读

TODO

注意事项

  1. electron 的调试器端口不可以和 flask 相同, 否则会报错

参考

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐