import os import shutil import subprocess import sys import time from pathlib import Path from .config import ScreenshotCfg class ScreenshotCapture: def __init__(self, cfg: ScreenshotCfg): self._cfg = cfg def capture(self) -> str: """截取全屏,返回保存路径。自动选择当前平台最佳方式。""" if self._cfg.delay_ms > 0: time.sleep(self._cfg.delay_ms / 1000) out_path = self._cfg.tmp_path Path(out_path).parent.mkdir(parents=True, exist_ok=True) if sys.platform == "darwin": self._capture_macos(out_path) else: self._capture_linux(out_path) if not Path(out_path).exists(): raise RuntimeError(f"截图文件未生成: {out_path}") return out_path # ------------------------------------------------------------------ # macOS # ------------------------------------------------------------------ def _capture_macos(self, out_path: str) -> None: # screencapture -x 静默截全屏(无声音/闪烁) subprocess.run(["screencapture", "-x", out_path], check=True) # ------------------------------------------------------------------ # Linux(按优先级尝试) # ------------------------------------------------------------------ def _capture_linux(self, out_path: str) -> None: wayland = os.environ.get("WAYLAND_DISPLAY") if wayland: # Wayland 优先:grim(轻量)> spectacle > gnome-screenshot if shutil.which("grim"): subprocess.run(["grim", out_path], check=True) return if shutil.which("spectacle"): # spectacle -b 后台 -f 全屏 -n 无通知 -o 输出路径 # 丢弃 stderr(Tesseract 未安装的无害警告) subprocess.run( ["spectacle", "-b", "-f", "-n", "-o", out_path], check=True, timeout=10, stderr=subprocess.DEVNULL, ) return if shutil.which("gnome-screenshot"): subprocess.run(["gnome-screenshot", "-f", out_path], check=True) return # 最后尝试 mss(可能在 XWayland 下有效) self._capture_mss(out_path) else: # X11:mss 最快,失败则用 scrot try: self._capture_mss(out_path) except Exception: if shutil.which("scrot"): subprocess.run(["scrot", out_path], check=True) elif shutil.which("import"): # ImageMagick subprocess.run(["import", "-window", "root", out_path], check=True) else: raise RuntimeError("未找到可用的截图工具(scrot / import)") def _capture_mss(self, out_path: str) -> None: import mss import mss.tools with mss.mss() as sct: monitor = sct.monitors[0] # 0 = 全部显示器合并 screenshot = sct.grab(monitor) mss.tools.to_png(screenshot.rgb, screenshot.size, output=out_path)