第14章 - 综合实战项目
前面十三章我们分模块讲解了 RobotGo 的各项能力。本章把这些能力融会贯通,通过几个由简到繁的完整实战项目,演示如何在真实场景中组织自动化代码。每个项目都给出完整可读的结构与关键实现,帮助你建立“工程化使用 RobotGo”的思路。
14.1 项目一:自动化表单填写
场景:在某个桌面应用或网页表单中,按固定流程填写多个字段并提交。这是 RPA 最经典的应用。
核心思路:定位输入框(用 Tab 切换或图像查找)→ 输入文本(长文本走剪贴板)→ 提交。
package main
import (
"runtime"
"github.com/go-vgo/robotgo"
)
func cmdKey() string {
if runtime.GOOS == "darwin" {
return "cmd"
}
return "ctrl"
}
// 通过剪贴板可靠地输入一个字段,然后用 Tab 跳到下一个
func fillField(value string) {
robotgo.WriteAll(value)
robotgo.MilliSleep(60)
robotgo.KeyTap("v", cmdKey())
robotgo.MilliSleep(60)
robotgo.KeyTap("tab")
}
func main() {
robotgo.MouseSleep = 150
robotgo.KeySleep = 80
// 给用户时间切换到目标表单并点中第一个输入框
robotgo.Sleep(3)
fillField("张三")
fillField("13800000000")
fillField("北京市海淀区中关村大街 1 号")
fillField("这是一段较长的备注信息,用剪贴板粘贴可以避免逐字输入的低效与丢字问题。")
// 提交(假设提交按钮可用回车触发)
robotgo.KeyTap("enter")
}
要点:用剪贴板粘贴而非逐字 Type,配合 Tab 在字段间跳转,避免硬编码每个输入框坐标,使脚本更稳健。
14.2 项目二:图像驱动的自动点击器
场景:等待某个按钮/图标出现在屏幕上,一旦出现就自动点击它。适合“等加载完成再操作”的流程,或简单的挂机点击。
核心思路:循环截图 → 位图查找模板 → 找到则点击 → 直到超时或用户停止。
package main
import (
"fmt"
"github.com/go-vgo/robotgo"
"github.com/vcaesar/bitmap"
)
// 在屏幕中查找模板图并点击,返回是否点击成功
func clickByImage(template string) bool {
bit := bitmap.Open(template)
defer robotgo.FreeBitmap(bit)
fx, fy := bitmap.Find(bit)
if fx < 0 || fy < 0 {
return false
}
robotgo.Move(fx, fy)
robotgo.MilliSleep(100)
robotgo.Click()
return true
}
func main() {
template := "button.png" // 预先截好的按钮模板
fmt.Println("开始监视按钮,最多等待 30 秒...")
elapsed := 0
for elapsed < 30000 {
if clickByImage(template) {
fmt.Println("已点击目标按钮")
return
}
robotgo.MilliSleep(500)
elapsed += 500
}
fmt.Println("超时,未发现目标按钮")
}
要点:用图像查找代替固定坐标,使脚本不受窗口位置变化影响;用轮询 + 超时机制保证健壮,避免无限等待。
14.3 项目三:全局热键启动器
场景:常驻后台的小工具,按下不同全局热键执行不同自动化动作(粘贴模板文本、打开网址、截图等)。综合运用 gohook 与 robotgo。
package main
import (
"fmt"
"runtime"
"github.com/go-vgo/robotgo"
hook "github.com/robotn/gohook"
)
func cmdKey() string {
if runtime.GOOS == "darwin" {
return "cmd"
}
return "ctrl"
}
func pasteText(text string) {
robotgo.WriteAll(text)
robotgo.MilliSleep(50)
robotgo.KeyTap("v", cmdKey())
}
func main() {
fmt.Println("热键启动器已就绪:")
fmt.Println(" ctrl+shift+1 → 粘贴签名")
fmt.Println(" ctrl+shift+2 → 截全屏到 shot.png")
fmt.Println(" ctrl+shift+q → 退出")
// 热键 1:粘贴签名
hook.Register(hook.KeyDown, []string{"1", "ctrl", "shift"}, func(e hook.Event) {
pasteText("此致\n敬礼\n张三")
})
// 热键 2:截图
hook.Register(hook.KeyDown, []string{"2", "ctrl", "shift"}, func(e hook.Event) {
robotgo.SaveCapture("shot.png")
fmt.Println("已保存截图 shot.png")
})
// 热键 Q:退出
hook.Register(hook.KeyDown, []string{"q", "ctrl", "shift"}, func(e hook.Event) {
fmt.Println("退出")
hook.End()
})
s := hook.Start()
<-hook.Process(s)
}
要点:用 Register 注册多个热键;务必提供一个退出热键调用 hook.End();回调内的动作要轻量。
14.4 项目四:定向操作指定应用
场景:找到某个特定应用(如浏览器),激活它,在地址栏输入网址并访问。综合运用进程窗口管理与键盘输入。
package main
import (
"fmt"
"runtime"
"github.com/go-vgo/robotgo"
)
func cmdKey() string {
if runtime.GOOS == "darwin" {
return "cmd"
}
return "ctrl"
}
func openURLInApp(appName, url string) error {
pids, err := robotgo.FindIds(appName)
if err != nil || len(pids) == 0 {
return fmt.Errorf("未找到应用 %s", appName)
}
// 激活窗口并等待其切到前台
robotgo.ActivePid(pids[0])
robotgo.MilliSleep(400)
fmt.Println("当前窗口:", robotgo.GetTitle())
// 定位地址栏(多数浏览器为 Ctrl/Cmd + L),输入并回车
robotgo.KeyTap("l", cmdKey())
robotgo.MilliSleep(100)
robotgo.WriteAll(url)
robotgo.MilliSleep(50)
robotgo.KeyTap("v", cmdKey())
robotgo.KeyTap("enter")
return nil
}
func main() {
if err := openURLInApp("chrome", "https://github.com/go-vgo/robotgo"); err != nil {
fmt.Println("出错:", err)
}
}
要点:先 FindIds 再 ActivePid 激活,激活后留足延时;用 GetTitle 确认窗口;用剪贴板粘贴 URL 比逐字输入更可靠。
14.5 项目五:操作录制与回放(进阶)
场景:记录用户一段时间内的键鼠操作,然后自动回放。综合运用 gohook 的低层事件流与 robotgo 的控制能力。
核心思路:
- 录制:用
hook.Start()消费事件通道,记录每个事件的类型、坐标/按键以及相对时间戳。 - 回放:遍历记录,按相对时间间隔
MilliSleep,用 robotgo 重放对应的鼠标移动/点击或按键。
package main
import (
"fmt"
"time"
"github.com/go-vgo/robotgo"
hook "github.com/robotn/gohook"
)
type record struct {
delay time.Duration
kind uint8
x, y int16
}
func recordEvents(seconds int) []record {
var records []record
last := time.Now()
evChan := hook.Start()
go func() {
time.Sleep(time.Duration(seconds) * time.Second)
hook.End()
}()
for ev := range evChan {
now := time.Now()
if ev.Kind == hook.MouseMove || ev.Kind == hook.MouseDown {
records = append(records, record{
delay: now.Sub(last),
kind: ev.Kind,
x: ev.X,
y: ev.Y,
})
last = now
}
}
return records
}
func replay(records []record) {
for _, r := range records {
time.Sleep(r.delay)
robotgo.Move(int(r.x), int(r.y))
if r.kind == hook.MouseDown {
robotgo.Click()
}
}
}
func main() {
fmt.Println("开始录制鼠标操作,持续 5 秒...")
recs := recordEvents(5)
fmt.Printf("录制完成,共 %d 个事件。3 秒后回放...\n", len(recs))
robotgo.Sleep(3)
replay(recs)
fmt.Println("回放结束")
}
要点:录制时同时保存相对时间间隔,回放才自然;注意字段名以你所用 gohook 版本为准;回放鼠标移动时用 Move,需要更自然可换 MoveSmooth。这是一个简化示例,完整的录制回放还应处理键盘事件、鼠标松开、滚轮等。
14.6 工程化建议
把上面这些项目落地为可维护的工具时,建议:
- 封装动作库:把“点击图像”“粘贴文本”“激活应用”等常用动作封装成可复用函数,业务脚本只调用这些高层函数。
- 配置化:把坐标、模板图路径、热键、文本内容等放到配置文件,避免硬编码、便于修改。
- 统一节奏:在程序入口集中设置
MouseSleep/KeySleep,并提供“调试模式”(更大延时、打印日志)。 - 健壮性:所有“查找”操作都要处理“找不到”的情况;所有等待都要有超时;保证热键工具能优雅退出。
- 跨平台:用
runtime.GOOS抽象修饰键等差异,必要时用构建约束分离平台代码。
14.7 小结
本章通过五个完整实战项目——自动化表单填写、图像驱动点击器、全局热键启动器、定向操作指定应用、操作录制与回放——把全书的鼠标、键盘、剪贴板、屏幕、图像查找、进程窗口、事件监听等能力综合运用起来,并给出了工程化的组织建议。下一章作为收尾,我们将系统总结最佳实践、性能优化与常见问题排查。