znlgis 博客

GIS开发与技术分享 — GDAL · GeoServer · PostGIS · QGIS · OpenLayers · Cesium · FreeCAD · NPOI

第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)
    }
}

要点:先 FindIdsActivePid 激活,激活后留足延时;用 GetTitle 确认窗口;用剪贴板粘贴 URL 比逐字输入更可靠。

14.5 项目五:操作录制与回放(进阶)

场景:记录用户一段时间内的键鼠操作,然后自动回放。综合运用 gohook 的低层事件流与 robotgo 的控制能力。

核心思路

  1. 录制:用 hook.Start() 消费事件通道,记录每个事件的类型、坐标/按键以及相对时间戳。
  2. 回放:遍历记录,按相对时间间隔 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 工程化建议

把上面这些项目落地为可维护的工具时,建议:

  1. 封装动作库:把“点击图像”“粘贴文本”“激活应用”等常用动作封装成可复用函数,业务脚本只调用这些高层函数。
  2. 配置化:把坐标、模板图路径、热键、文本内容等放到配置文件,避免硬编码、便于修改。
  3. 统一节奏:在程序入口集中设置 MouseSleep / KeySleep,并提供“调试模式”(更大延时、打印日志)。
  4. 健壮性:所有“查找”操作都要处理“找不到”的情况;所有等待都要有超时;保证热键工具能优雅退出。
  5. 跨平台:用 runtime.GOOS 抽象修饰键等差异,必要时用构建约束分离平台代码。

14.7 小结

本章通过五个完整实战项目——自动化表单填写、图像驱动点击器、全局热键启动器、定向操作指定应用、操作录制与回放——把全书的鼠标、键盘、剪贴板、屏幕、图像查找、进程窗口、事件监听等能力综合运用起来,并给出了工程化的组织建议。下一章作为收尾,我们将系统总结最佳实践、性能优化与常见问题排查。