Files
VRCWT-OSC/VRWT_Tool/VRC_OSC/src/screen/screen_actions.py
2026-06-24 00:38:14 +09:00

168 lines
4.3 KiB
Python

import pyautogui
import pygetwindow as gw
from common.config_loader import loadOcrConfig
from common.project_paths import RUNTIME_DIR
SCREENSHOT_PATH = RUNTIME_DIR / "latest_screenshot.png"
OCR_SCREENSHOT_PATH = RUNTIME_DIR / "latest_ocr.png"
EXCLUDED_WINDOW_KEYWORDS = [
"Google Chrome",
"Microsoft Edge",
"Mozilla Firefox",
"Visual Studio Code",
"PowerShell",
"Windows Terminal",
"ChatGPT",
]
def log(level, message):
from datetime import datetime
now = datetime.now().strftime("%H:%M:%S")
print(f"[{level}] {now} {message}", flush=True)
def findVrchatWindow():
windows = gw.getAllWindows()
candidates = []
for window in windows:
title = window.title or ""
normalized = title.strip().lower()
if not normalized:
continue
if any(keyword.lower() in normalized for keyword in EXCLUDED_WINDOW_KEYWORDS):
continue
if normalized == "vrchat":
candidates.append((0, window))
continue
if normalized.startswith("vrchat "):
candidates.append((1, window))
continue
if normalized.startswith("vrchat"):
candidates.append((2, window))
if not candidates:
visible_titles = []
for window in windows:
title = (window.title or "").strip()
if title:
visible_titles.append(title)
log("ERROR", "VRChatウィンドウが見つかりません")
log("CHECK", "現在検出できるウィンドウ")
for title in visible_titles[:30]:
print(title, flush=True)
raise RuntimeError("VRChatウィンドウが見つかりません")
candidates = sorted(
candidates,
key=lambda item: (item[0], -(item[1].width * item[1].height)),
)
window = candidates[0][1]
if window.width <= 0 or window.height <= 0:
raise RuntimeError("VRChatウィンドウサイズが不正です")
return window
def getVrchatRegion():
window = findVrchatWindow()
try:
if window.isMinimized:
window.restore()
except Exception:
pass
left = int(window.left)
top = int(window.top)
width = int(window.width)
height = int(window.height)
if width <= 0 or height <= 0:
raise RuntimeError("VRChatウィンドウ範囲が不正です")
log("INFO", f"VRChat window title={window.title}")
log("INFO", f"VRChat region left={left} top={top} width={width} height={height}")
return left, top, width, height
def clampRatio(value):
return max(0.0, min(1.0, value))
def getOcrRegion():
left, top, width, height = getVrchatRegion()
config = loadOcrConfig()
# Default to the lower central area. It is much faster than OCRing the
# whole VRChat window and usually contains chat/subtitle text.
crop_left = clampRatio(config["crop_left"])
crop_top = clampRatio(config["crop_top"])
crop_right = clampRatio(config["crop_right"])
crop_bottom = clampRatio(config["crop_bottom"])
if crop_right <= crop_left:
crop_left = 0.0
crop_right = 1.0
if crop_bottom <= crop_top:
crop_top = 0.0
crop_bottom = 1.0
region_left = left + int(width * crop_left)
region_top = top + int(height * crop_top)
region_width = max(1, int(width * (crop_right - crop_left)))
region_height = max(1, int(height * (crop_bottom - crop_top)))
log(
"INFO",
f"OCR crop left={region_left} top={region_top} width={region_width} height={region_height}",
)
return region_left, region_top, region_width, region_height
def captureRegionTo(image_path, region):
image_path.parent.mkdir(parents=True, exist_ok=True)
left, top, width, height = region
image = pyautogui.screenshot(region=(left, top, width, height))
image.save(image_path)
log("ACTION", f"captureVrchatWindow saved={image_path}")
return image_path
def captureVrchatWindowTo(image_path):
return captureRegionTo(
image_path,
getVrchatRegion(),
)
def captureVrchatWindow():
return captureVrchatWindowTo(SCREENSHOT_PATH)
def captureOcrRegion():
return captureRegionTo(
OCR_SCREENSHOT_PATH,
getOcrRegion(),
)
def captureScreen():
return captureVrchatWindow()
def takeScreenshot():
return captureVrchatWindow()