OCR 操作示例

返回:文档索引 / README

本文演示四个核心屏幕识别/点击函数的用法。 每个示例都是可直接放入 src/tasks/onetime/ 并注册运行的完整任务文件, 风格与 src/tasks/test/ 下的调试任务保持一致。


函数速览

函数 何时用
ocr(match, box) 立即扫描一次,不等待;适合循环轮询或判断当前画面
wait_ocr(match, box, time_out) 持续扫描直到出现匹配或超时;返回 List[Box],超时返回 None
click(target, after_sleep) 点击坐标或 Box 对象
wait_click_ocr(match, box, time_out) wait_ocr + click 的一步组合

Box.name 属性保存 OCR 识别到的文字内容,可用于二次判断。


示例一:用 wait_click_ocr 点击简单按钮

场景:打开信用交易所,等待"立即刷新"按钮出现后点击。

# src/tasks/onetime/ExampleClickOcr.py
import re

from src.core.BaseEfTask import BaseEfTask


class ExampleClickOcr(BaseEfTask):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.name = "示例:wait_click_ocr"
        self.description = "等待指定文字出现后自动点击"

    def run(self):
        # 等待顶部区域出现"信用交易所"并点击,最多等 5 秒
        self.wait_click_ocr(
            match=re.compile("信用交易所"),
            box=self.box.top,
            time_out=5,
        )

        # 等待左下角出现"收取信用"或"无待领取信用"其中之一并点击
        # recheck_time=1:> 0 即启用,等待 recheck_time 秒后重新定位文字坐标再点击,降低因元素位移导致点偏的概率
        result = self.wait_click_ocr(
            match=[re.compile("收取信用"), re.compile("无待领取信用")],
            box=self.box.bottom_left,
            time_out=7,
            recheck_time=1,
        )

        if not result:
            self.log_info("未找到收取信用或无待领取信用")
            return

        # result 是 List[Box],Box.name 是识别到的文字
        if "收取信用" in result[0].name:
            self.log_info("已点击收取信用,等待弹窗关闭")
            self.wait_pop_up()
        else:
            self.log_info("本次无待领取信用,跳过")

示例二:用 wait_ocr + 手动 click 拆分识别与点击

场景:需要在点击前先记录识别结果或做额外判断时,手动拆分两步。

# src/tasks/onetime/ExampleWaitOcrClick.py
import re

from src.core.BaseEfTask import BaseEfTask


class ExampleWaitOcrClick(BaseEfTask):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.name = "示例:wait_ocr + click"
        self.description = "先等待识别,再手动点击"

    def run(self):
        # 等待右侧出现"好友"按钮(最多 7 秒)
        result = self.wait_ocr(
            match=re.compile("好友"),
            box=self.box.right,
            time_out=7,
        )
        if not result:
            self.log_info("超时未找到好友按钮,任务结束")
            return

        # result 是 List[Box],取第一个匹配项点击
        # after_sleep=1:点击后等待 1 秒,让页面完成跳转
        self.click(result[0], after_sleep=1)
        self.log_info(f"已点击:{result[0].name}")

        # 等待确认弹窗出现并点击,点击后将鼠标移回原位
        confirm = self.wait_ocr(match="确认", box=self.box.bottom, time_out=5)
        if confirm:
            self.click(confirm[0], after_sleep=0.5)

示例三:用 ocr 循环轮询(不阻塞等待)

场景:需要在某个文字消失之前持续等待,wait_ocr 等待的是"出现", 而等待"消失"只能用 ocr 自行轮询。

# src/tasks/onetime/ExampleOcrPolling.py
import re

from src.core.BaseEfTask import BaseEfTask


class ExampleOcrPolling(BaseEfTask):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.name = "示例:ocr 轮询"
        self.description = "等待某文字消失,或用 ocr 读取当前状态"

    def run(self):
        # 等待"舰桥"提示文字从左侧区域消失(最多 60 秒)
        for _ in range(120):
            if not self.ocr(match="舰桥", box=self.box.left):
                self.log_info("舰桥提示已消失,继续执行")
                break
            self.next_frame()
            self.sleep(0.5)
        else:
            self.log_info("等待舰桥消失超时,任务中断")
            return

        # 单次扫描右上角,判断当前有没有武器补给提示
        if self.ocr(match=re.compile("武器补给"), box=self.box.top_right):
            self.log_info("检测到武器补给,跳过本次任务")
            return

        self.log_info("画面正常,继续后续操作")

示例四:wait_ocr 读取屏幕上的数字

场景:等待指定区域出现数字文本并解析为整数(例如读取剩余票数)。

# src/tasks/onetime/ExampleOcrReadNumber.py
import re

from src.core.BaseEfTask import BaseEfTask


class ExampleOcrReadNumber(BaseEfTask):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.name = "示例:wait_ocr 读数字"
        self.description = "识别屏幕上的数字并解析为整数"

    def run(self):
        # box_of_screen(x1, y1, x2, y2) 的参数是 0.0~1.0 的相对比例
        ticket_box = self.box_of_screen(
            1224 / 1920, 235 / 1080,
            1551 / 1920, 356 / 1080,
        )

        num_str = self.wait_ocr(
            match=re.compile(r"\d+"),
            box=ticket_box,
            time_out=5,
        )

        if not num_str:
            self.log_info("未识别到数字")
            return

        try:
            count = int(num_str[0].name)
        except ValueError:
            self.log_info(f"识别内容无法转为数字:{num_str[0].name}")
            return

        self.log_info(f"当前剩余票数:{count}")

示例五:alt=True — 大世界交互点击

为什么需要 alt=True

游戏大世界场景中,交互键默认为 F,但直接发送 F 键不可靠: 角色可能正在移动,或场景内可交互道具过多,导致 F 键命中错误的目标。

更可靠的做法是先按住 Alt 键,再用鼠标点击目标。 按住 Alt 后游戏会锁定鼠标焦点到对应按钮,此时鼠标点击的优先级高于 场景内的通用交互,不会被其他道具或移动状态干扰。

alt=True 让框架在发送点击前自动按下 Alt、点击后再松开, 一步完成"Alt + 鼠标点击"的组合操作。

经验法则:大世界内与场景物件/NPC 交互时使用 alt=True; 普通菜单 UI 按钮通常不需要。

# src/tasks/onetime/ExampleAltClick.py
import re

from src.core.BaseEfTask import BaseEfTask


class ExampleAltClick(BaseEfTask):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.name = "示例:alt=True 点击"
        self.description = "演示大世界场景中需要按住 Alt 才能可靠触发的交互"

    def run(self):
        # 大世界场景:等待屏幕右下角出现"放弃"交互提示并点击
        # 直接发 F 键可能因角色移动或道具干扰而命中错误目标
        # alt=True 让框架先按住 Alt 再点击,锁定到正确的交互目标
        if self.wait_ocr(match=re.compile("放弃"), box=self.box.bottom_right, time_out=5):
            self.log_info("发现放弃交互,执行 Alt+点击")
            self.wait_click_ocr(
                match=re.compile("放弃"),
                box=self.box.bottom_right,
                time_out=5,
                recheck_time=1,
                alt=True,
            )
            self.wait_click_ocr(
                match=re.compile("确认"),
                box=self.box.bottom_right,
                time_out=5,
            )

        # 等待"激发"交互出现,同样使用 alt=True 确保在大世界内可靠点击
        self.sleep(1)
        if not self.wait_click_ocr(
            match=re.compile("激发"),
            box=self.box.bottom_right,
            time_out=5,
            recheck_time=1,
            alt=True,
        ):
            self.log_info("没有找到『激发』交互,任务结束")
            return

        self.log_info("激发成功,进入战斗")

示例六:recheck_time — 等待后重新定位再点击

为什么需要 recheck_time

wait_ocr 找到目标文字后会立即尝试点击,但部分元素在刚出现时仍在移动 (例如弹出动画、滚动列表);或文字刚好出现在过渡画面里,1~2 帧后就消失。 此时直接点击可能命中偏移后的位置,或点到已消失的元素而静默失败。

recheck_time > 0 会让 wait_click_ocr 在找到结果后等待 recheck_time (等待时长即传入的数值), 然后再做一次 ocr 重新获取文字坐标。若仍能找到同一文字,以新坐标执行点击; 若期间元素已消失,则本次点击跳过,避免误操作。 注意:这只是降低偏移的概率,并不能保证等待期间动画一定已完成。

经验法则:元素在出现后位置仍可能偏移,或需要规避短暂出现后消失的过渡元素时, 加 recheck_time=1;画面静止、按钮始终可见时不需要。

# src/tasks/onetime/ExampleRecheckTime.py
import re

from src.core.BaseEfTask import BaseEfTask


class ExampleRecheckTime(BaseEfTask):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.name = "示例:recheck_time"
        self.description = "演示点击前等待画面稳定的用法"

    def run(self):
        # 点击"领取奖励"按钮,该按钮有淡入动画,刚出现时位置可能仍在偏移
        # recheck_time=1:> 0 即启用,等待 recheck_time 秒后重新定位文字坐标再点击,降低点偏的概率
        result = self.wait_click_ocr(
            match=re.compile("领取奖励"),
            box=self.box.bottom_right,
            time_out=10,
            recheck_time=1,
        )

        if not result:
            self.log_info("未找到领取奖励按钮,任务结束")
            return

        self.log_info("已点击领取奖励")

        # 等待确认弹窗出现并点击,同样加 recheck_time 等待弹窗坐标稳定后再点击
        self.wait_click_ocr(
            match=re.compile("确认"),
            box=self.box.bottom,
            time_out=5,
            recheck_time=1,
        )

注册与运行

将上述任意示例文件保存到 src/tasks/onetime/ 后,在 src/config.py 中注册:

config["onetime_tasks"].append(
    ["src.tasks.onetime.ExampleClickOcr", "ExampleClickOcr"]
)

重启程序(python main_debug.py),在 GUI 任务列表中即可找到并运行。


相关文档

文档 说明
图像模板匹配示例.md 用模板匹配识别图标:find_feature 完整示例任务
在 GitHub 查看源文件 ↗ · 页面生成时间: 2026年8月10日