Files
yidaima_tools/utils/screenshot.py

80 lines
2.8 KiB
Python
Raw Normal View History

2026-04-09 14:55:54 +08:00
from __future__ import annotations
import io
from dataclasses import dataclass
from typing import Optional
@dataclass(frozen=True)
class CaptureResult:
image_bytes: bytes
# 截图区域在“屏幕坐标系”中的位置left, top, width, height
region: tuple[int, int, int, int]
# 截图图片像素坐标 -> 屏幕坐标 的缩放系数
scale_x: float
scale_y: float
def capture_screen(region: Optional[tuple[int, int, int, int]] = None, *, prefer_mss: bool = True) -> CaptureResult:
"""
截取屏幕全屏或局部返回 PNG bytes + region + 像素到屏幕坐标的缩放
- region: (left, top, width, height) 使用屏幕坐标
- scale_x/scale_y: 用于处理高 DPI mss 取到的物理像素与 pyautogui 屏幕坐标不一致的问题
"""
if prefer_mss:
try:
return _capture_with_mss(region)
except Exception:
# 回退到 pyautogui更贴近“屏幕坐标系”
return _capture_with_pyautogui(region)
return _capture_with_pyautogui(region)
def _capture_with_pyautogui(region: Optional[tuple[int, int, int, int]]) -> CaptureResult:
import pyautogui
img = pyautogui.screenshot(region=region) # type: ignore[arg-type]
buf = io.BytesIO()
img.save(buf, format="PNG")
if region is None:
w, h = img.size
region2 = (0, 0, int(w), int(h))
else:
region2 = (int(region[0]), int(region[1]), int(region[2]), int(region[3]))
return CaptureResult(image_bytes=buf.getvalue(), region=region2, scale_x=1.0, scale_y=1.0)
def _capture_with_mss(region: Optional[tuple[int, int, int, int]]) -> CaptureResult:
import mss
import mss.tools
import pyautogui
with mss.mss() as sct:
if region is None:
mon = sct.monitors[1] # 主屏
left, top, width, height = int(mon["left"]), int(mon["top"]), int(mon["width"]), int(mon["height"])
else:
left, top, width, height = (int(region[0]), int(region[1]), int(region[2]), int(region[3]))
shot = sct.grab({"left": left, "top": top, "width": width, "height": height})
png_bytes = mss.tools.to_png(shot.rgb, shot.size)
# DPI 缩放处理mss 的像素尺寸可能与 pyautogui 的屏幕坐标尺寸不同
screen_w, screen_h = pyautogui.size()
mon = sct.monitors[1]
mon_w, mon_h = int(mon["width"]), int(mon["height"])
# 若 mss 返回的是物理像素mon_w/mon_h 往往大于 pyautogui.size()
scale_x = float(screen_w) / float(mon_w) if mon_w else 1.0
scale_y = float(screen_h) / float(mon_h) if mon_h else 1.0
return CaptureResult(
image_bytes=png_bytes,
region=(left, top, width, height),
scale_x=scale_x,
scale_y=scale_y,
)