很多 GUI 小工具能跑起来以后,作者就觉得差不多完成了。但从“能跑”到“好用”,中间还差一段路。尤其是带编辑框、文件保存、配置项的小工具,如果没有菜单、保存状态、默认配置和修改提示,用户很快就会迷路。

Jack 有一组文章专门讲 GUI 脚本的收尾工作。我觉得最值得留下的观点是:菜单栏不是装饰,它会逼你把脚本里的动作拆清楚。一个 GUI 工具有没有真正成形,看它有没有清晰的“打开、保存、另存为、设置、退出”这些入口,基本就能看出来。

先给 Gui 加菜单栏

菜单栏最直接的好处是节省窗口空间。按钮太多时,不一定都要摆在界面上。常用操作留按钮,不常用但重要的操作放菜单。

#Requires AutoHotkey v1.1

Menu, FileMenu, Add, 打开(&O), MenuOpen
Menu, FileMenu, Add, 保存(&S)`tCtrl+S, MenuSave
Menu, FileMenu, Add, 另存为(&A), MenuSaveAs
Menu, FileMenu, Add
Menu, FileMenu, Add, 退出(&X), GuiClose

Menu, HelpMenu, Add, 关于, MenuAbout

Menu, MyMenuBar, Add, 文件(&F), :FileMenu
Menu, MyMenuBar, Add, 帮助(&H), :HelpMenu

Gui, Menu, MyMenuBar
Gui, Add, Edit, vMainText w500 h260
Gui, Show,, 未命名
return

MenuOpen:
MsgBox, 这里写打开文件逻辑
return

MenuSave:
MsgBox, 这里写保存逻辑
return

MenuSaveAs:
MsgBox, 这里写另存为逻辑
return

MenuAbout:
MsgBox, 一个简单的 GUI 小工具
return

GuiClose:
ExitApp

菜单一加上去,你会自然开始思考:哪些动作应该独立成子程序?哪些动作可以复用?哪些按钮其实没必要一直占着界面?这就是菜单栏真正的价值。

保存和另存为要分清

很多小工具只做一个“保存”按钮,第一次保存和后续保存都走同一段逻辑。脚本小的时候没问题,后面就会乱。

我一般这样拆:

  • 保存:如果已经有文件路径,直接覆盖写入。
  • 另存为:总是弹出 FileSelectFile,让用户选择新路径。
  • 第一次保存:因为还没有路径,所以自动走另存为。
CurrentFile := ""

^s::
gosub, MenuSave
return

MenuSave:
if (CurrentFile = "")
{
    gosub, MenuSaveAs
    return
}
GuiControlGet, text,, MainText
FileDelete, %CurrentFile%
FileAppend, %text%, %CurrentFile%, UTF-8
MarkSaved()
return

MenuSaveAs:
FileSelectFile, SaveFile, S16, %A_ScriptDir%\未命名.txt, 保存文件, 文本文件 (*.txt)
if ErrorLevel
    return
CurrentFile := SaveFile
gosub, MenuSave
return

这样拆开以后,后面要加快捷键、菜单项、按钮,都可以复用同一套保存逻辑。

修改后给用户提示

成熟一点的编辑工具,内容改了以后标题栏会出现一个星号,提醒用户还没保存。AHK 里也可以做一个简单版本:Edit 控件一变化,就把状态标记为已修改。

Dirty := false
WindowTitle := "未命名"

Gui, Add, Edit, vMainText gTextChanged w500 h260
Gui, Show,, %WindowTitle%
return

TextChanged:
if (!Dirty)
{
    Dirty := true
    Gui, Show,, %WindowTitle% *
}
return

MarkSaved()
{
    global Dirty, WindowTitle
    Dirty := false
    Gui, Show,, %WindowTitle%
}

这个细节很小,但用户体验差很多。尤其是你的 GUI 里有大段文本、配置、列表时,用户需要知道“我改过东西,但还没保存”。

关闭前确认保存

如果内容已经修改,关闭窗口时最好提醒一下。否则用户误关一次,就可能再也不信这个工具了。

GuiClose:
if (Dirty)
{
    MsgBox, 35, 保存确认, 内容已经修改,要先保存吗?
    IfMsgBox, Yes
        gosub, MenuSave
    IfMsgBox, Cancel
        return
}
ExitApp

MsgBox, 35 是“是/否/取消”加问号图标。这里的逻辑是:选“是”就保存,选“否”直接退出,选“取消”留在程序里。

默认配置放哪里

小工具经常需要记住上次打开的文件、窗口位置、用户选项。最常见的做法是 INI 文件,也可以写注册表。我的建议是:普通脚本优先 INI,便于备份和迁移;确实想隐藏一点或跟随当前用户环境,再考虑注册表。

IniFile := A_ScriptDir "\settings.ini"

IniRead, LastFile, %IniFile%, Main, LastFile,
if (LastFile != "")
    CurrentFile := LastFile

; 保存设置
IniWrite, %CurrentFile%, %IniFile%, Main, LastFile

如果使用注册表,建议只写 HKCU\Software\你的工具名 下面,避免碰系统已有键值。

RegWrite, REG_SZ, HKCU\Software\MyAhkTool, LastFile, %CurrentFile%
RegRead, LastFile, HKCU\Software\MyAhkTool, LastFile

注册表不是不能用,但要克制。能用 INI 解决的配置,就别为了“看起来专业”去写注册表。

收尾清单

我写 GUI 小工具时,最后会过一遍这个清单:

  • 常用操作是否在界面上,不常用操作是否可以放菜单。
  • 打开、保存、另存为是否拆成独立逻辑。
  • 内容修改后是否有提示。
  • 关闭前是否确认未保存内容。
  • 上次路径、窗口设置、用户选项是否能保存。
  • 错误提示是否让用户知道下一步该怎么办。

GUI 的难点不是把控件摆出来,而是让用户放心地操作。一个小工具只要做到能打开、能保存、能恢复、能提醒,哪怕界面朴素,也会比一堆按钮挤在一起更像一个真正能长期使用的工具。

声明:站内资源为整理优化好的代码上传分享与学习研究,如果是开源代码基本都会标明出处,方便大家扩展学习路径。请不要恶意搬运,破坏站长辛苦整理维护的劳动成果。本站为爱好者分享站点,所有内容不作为商业行为。如若本站上传内容侵犯了原著者的合法权益,请联系我们进行删除下架。