init
This commit is contained in:
87
core/actions.py
Normal file
87
core/actions.py
Normal file
@@ -0,0 +1,87 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
import pyautogui
|
||||
|
||||
from core.ocr_client import UmiClient
|
||||
from utils.screenshot import capture_screen
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MatchResult:
|
||||
# 屏幕坐标(可直接点击)
|
||||
x: int
|
||||
y: int
|
||||
# 命中的文字(当前实现为目标文字本身;若后续做模糊匹配可返回实际匹配串)
|
||||
text: str
|
||||
|
||||
|
||||
class ActionRunner:
|
||||
def __init__(self, ocr: UmiClient, *, prefer_mss: bool = True, default_region: Optional[tuple[int, int, int, int]] = None):
|
||||
self.ocr = ocr
|
||||
self.prefer_mss = prefer_mss
|
||||
self.default_region = default_region
|
||||
|
||||
def locate_text(
|
||||
self,
|
||||
text: str,
|
||||
*,
|
||||
region: Optional[tuple[int, int, int, int]] = None,
|
||||
exact: bool = True,
|
||||
case_sensitive: bool = False,
|
||||
) -> Optional[MatchResult]:
|
||||
cap = capture_screen(region or self.default_region, prefer_mss=self.prefer_mss)
|
||||
items = self.ocr.ocr_bytes(cap.image_bytes)
|
||||
pt = self.ocr.find_text(text, items, exact=exact, case_sensitive=case_sensitive)
|
||||
if pt is None:
|
||||
return None
|
||||
|
||||
img_x, img_y = pt
|
||||
left, top, _, _ = cap.region
|
||||
# OCR 坐标是“截图图片像素坐标”,需先缩放到屏幕坐标,再加上截图区域偏移
|
||||
scr_x = int(left + img_x * cap.scale_x)
|
||||
scr_y = int(top + img_y * cap.scale_y)
|
||||
return MatchResult(x=scr_x, y=scr_y, text=text)
|
||||
|
||||
def click_text(
|
||||
self,
|
||||
text: str,
|
||||
*,
|
||||
region: Optional[tuple[int, int, int, int]] = None,
|
||||
exact: bool = True,
|
||||
case_sensitive: bool = False,
|
||||
clicks: int = 1,
|
||||
interval: float = 0.05,
|
||||
button: str = "left",
|
||||
move_duration: float = 0.0,
|
||||
pause: float = 0.05,
|
||||
) -> MatchResult:
|
||||
pyautogui.PAUSE = pause
|
||||
m = self.locate_text(text, region=region, exact=exact, case_sensitive=case_sensitive)
|
||||
if m is None:
|
||||
raise TimeoutError(f"未找到文字:{text}")
|
||||
pyautogui.moveTo(m.x, m.y, duration=move_duration)
|
||||
pyautogui.click(x=m.x, y=m.y, clicks=clicks, interval=interval, button=button)
|
||||
return m
|
||||
|
||||
def wait_for_text(
|
||||
self,
|
||||
text: str,
|
||||
*,
|
||||
timeout: float = 20.0,
|
||||
interval: float = 0.5,
|
||||
region: Optional[tuple[int, int, int, int]] = None,
|
||||
exact: bool = True,
|
||||
case_sensitive: bool = False,
|
||||
) -> MatchResult:
|
||||
end = time.time() + timeout
|
||||
while time.time() < end:
|
||||
m = self.locate_text(text, region=region, exact=exact, case_sensitive=case_sensitive)
|
||||
if m is not None:
|
||||
return m
|
||||
time.sleep(interval)
|
||||
raise TimeoutError(f"等待超时:{timeout}s 内未出现文字:{text}")
|
||||
|
||||
Reference in New Issue
Block a user