Files
yidaima_tools/utils/screenshot.py
王鹏 a2f5875d1b init
2026-04-09 14:55:54 +08:00

80 lines
2.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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,
)