说明事项
为不挤占文章排版空间, 我将说明事项放在了文末. 点此跳转.
目录
本文内容量巨大. 请善用博客左侧的目录导航快速跳转你想看的章节.
💠 Python GUI
💡 声明式 UI 框架
🔥 QML (PySide6/PySide2/PyQt6/PyQt5) (★★★★★)
关键词: qt; 反应式编程; 高级动效; 声明式 ui; 现代化
应用截图: 点击跳转本文附录.
宽高, 以及隐式宽高的处理
qml 的 width, height 和 implicitWidth, implicitHeight 经常以 “出乎意料” 的方式打破我们的布局. 常见的问题比如:
- 组件看不见 (初始宽高未设定)
- 组件溢出容器边界 (缺少裁剪或约束)
- Button-contentItem-background 三者的宽高纠缠不清
- 组件宽高与预期不符 (在由下到上的和由上到下的层层继承和约束中迷失自我) 等
不仅是新手, 连老手也经常遇到这类难题.
没有百分比布局
略.
Model (在 Python 端实现) 上手难度高 (而且难用)
略.
在 Python 端获取 children 属性时表现 “异常”
在 qml 端使用 <qobj>.children
和 python 端使用 <qobj>.children()
得到的结果可能不一样!
二者的差别在于, python 端返回的结果 (list[QObject]
) 中可能会多出一个未知的元素 (该元素的所有属性都是 None), 但并不知道该元素的产生原因和出现规律.
import 必须指定版本号 (Qt 5.15 及以下)
很难想象一个人能为多个不同的导入记住不同的版本号. 虽然官方的版本号具有一定的规律性, 但除了最最常用的库外, 不少人还是记不住, 每当用到时都要翻翻手册.
所幸在 Qt 6.0 开始这种写法已经不需要了.
Rectangle 的 clip 对矩形的圆角裁剪无效的问题
参考 这篇文章 来解决.
Button 快速点击时信号 “丢失”
参考 这个回答 来解决.
🔥 Enaml (1.1k 标星. 个人评级 ★★★☆)
关键词: 纯 python 编程; 声明式 ui
enaml 给人的感觉是初见惊艳, 在 python dsl 这方面较 kv-lang 更胜一筹 (作者所称 enaml 语言是 python 的 “超集”, 令我印象深刻), 但这么多年过去了一直不温不火, 网上也鲜少有人讨论.
官方文档长时间未更新
首页还保留着一些 6, 7 年前的链接. 官方示例也大概有 3 年时间没有变化 (至少从我 2019 年第一次观察这个项目开始, 没感觉到变化).
UI 定制化 (Style) 不成熟
enaml 自己封装了一套 stylesheet, 但目前的文档完成度和提供的可定制程度都远不如 qss 和 qml.
缺少 IDE 支持
和 kivy (kv 语言) 类似的窘境, 但考虑到 enaml 中会更大量地使用 python 语法和模块, 缺少了 ide 级别的 python 支持 (代码补全, 静态分析, 重构等) 受到的影响更大一些.
💡 业务优先 > 视觉优先
🔥 PySimpleGUI (9.9k 标星. 个人评级 ★★★★★)
使用 pysimplegui (简称 “psg”) 开发界面的速度非常快, 包体积小, 写法简单, 语法直观, 界面效果也独具特色.
我在开发中小型工具应用中经常使用到 psg. 相比于 qml 按天起步的工作量, psg 几乎可以在几小时内完成原型需求.1
容易写出面条代码
略.
外观不够现代化. 主题只是在不断换配色
如图 (原图点这里查看):
🔥 Guietta (1.9k 标星. 个人评级 ★★★)
依赖于 PySide2, 体积较大
略.
于 2020 年末停止更新
略.
Python 3.10 运行示例报错
看报错内容应该是 guietta 用到了 ast 解析. 在解析 with 语法结构时出现了错误.
🔥 Gooey (★★☆)
gooey 将函数转换为 gui 界面. 一般拿它和 fire, click, argparse 等命令行传参的处理库做对比 (个人偏向使用 fire 和 click).
请注意与常规的 gui 相比, gooey 的使用场景是有限的.
💡 原生 (或类原生) 组件
🔥 wxPython (★★★☆)
TODO
🔥 Tkinter with Sun-Valley-ttk-theme (360 标星. 个人评级 ★★★)
TODO
🔥 Toga (★★☆)
TODO
🔥 appJar (☆)
TODO
🔥 Atlas (☆)
TODO
自绘引擎
🔥 PySide6 / PySide2 / PyQt5 (★★★★★)
TODO
🔥 DearPyGui (6.6k 标星, 个人评级 ★★★★)
缺少中文字体
需要自己找中文字体文件加载.
UI 字体发虚模糊
在本人的高分屏电脑上有遇到该问题, 暂不清楚是否跟系统设置有关, 需要进一步研究.
🔥 Kivy (13.7k 标星. 个人评级 ★★★)
特点:
- 充分利用 python 的语法特性, 有很多创新性的写法非常酷 (比如属性事件的魔术方法, 序列动画和并行动画的操作符重写等)
- kv 语言: 声明式语法 (对标 qml, enaml)
- 跨平台 (桌面端 + 移动端)
相关阅读:
- kv 语言对比 enaml: http://blog.codelv.com/2017/12/kivy-vs-enaml-native-comparision.html?m=1
中文显示乱码
最新的 kivy (2.1.0) 仍然没有解决, 而且长期来看也缺乏解决意愿.
中文界面仍然需要通过自带一个中文字体文件来解决乱码问题.
阴影效果差
使用 kivy 提供的方法制作出来的阴影效果看起来生硬, 不自然 (如图 1); 此外我也看了 kivymd 的作者实现的一套柔和阴影的 方案, 原理大概是用 pillow (pil) 库对组件加了一层纹理 (如图 2). 个人感觉用 pillow 这种重量级库来实现不太好, 特别涉及到组件运动或者变形, 请求的重绘成本的性能开销会非常大.
TODO:SamplesRequired
此外, 无论上述哪种方案, 都与 这篇前端文章 介绍的效果相形见绌.
界面效果不好看
注: 该评价纯主观感受.
圆角矩形难画
(与 qml 相比) kivy 的圆角矩形写法不友好:
TODO
文档难用
略.
输入框光标居中
kivy 的输入框组件没有这个属性, 需要自己来实现 (参考 这篇文章 (TODO)).
ps: 跟 qml 对比, kivy 在组件数量和属性丰富程度上都有较大差距.
💠 Python Web UI
💡 Python Web 组件化
🔥 JustPy (780 标星. 个人评级 ★★★★)
关键词: html 组件化; tailwind-css; 现代化; 异步请求
TODO
依赖项过多
直接依赖项有 11 个, 间接依赖项加起来有 42 个. 体积合计约 39mb (使用 7z 极限压缩可以达到 7mb).
🔥 Streamlit (★★★☆)
主要面向数据科学家
该群体经常使用 notebook, pandas, numpy 等高级数学计算库. 其他领域的开发者虽然也能使用 streamlit 的很多特性, 但适应性会很差 (严格来说这个库不适合被归类到 gui, 更适合看作 web 版的 jupyter notebook).
依赖项过多且体积较大
TODO
🔥 LONA (370 标星. 个人评级 ★★★☆)
关键词: flask 路由风格; html 组件化; 响应式
TODO
🔥 Flexx (★★★)
TODO
🔥 FlaskWebGUI
TODO
💡 Python 操作 DOM API, Python 生成 HTML
🔥 Domonic (75 标星. 个人评级 ★★★★)
没有对 DOM Event 封装
我预想的写法 (无效):
import domonic as dom
button = dom.button('click me', _onclick=lambda: print('clicked!'))
# domonic 会强制把 lambda 转换成 str, 结果生成的 html 变成了下面这样:
# <button οnclick="<function <lambda> at 0x000001D7D002C940>">click me</button>
要想处理点击事件, 我们不得不用 javascript 来完成, 或者用 javascript 发送一个请求到后端 (需要 python 安装 websocket 或者 flask 来处理). 例如官方的 计算器示例 以及 鼠标点击事件处理示例.
ps: 本人在自己的项目中基于后者做了一套简单的事件封装, 供参考: TODO.
HTML 属性名称的写法个人不太喜欢
import domonic as dom
# domonic 写法: 为了避免与 python 关键词撞车, 以及为了写法上的统一, attribute 一律使用
# 下划线开头.
page = dom.html(
dom.link(_rel='stylesheet', _href='/css/some-style.css'),
dom.label(_class='someclass', _for='someinput')
)
# 我喜欢的写法: 使用正常的 html 属性写法. 仅对与 python 关键词撞车的词末尾加个下划线.
page = dom.html(
dom.link(rel='stylesheet', href='/css/some-style.css'),
dom.label(class_='someclass', for_='someinput')
)
🔥 PyWebview (2.7k 标星. 个人评级 ★★★☆)
关键词: 窗口管理; 胶水框架; 接口友好; 原生窗口
如果单独把 pywebview 的原生窗口实现拿出来看, pywebview 提供了很好的窗口管理方案, 并且与 justpy, lona2 等 web gui 无缝衔接 (这也是为什么我称它为 “胶水框架” 的原因):
# pywebview with justpy
import webview
from justpy import WebPage, justpy
webview.create_window('Demo Web App', 'http://127.0.0.1:8000')
webview.start(lambda : justpy(WebPage()))
# pywebview with lona
import webview
from lona import LonaApp, LonaView
app = LonaApp(__file__)
@app.route('/')
class MyView(LonaView):
def handle_request(self, req):
from lona import HTML
page = HTML()
self.show(page)
return page
webview.create_window('Demo Web App', 'http://127.0.0.1:8080')
webview.start(lambda : app.run(port=8080))
TODO:AddScreenshot
对前端技术能力要求较高
略.
窗口动画耗时过高
TODO
💡 Python + WebEngine
🔥 CEFPython (★☆)
TODO
💡 Python “取代” JavaScript / Python WebAseembly
🔥 PyScript (6.4k 标星. 个人评级 ★★★★★)
pyscript 是在 2022 pyconus 大会初露峥嵘的一个项目, 也是 webaseembly, pyodide 等前沿技术的集大成者.
从项目名称来看 java - javascript, python - pyscript. 这个项目的愿景也是非常令人期待的.
numpy 等库加载缓慢
部分人在试用时报告, 按照官方的一些演示进行操作, 但是反应都比较慢 (大概 3 - 4 秒的时间).
🔥 Brython (5.4k 标星. 个人评级 ★★★★)
模块导入的问题
TODO
IDE 语法支持
TODO
官方列出的已知限制
TODO
🔥 Pyodide (7.1k 标星. 个人评级 ★★★☆)
pyodide 是 mozilla 开发团队 (旗下知名产品火狐浏览器) 的一个开源项目.
pyodide 具有以下特性:
- 支持安装几乎所有的 pypi 包
- 支持含 c 扩展写的 python 包 (例如 numpy, pandas, matplotlib 等)
- javascript <=> python 无缝沟通. 错误能完美抛接 (一方报错, 另一方捕获)
- webaseembly 技术作为性能保障 (点此阅读 webaseembly 概念)
体积过于庞大
release 包 (0.19.0 alpha 版) 体积 170mb.
上手难度过高
略.
🔥 Transcrypt (★★★)
Hello World 示例不友好
略.
TODO:MoreReviews
🔥 PyJS (☆)
项目已死
略.
🔥 Skulpt (☆)
仅支持 python 2
略.
💠 Python TUI
💡 以组件化的方式构建 TUI
🔥 Textual (10.5k 标星. 个人评级 ★★★★★)
关键词: reactive; tui; 高级动效; 现代化
更多应用截图: 点击跳转本文附录.
特点:
- 基于 rich 库的 tui 项目 (rich 同样是一个明星项目)
- 将 vue, react, css 等概念引入到 tui 中 (推荐观看: css - 新特性演示)
- 视觉效果惊艳
- 非线性 (文字) 动画
- 极为流畅
- 语法友好
- 异步渲染 (你会在这里更经常地使用到 async/await 协程语法)
注意: 以下评测大约在 1 年前写成, 有些观点可能已经过时.
容器和布局实现存在诸多困难
- 在没有文档的情况下, 理解官方的容器和布局系统非常困难.
- 缺少横向布局 (HorizontalLayout), 可以用其他布局替代但终究不是好方法.
- 缺少分散对齐, 固定间距等排列方式.
- 当布局嵌套时, 无法绘制外框.
- 当布局嵌套时, 子布局设置 margin/padding 会导致间隙遮住背景 (issue#218).
备注: 上述问题部分可以通过较复杂的方法解决.
View 诸多功能缺失
view 继承自 widget, 具有 “容器” 的作用, 可以包含多个子 views/widgets. 但 view 本身的功能缺失导致在实际嵌套组合当中出现了很多难以解决的问题. 列举如下:
- view 的 margin, spacing, size 等属性无效
- view 无法嵌入到 scrollview 当中 (issue#177)
- view 无法新增/删除/插入子组件
view 在on_mount
阶段结束后, 不知道该怎么往里面动态添加/移除组件.
缺少文档
主要关注的两大领域: 术语和概念的阐述 (形而上学) 以及 api 手册 (形而下学) – 目前是没有的. 现阶段唯一的依据就是官方提供的几个示例和一些第三方项目.
但是官方示例的覆盖度非常有限, 一些核心的内容还没有得到解释, 比如布局的详细用法, 事件绑定方式, style & markup 等.
没有 Input 组件
根据 issue#132, 相关工作仍在推进中.
网上有其他人封装的 单行输入组件 (笔者没有测试过); 以及笔者自己封装的单行和多行输入组件: 见 我的 textual 学习笔记.
父事件在未调用的情况下仍 “被动” 触发
注: 该问题并非 bug, 需理解 textual 的事件系统设计.
例如, 子组件在没有显式调用 super().on_click(event)
的情况下, 发现父组件的点击事件仍然被触发.
子组件继承父组件, 并覆写父组件的事件. 在没有显式调用 super().<xxx>
的情况下, 发现父组件的同名方法仍然被触发.
该现象违反了我们对 python 类的用法的第一直觉, 有时候会产生一些难以排查的异常.
🔥 PyTermGUI (950 标星. 个人评级 ★★★★)
pytermgui 最近 (2022-01) 终于发布 1.0 版本了, 还上了 python weekly #532 的推荐列表. 距离上个月的说明文档产生了非常大的变化. 特点如下:
- 零依赖3
- 语法风格友好: 提供高级和低级接口; 支持 1) 类似 pysimplegui 的写法; 2) 类似 guietta 的写法 (一种比 psg 更精炼的纯数据驱动界面模型); 以及 3) yaml 写法 (在众多 gui/tui 中独树一帜, 值得尝试)
- 1.0 的 (演示用例) 界面美观度大幅提升
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5ek7PKZ2-1651769655959)(.assets/python-gui-个人体验及评价/2022-01-20 18.57.45.png)]
注意: 随着 1.0 版本的发布, 作者正在非常积极地修复问题, 到了 5 月份版本已经飞升到 5.0 了; 下面列举的缺点可能已经过时.
界面刷新时组件 “闪烁”
该问题比较早期, 现如今可能已经解决.
组件不完善
- 横向布局: 目前只有一个 Splitter 用来横向布局, 但是会 强制平分 每个子组件宽度
- 输入框: 当文字过长时, 会强制以多行显示 (我想要单行输入组件)
Splitter 在组件过多时会报错
略.
文档完成度过低, 难以开始自己的项目
- 高亮语法 (例如
ptg.Label('[47 @60 bold]hello[/]')
) 在文档中没有说明 - guietta, yaml 等特色语法着墨过多, 但实际用途 (考虑到逻辑控制等控制性的话) 可能没有传统方式那么广, 感觉这部分可以简略一些, 给更重要的内容腾出空间
输入框文字过多时应用异常
当文字过多时, 输入框会强制以多行显示, 当行数超出一定数量 (大概在 7 行左右), 整个界面会变成空白, 无法恢复.
没有处理好 tab
pytermgui 使用上下方向键来切换组件焦点; 而按下 tab 键会出现以下异常:
官方示例存在不少错漏
有些代码根本运行不起来. 例如作者在介绍 __add__
和 __ladd__
魔术方法时, 把前个例子的源代码复制了一下仅凭印象改了改, 一些明显的语法或逻辑错误都没有修正, 直接拷贝这些示例也运行不起来.
文档中这样的细节错误比较多, 赶工痕迹明显, 很多示例第一次运行都运行不通.
部分示例对 Windows 用户不友好
有一个判断用户输入字符的示例, 当输入为 \n
时退出循环, 而 windows 系统的回车键传回的是 \r
.
Windows 须安装 Windows Terminal
🔥 PicoTUI (640 标星. 个人评级 ★★☆)
Windows 不支持 (缺少 termios 标准库)
略.
🔥 Urwid (★★☆)
设计风格不够 “现代化”
示例代码和相关的概念比较难懂, 有一种比较早期的 “理工学” 风格.
Button 组件样式丑陋
略.
🔥 AsciiMatics (★★☆)
跟本文所讨论的 TUI 定位不一致
asciimatics 对标的应该是 rich, 强项在于显示而非交互. 相对于本文要讨论的 tui 它缺少很多必要的元素, 比如组件, 光标的焦点, 事件机制以及反应式更新等.
🔥 Py-CUI (550 标星. 个人评级 ★★☆)
界面表现生硬
略.
🔥 Curses (☆)
略.
🔥 NPyScreen (☆)
略.
个人推荐
我个人正在使用的有 (按使用频率): qml, pysimplegui, textual.
qml 用于大型项目, 要求复杂, 精致的 ui; pysimplegui 用于小工具, 轻量化打包. textual 则是个人的热情驱使, 主要在个人项目中使用, 希望能打造一些非常酷的应用.
此外, 一些个人项目或小工具, 我不使用任何 gui. 一般会使用: 1) 命令行接口 (rich-click, typer, fire); 2) 直接在 pycharm 中运行脚本.
我心目中期待学习的下一个 gui 库是:
(目标: 打包体积小, 支持复杂的 ui 和动画效果, 写法优雅.)
- textual
- pyscript (同时学习前端技术)
- 其他语言 (react, flutter)
下面这个分支路线表可能适合更多的人找准自己的定位:
-
我喜欢成熟的库
-
我不想要学习新一套语法
- pyside6 / pyside2 / pyqt6 / pyqt5
- wxpython
-
我对界面动效要求很高
- qml
-
-
我想走 web app 开发路线
- 我有一定的前端基础 / 我愿意花时间学习前端知识
- 我非常在意应用的打包体积
- pyscript
- brython
- pywebview
- pywebview + domonic
- lona
- 我非常在意应用的打包体积
- 我的 web 基础为零, 短期内也没有学习的意愿. 只想 python 一把梭
- 我非常在意应用的打包体积
- domonic + pyscript
- domonic + brython
- justpy + pywebview
- justpy + pywebview + domonic
- 我可以接受打包体积比预想中稍大一点的情况 (如果能压缩到预期就更好)
- justpy + pywebview
- domonic + pyscript (+ pico css)
- domonic + brython (+ pico css)
- justpy + pywebview + domonic
- 我非常在意应用的打包体积
- 我有一定的前端基础 / 我愿意花时间学习前端知识
-
我以业务为优先, gui 在我看来只是为了方便操作的界面 (能用 > 好用 > 精致)
- 我追求尽可能快速地开发
- 我不太关心怎么布局, 排版, 色彩搭配等, 让 gui 自己来决定
- 我非常在意应用的打包体积
- pysimplegui
- dearpygui
- textual
- remi
- 我不太在意应用的打包体积 (在其他方面让我体验好一点)
- pysimplegui
- pysimplegui on qt
- dearpygui
- 我非常在意应用的打包体积
- 我想要对布局有更强的控制力
- textual
- dearpygui
- pysimplegui
- 我不太关心怎么布局, 排版, 色彩搭配等, 让 gui 自己来决定
- 我愿意适当放弃开发速度 (在其他方面让我体验好一点)
- pysimplegui
- dearpygui
- remi + pywebview
- 我追求尽可能快速地开发
-
我喜欢体验新奇有趣的东西
- 我对 python 非常偏爱, 想要用 python 解决一切问题
- enaml
- textual
- pyscript
- justpy
- qml
- 我是一个视觉控, 我对动效表现, 文字排版等有着极致的要求
- qml
- textual
- 我对 python 非常偏爱, 想要用 python 解决一切问题
-
我是一名数据科学家
- streamlit
- pyscript
- pyodide
值得期待的一些开发中的项目
Declare GUI Project
这是我在业余时间推进的一个 gui 语法封装框架.
python 的 with 语法和它的嵌套型结构天然适合来描述界面的组织方式, 就像 xml, flutter, qml 那样, 通过嵌套来描述布局, 再通过模块拆分和引用来避免 “嵌套地狱”. 利用 python 的魔术方法和类型注解可以得到更魔幻的体验, 同时又能写出易读性很高, 格式优美的代码.
上面的代码示例在小范围内已经得到了一部分验证, 现在的进展是可以通过上述代码生成出对应的 qml 代码. 但事件绑定, 更多语法和组件库支持都远没有开始.
该项目距离 1.0 版还有相当长的路要走. 相关进度可在 declare gui project (欢迎页) 和 declare-qml (当前活跃的仓库) 中跟进.
说明事项
- 文章层级:
- 主标题是大的技术路线和方向, 因为 python gui 的实现方案种类繁多, 但大致可分为比较明确的几个大方向. 你可以根据自己的技术栈侧重来选择合适的大方向阅读.
- 副标题是具体的库. 为了便于快速查看, 副标题同时提供官方链接, github 标星, 个人评级信息.
- 缺点描述:
- 缺点仅为个人观点. 因为本人能力, 认知水平和主观喜好, 观点可能存在较明显偏向.
- 所有评论仅针对截止于本文发布日期 (2021-12 ~ 2022-02) 的观察和体验, 有些 bug 可能已经修复, 但本文会来不及更新有关章节.
- 缺点按照严重性降序.
- 严重程度用颜色标识出来. 具体有:
- 影响严重, 几乎不可解或没有合适的解 (sharp pink)
- 影响较重, 但可以以较差的方式解决 (old orange)
- 影响一般, 有合适的解决方案 (normal black)
- 影响轻微, 或者非常冷门 (gray)
附录
QtWidgets & QML Gallery
(返回正文)
组件演示
矩形缩放:
伪 3D 倾斜 + 视差动画
TODO
密码框动画:
TODO:MoreScreenshots
应用软件截图
feeluown: 一款开源音乐软件.
toou 2d: 基于 qt quick (qml) 跨平台技术打造的 2d 框架.
snipaste: built with qt…
TODO:MoreScreenshots
Textual Apps Gallery
(返回正文)
kaskade: apache kafka 框架的文字图形界面.