Movwin:我的(未发布)TUI 框架

Hacker News Top 工具

摘要

一篇博文,详细介绍了作者个人未发布的、基于 ncurses 的 Python TUI 框架 'movwin',重点突出了其对 Unicode 的支持和性能表现,并因担心 AI 公司爬取代码而决定将其保密。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/06/01 13:42

# movwin:我的(未发布)TUI框架 来源:https://movq.de/blog/postings/2026-05-29/0/POSTING-en.html 博客 (https://movq.de/blog/)\-git (https://movq.de/git/)\-桌面 (https://movq.de/desktop/)\-联系 (https://movq.de/contact.html) --- 2026-05-29 很长一段时间以来,编写带某种 GUI(或 TUI)的程序对我来说一直不太满意。库来了又走,趋势来了又走。你不得不不断跟随上游的新决策并调整代码。有时你完全不认同上游的决定,然后就得寻找新的框架。这有点令人疲惫。我的项目经常维持 5 年、10 年甚至更久,而在这段时间里**会有很多**变化发生。 于是,在 2025 年 12 月底的上一届 Advent of Code 之后,我决定开始自己做一个 TUI 框架。这不是一个容易的决定,因为我知道这将耗费大量工作。我寻找过替代方案,但找不到任何我喜欢的——或者足够快的。最近性能似乎真的变差了,有些框架光是初始化就需要两秒钟。 这篇博文只是对 movwin 当前状态的一次简单巡览,因为我决定暂时不发布这些代码。目前情况不太好:我发布的任何东西都会被"AI"公司抓取,然后他们拿去卖,完全无视代码附带的许可协议。我对此无法接受。 ## 基础 它是一个 Python 库。我不再是 Python 的最大粉丝,但它确实有它的优势,主要是**庞大**的标准库,让我能轻松做很多事情。 movwin——代表"movq's windows and widgets"——构建在 ncurses 之上。ncurses 在终端兼容性方面承担了繁重的工作。movwin 没有使用的是 ncurses 的子窗口或 pads。相反,ncurses 充当一种(智能的)帧缓冲,我可以向其绘制内容,同时它也是键盘和鼠标输入的来源。 一个主要目标是"可接受"的 Unicode 支持。movwin 不太可能支持从右到左之类的功能,但我**绝对不**想在某处放一个 emoji 就导致整个布局爆炸。换句话说,movwin 必须知道一个 Unicode 序列在终端中可能占用多少个单元格。这里的大问题是这取决于终端,所以永远无法完美。 只有一个依赖(除了 Python 3.14 本身):wcwidth (https://pypi.org/project/wcwidth/) 及其 `wcswidth()` 函数。这用于测量文本的"外观尺寸":例如,"♀️" 占两个单元格宽度。 从很早开始,movwin 就有了"Window"和"Window Manager"的概念。我在制作整个东西时脑海中浮现的是古老的 DOS TUI: tc.png (https://movq.de/blog/postings/2026-05-29/0/tc.png) pcdoshelp.png (https://movq.de/blog/postings/2026-05-29/0/pcdoshelp.png) pcdosshell.png (https://movq.de/blog/postings/2026-05-29/0/pcdosshell.png) 要是能真正重现这种体验就好了,在某种程度上我也做到了,但 UNIX 终端中的鼠标支持并不太好。在我的测试中,没有终端默认报告鼠标**移动**事件(只有"按下/释放"),我不得不调整 terminfo 文件。更糟糕的是,在大的终端窗口中,有些鼠标事件根本不会触发。因此,movwin 中的鼠标支持目前相当有限。也许我甚至会完全移除它,因为,嗯,用键盘驱动其实也是好事。不过对于移动窗口或控制滚动区域之类的事情,鼠标支持还是很不错的。 另一个主要目标是"可接受"的性能,意思是:"在我那台 10 年旧的、装有 Celeron CPU 的小 Intel NUC 上,启动时间大约 200-300 毫秒是可以接受的,但不应超过这个数。" Python 的启动时间确实是个问题,因为 `import` 超级慢。我不得不在一些地方做出牺牲,比如不使用 `dataclasses`。在这台 NUC 上,光是 import 的时间就足以致命: ``` $ time python -c 'exit(0)' real 0m0.061s user 0m0.048s sys 0m0.011s $ time python -c 'from dataclasses import dataclass' real 0m0.151s user 0m0.115s sys 0m0.027s ``` ## 应用程序 这一切的起因是一个叫做 `tracktivity` 的小程序:我用它来追踪活动和事件,比如咖啡因摄入或天气事件。它操作 CSV 文件: ``` rfc3339datetime,Food[coffee;blacktea;greentea],Comment 2026-05-29T14:04:00+00:00,coffee,at home 2026-05-29T14:04:43+00:00,blacktea,just some dummy entry :-) 2026-05-29T14:04:51+00:00,blacktea,more tea ``` `Food` 列被配置为有几个选项可以选,`Comment` 列是自由文本。`tracktivity` 基于该文件构建一个 UI 表单,看起来像这样: tracktivity.png (https://movq.de/blog/postings/2026-05-29/0/tracktivity.png) 它没什么花哨,而且一些控件类型仍然缺失,例如还没有合适的表格或列表控件。 `tracktivity` 是一个简单的单窗口全屏程序,但它仍然使用 `Window` 类(像所有 movwin 程序应该做的那样),所以如果我愿意,我可以调整大小并移动这个窗口(点击观看视频): tracktivity-moveresize.mp4 (https://movq.de/blog/postings/2026-05-29/0/tracktivity-moveresize.mp4) "弹出窗口"也被实现为新的窗口。movwin 自带一些内置弹出窗口,比如"是/否框"、"输入框"或"消息框"。 `bine` 是另一个使用 movwin 的程序:它是一个基础的十六进制编辑器。我真正想要的是一个**简单**的十六进制编辑器,性能良好,并且底部有一个小信息面板(点击观看视频): bine.mp4 (https://movq.de/blog/postings/2026-05-29/0/bine.mp4) 比如,"光标下的字节,当解释为有符号 8 位整数时,其值是多少"或同样的事情对于 2、4、8 字节,有符号和无符号。或者尝试将字节(从光标位置开始)解释为 UTF-8。 这是我滚动菜单以显示更多可用功能(点击观看视频): bine-menus.mp4 (https://movq.de/blog/postings/2026-05-29/0/bine-menus.mp4) 还有两个演示,第一个展示"编辑二进制"功能,它使用了一个带有应用定义控件的自定义弹出窗口,第二个视频展示了撤销/重做(点击观看视频): bine-binary.mp4 (https://movq.de/blog/postings/2026-05-29/0/bine-binary.mp4) bine-undoredo.mp4 (https://movq.de/blog/postings/2026-05-29/0/bine-undoredo.mp4) `bine` 大量使用 `mmap()`,用 Python 编写并不影响性能。正如你在第一个视频中看到的,在一个 2GB 的文件中搜索 ASCII 字符串也许只用了一秒。对我来说这已经足够好了,而且我没有花任何时间试图优化它。Python 的 `bytes.find()` (https://docs.python.org/3/library/stdtypes.html#bytes.find) 操作在 mmap 对象上,它完成了所有繁重的工作。 `bine` 也能很好地利用窗口系统:你可以在一个新窗口中打开另一个文件,而第一个窗口不受影响。当打开多个文件时,`WindowManager` 类会应用平铺布局(或者你可以切换到"将焦点窗口置于全屏模式"——或者如果你愿意,也可以自由移动它们): bine-multiwin.png (https://movq.de/blog/postings/2026-05-29/0/bine-multiwin.png) bine-multiwin-chaos.png (https://movq.de/blog/postings/2026-05-29/0/bine-multiwin-chaos.png) 最后,我最近制作了一个非常简单的计时程序: watwar2.png (https://movq.de/blog/postings/2026-05-29/0/watwar2.png) 它有一个开始/停止按钮,并显示一个简单的"记录"列表:在那个截图中,我从 4:59 开始工作到 10:08,然后休息,之后在 12:21 又开始工作。到我截图时,总计为 9 小时 28 分钟。 截图中看不到的是,这个工具还会将 9:28 显示在连接到 Arduino 的一个小七段数码管上: arduino.webp (https://movq.de/blog/postings/2026-05-29/0/arduino.webp) 我在工作中用这个东西来注意我已经工作了几个小时。:-) 当一天结束时,我可以将这些数据上传到我们基于云的时间追踪服务(这就是"传输"菜单的作用)。 顺便提一下:在 Linux 上,可以使用 timerfd (https://docs.python.org/3/library/os.html#os.timerfd_create) 来实现定时器事件。movwin 的主循环基于 `select()`,你可以注册任意文件描述符。timerfd 以固定间隔触发,所以这允许 UI 和 Arduino 显示定期更新。 ## 更多好东西 ### 主题 movwin 自带两个内置主题: bine-bluegray.png (https://movq.de/blog/postings/2026-05-29/0/bine-bluegray.png) bine-amber.png (https://movq.de/blog/postings/2026-05-29/0/bine-amber.png) "琥珀色"主题让我充满怀旧感,因为它让我想起我第一台琥珀色 CRT 显示器上的 DOS 程序。 如果你愿意,可以把颜色定义放在 `~/.config/movwin/colors.json` 中,然后创建自己的主题,但这显然会有些棘手,因为应用程序可能也定义了自己的颜色(你**可以**在该文件中覆盖它们的颜色)。 默认情况下,movwin 根据月份选择当前主题。如果你在北半球,在十二月左右的月份会看到"琥珀色"主题,其他月份是更亮的"蓝灰色"主题。自动选择可以切换到"南半球",或者你也可以直接设置 `$MOVWIN_THEME` 来选择某个主题。 我最近在别处写道: > 在过去的几周里,我越来越欣赏**菜单**的概念。它们提供了功能易于发现的能力。无需阅读手册。它们按类别组织。例如,我在查找程序的全局设置时不会去打开"格式"菜单。由于"加速键",它们可以用键盘驱动。而且当一个菜单项有直接快捷键(如 `Ctrl+U`)时,它会直接在菜单中显示出来。(当然,并非所有菜单系统都实现了所有这些。)我们曾经认为菜单是理所当然的,因为那是 GUI 程序的常规操作。但感觉它们在某处丢失了……尤其是在(UNIX)终端程序和网站上。还有智能手机上。 我特别喜欢 movwin 中热键的工作方式: ```python menu_tree = MenuRoot( [ MenuSub( '&File', children=[ MenuItem( '&Quit', cb_quit, hotkey='KEY_F3', ), ], ), MenuSub( '&Edit', children=[ MenuItem( 'Edit &raw file', cb_edit_raw, hotkey='KEY_F5', arg=state, ), ], ), ... ], ) ``` 所以当你定义菜单结构时,你可以直接在那里指定菜单项的热键。我喜欢这一点,因为它是自文档的。不需要专门的 F3 处理函数,也不需要将 F3 放到帮助页面中,因为菜单已经告诉你这个键是干什么的。(我可以在 `bine` 中更多地利用这一点。) 至于加速键:在上面的例子中,`Alt+f, q` 会调用 `cb_quit()`,`Alt+e, r` 会调用 `cb_edit_raw()`。 菜单可以任意嵌套,尽管我还没有实际需要用到这一点: menu-nesting.png (https://movq.de/blog/postings/2026-05-29/0/menu-nesting.png) 不过菜单缺少鼠标支持。目前,我懒得实现它。而且正如我说的,也许我无论如何都会移除鼠标支持。 而且菜单不会在下面的项目上投下阴影。同样,我现在也懒得做。:-) ### 编辑框和 Unicode 编辑框对 Unicode 的理解足够到位,使得水平滚动不会出现故障(点击观看视频): edit-unicode.mp4 (https://movq.de/blog/postings/2026-05-29/0/edit-unicode.mp4) 当两只单元格宽的企鹅从屏幕左侧离开时,滚动并不完美,但这里主要的是控件不会将企鹅绘制到分配的区域之外。 (这种裁剪适用于所有文本,不仅仅是编辑框。不过编辑框有点特殊,因为它们实现了自己的水平滚动。) ## 接下来往哪里去? 首先,我对目前的状态非常满意。代码中还有一些待办事项,但它已经相当可用了。拥有一个能做我所需所想、并且不会在下一个版本中意外改变的框架,感觉真好。ncurses 超级稳定,它不会做奇怪的事情。wcwidth 如果必要的话我可以 fork。Python 本身也相对稳定,而且我**认为**他们从艰难的 2 到 3 过渡中学到了教训,短期内不会再这样做?不确定。不管怎样,movwin 感觉像是一个在 5 年、10 年、15 年后仍然能工作——而不会太折腾的东西。 还有一些"更大"的事情我想实现,比如合适的列表视图,也许还有树形视图,以及最重要的文件选择对话框。 然后……我们拭目以待。

相似文章

@jakevin7: 发表个暴论,TUI 会逐渐式微甚至被淘汰。 我已经很久没有用 claude code 了。基本都是用 slock。 对于临时任务,现在用的更多的是 codex desktop,偶尔用 claude desktop。 让我开始重新思考 TU…

X AI KOLs Following

文章讨论了TUI(终端用户界面)在AI编程工具中逐渐式微的趋势,作者认为随着模型能力增强,TUI将被CLI+server架构或Web UI取代,并分享了从Claude Code转向Slock、Codex Desktop等工具的个人体验。

@axiaisacat: 字节跳动开源了一个能直接操控你电脑的 AI 叫 UI-TARS,开源免费,本地运行 你用说话的方式告诉它: 「帮我在 Priceline 订9月1日最早的旧金山到纽约的机票」 「帮我把 VS Code 的自动保存延迟设置成500毫秒」 「…

X AI KOLs Timeline

ByteDance has open-sourced UI-TARS, an AI model capable of directly controlling computer interfaces via mouse and keyboard for tasks like booking flights or configuring software. Available in 2B, 7B, and 72B parameter sizes, it runs locally and offers a free alternative to paid services like Anthropic's Computer Use.

Open WebUI 的更简单的自托管替代方案

Reddit r/LocalLLaMA

OvertChat 是 Open WebUI 的一个更简单、精致的自托管替代方案,适用于本地 AI 模型,具有单一的 Docker Compose 设置、内置网页搜索和 Kokoro TTS,全部采用 MIT 许可证。