首发于SyntaxError

使用 web 技术构建 python 的 GUI 界面

一般来说,选择用于应用程序的 GUI 工具箱会是一个棘手的事情,Python 也不例外。对于 Python 来说,可以选择的工具箱种类繁多。就我所知道的而言,比较常用的就有 TkInter, wxPython, PyQT/PySide 等。我试过 TkInter 和 wxPython,对 QT 的 Python 实现(PyQT/PySide)也作了略微的了解。大致来说,TkInter 相当于一个可用的缺省选择,但功能不够强大。wxPython 则有更丰富的组件,可以实现绝大多数的应用需求。但是在布局效率,可定制性以及交互方式上面并不出众。

web 技术的优势

如果论快速构建出一个体验良好的 UI 系统的话,我想没有什么技术能和 web 技术相比了。Web 天生就是为交互而生,利用 javascript,可以比较方便的实现各种交互;用 css 语言进行 UI 布局,可以对 UI 进行精确调控,而且是可见的,相对于 wxPython 之类的盲调,效率高了许多;Web 前端技术的繁荣,催生了很多实用的框架和库,借助 vue.js 等类似的响应式前端框架,能够把精力集中在模型的构建上。这一切都让使用 web 技术构建 UI 界面变得高效。

所以这些天,我查阅了一些资料,对需要使用的技术进行了选型,最终形成了一套比较成熟的解决方案。

整体架构

在架构上吸收了 MPV 模式的思想,尽量减少不同层之间的依赖,实现低耦度。而 View-Level 则通过 Vue 的类 MVVM 框架进行渲染。

使用到的技术栈/库:

  • node.js: Electron, Vue.js, webpack, zerorpc, element-ui
  • Python: zerorpc, peewee



在整个架构中,具体功能和逻辑均由 Python 实现;Node.js 的部分仅作为视图层进行数据展示及交互控制。Python 将具体功能暴露为 API,并通过 RPC 方式与 Node.js 进行通讯。

Model:与数据库通信,将数据库记录的数据抽象成 python 可以识别的对象。

Presenter:响应 View 的操作,并和 Model 进行交互,向 View 返回数据。Logic 是其中具体实现逻辑的部分(可视为一个整体)。

View:基于 Electron 实现,采用 Vue.js 作为响应式框架。

Presenter 和 View 之间的通信通过 zerorpc 实现。

所涉及到的技术的简述:

Electron:

Electron是由Github开发,用HTML,CSS和JavaScript来构建跨平台桌面应用程序的一个开源库。原名 Atom Shell,起初是作为构建Github上可编程的文本编辑器Atom的框架而被开发出来,后来看着很好用就开源了,并改名 Electron。目前它已成为开源开发者、初创企业和老牌公司常用的开发工具。

Atom Shell,Electron,Atom Shell,Electron…嗯,原子的壳就是电子,你说的真对。

Electron 使用 Cromium 的渲染引擎,提供了一套类似浏览器的渲染环境,同时实现了与桌面系统进行交互的一系列接口。简单的讲,它可以认为是这两样东西的组合:一个可以运行网页的浏览器,以及一些可以通过网页调用系统原生资源的接口——例如原生的菜单,对话框,消息提示等。相当于在利用网页强大的交互功能的基础上,扩展了网页与原生桌面进行交互的能力。而交互方式的实现则主要是通过 web 技术完成的,只是在实现交互的过程中,调用了一些接口,所以如果你熟悉 web 技术,用 Electron 编写应用对你来说不会显得太陌生。

正因为如此,Electron 在使用上是比较零散的,没有什么结构——而它的文档也是如此。一般来讲,你需要搞清楚 BrowserWindow,主进程与渲染进程等基本的概念,以及主/渲染进程之间相互调用的方式。其它的内容/接口,在需要用到的时候查阅对应的文档就可以了,由于内容之间没有太多的依赖,学习成本相对不高。

Electron 的官方文档。大部分内容已经被翻译为中文。

Mac App Store 和 Windows Store 都已支持 Electron 编写的应用。但 Electron 并不能够用来编写移动应用,未来也不会支持。

Vue.js:

Vue.js 是一套用于构建用户界面的渐进式框架。它的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统,并且实现了响应式的数据绑定。此外,它还有强大的路由功能和组件系统,能够模块化的构建用户界面。Vue.js 是一个结构化的东西,不同内容之间存在较为紧密的依赖,因此你需要完整的学习整个结构,学习成本是比较高的。即便是引用别人的组件,也需要知道它的工作方式,不然遇到各种奇怪的问题,你不知道怎么解决。

Vue.js 具有完整的中文官方文档

electron-vue:

基于 vue (基本上是它听起来的样子) 来构造 electron 应用程序的样板代码。该项目的目的,是为了要避免使用 vue 手动建立起 electron 应用程序。electron-vue 充分利用 vue-cli 作为脚手架工具,加上拥有 vue-loader 的 webpack、electron-packager 或是 electron-builder,以及一些最常用的插件,如vue-router、vuex等等。

electron-vue 也有完整的中文官方文档。使用前一定要完整的阅读整个官方文档。

ElementUI:

饿了么出品的一个前端组件库。拥有常见的交互式组件,每个组件都有丰富的设置选项。这样你就拥有了强大多样的交互方式。加上这是 web,你可以很方便的将这些组件定制化,精确的实现独特的需求。

zerorpc:

基于 ZeroMQ 的高性能分部式 RPC 框架。RPC 即远程过程调用协议。不同过程之间通过 TCP 的方式进行通信。zerorpc 在 Python 和 node.js 上都有实现,因此我们可以通过 zerorpc 在 Python 和 node.js 之间进行通信。

peewee:

一个轻量级的 ORM (Object Relational Mapping) 库。可以将数据库抽象成 Python 可以识别的对象。使用方法和 Django model 很类似。

一些讨论

文件大小:

为了打包 Electron 的依赖环境,Electron 最终生成的 exe 文件包至少有 70M 左右,这些都是 Electron 相关的依赖。但在 PC 使用环境中,这并不是什么问题,80M 并不是很大的数字,相对于当今的硬盘空间根本不算什么。只有在存储资源稀缺的场景,如嵌入式开发中,文件大小才是有必要考虑的因素。

另外,你可以使用 UPX 对 electron.exe 和 node.dll 进行压缩,压缩后,这两个文件的大小之和从 69M 下降到 25M 以下。

兼容性:

最近发现一个兼容性问题,在 win 7 中使用非 Aero 主题(就是透明边框主题)时,从 electron 应用切换到其它应用时,其它应用的部分界面可能不会刷新,也就是在其它应用上留有 electron 应用的残影。

这个问题是 electron 的一个历史 bug,一定程度上也是 win 7 的一个缺陷。在所有用 electron 编写的应用中,例如 VS Code, Slack,都存在该问题。目前 electron 官方并无解决该 bug 的意图,唯有切换到 Aero 主题。

https://github.com/electron/electron/issues/1821github.com

关于评论区一些讨论的汇总:

这些天和评论区的一些人进行了一些争论和讨论,有些评论很有道理,到这里做个总结。

1. 架构的结构比较复杂,初期导致调试起来比较困难。有很多时候通过报错,并不能比较直观的找到错误的根源。特别是初期搭建的时候,需要花费比较大的精力去踩坑。因此,对于比较简单的需求是很不经济的。

我是形成了一套以后可以复用的通用方法,后期使用会很方便。换句话说,你得抱着“构建工具”的心态来搭建它,这自然比单纯使用现成的工具要复杂。但幸运的是你以后可以很方便的复用它。

2. 打包后体积大

体积大主要是 electron 的依赖体积大,这跟用了两个语言其实没太大关系,python 的依赖(排除 modules )的体积相对来说并不大。

这个问题,一是看你项目的大小。如果非常简单,相对来说,对体积的大小影响就显著。二是看你的应用场景。在 PC 上这通常不是什么问题,一个手机 APP 都一百多M,70M真的不算什么。

3.使用了 2 个运行时,会不会性能很差?

使用了 node.js 和 python,导致有两个运行时。这样确实比 “只用 python” 占用更多的资源,但主要是 electron 相对比较耗资源,并不是多一个运行时少一个运行时的问题。毕竟资源主要是运行时里面所包含的逻辑消耗的,而这些逻辑被分离到两个语言了,仅此而已。

python 的性能低不低呢?看评论区,我觉得有些人可能对 python 的性能有什么极端的误解,这些讨论知乎可以很方便的搜到。我只简单的说几个方面。

首先,python 的性能“很多时候”并不差。为什么呢?因为 python 及其第三方库的某些“计算密集”的核心部分,本来就是 C 语言写的,例如 numpy。python 通常只负责顶层的逻辑设计,而这些计算密集的部分才是决定程序运行性能的关键。如果你的代码中有某些计算密集的部分,将这一小部分改写成 C 语言,就可以显著提高性能。这跟你用的脚本语言是一个还是两个关系不大。。。

其次,随着计算机性能的提升,在性能不是瓶颈的情况下,这问题真的可以忽略不计。单纯一个 GUI 界面真的耗不了多少资源。反应都是瞬间,用户根本感觉不出来区别。

当然这些都得看你具体的应用场景了。至少在我们实验室那比较老旧的电脑上是没什么问题的,因为本来资源占用也不高,能有啥区别呢。

我觉得如今电脑性能已经非常强大,相对来说,大部分应用并没那么耗资源,因此相比于那一点点的性能提升,编码效率、用户体验、跨平台等才是更值得考虑的。python 性能相对较差是客观事实,但似乎有人将其估计的过分严重甚至超出常识范围了。

另外搜知乎发现现在有很多显著提高 python 性能的方法,有时间可以研究下。

4. 为什么不全部由 node.js 实现?多用一个 python 是不是多此一举?

我们讨论的是,怎么给 python 加一个 GUI 界面,这是前提。比如有些行业,python 是比较通用的语言,而 js 估计只有搞过前端的才会用,这样 js 就不适合作为逻辑层面的代码,但作为 GUI 层的代码是没有问题的。总之就是看你自己的使用场景了。

5. 关于学习成本

如果你没有任何前端基础,当然学习成本是很高了,你得先学 HTML,CSS, javascript, 然后再了解 node.js ,最后你还要熟练掌握 Vue.js(或者其替代品)和一个组件库。webpack 的使用也是需要较长时间熟悉或踩坑的。

然而也有很多本来就懂得一些前端知识的人,对于他们来说,可能新学一个 GUI 库成本更高吧。

------

另:关于如何用 zerorpc 在 electron 和 python 之间通信的例子,可参考我翻译的这篇文章,里面附有英文链接。

白銀之魔女:使用 web 技术构建 python 的 GUI 界面zhuanlan.zhihu.com图标

动机:

之前在知乎搜索"用 Web 技术做 python GUI"的相关内容,却没有比较好的解决方法,后来无意看到这篇文章,本着跟大家分享的善意翻译过来,并且把自己最终的架构也分享出来。这问题据我所知关注的人还不少,因此应该对一些人有所帮助。

一篇翻译,描述了用 electron 作为 python GUI 的基本方法

一篇文章,分享了自己搭建的比较完整的架构


使用 web 技术构建 python 的 GUI 界面swiity.com

编辑于 2018-06-28

文章被以下专栏收录