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日