""" Windows Input Event Implementations Concrete implementations of input events for Windows desktop applications. """ from typing import Optional, Dict, Any, List from ...core.abstract_input_event import ( AbstractInputEvent, EventType, BaseTouchEvent, BaseLongTouchEvent, BaseSwipeEvent, BaseScrollEvent, BaseSetTextEvent, BaseKeyEvent, BaseKillAppEvent ) class WindowsTouchEvent(BaseTouchEvent): """Windows 点击事件""" def send(self, device) -> bool: """发送点击事件到 Windows 设备""" x, y = self.x, self.y if self.view is not None: from .windows_device_state import WindowsDeviceState x, y = WindowsDeviceState.get_view_center(self.view) # 默认左键点击 device.view_touch(int(x), int(y), button='left') return True class WindowsRightClickEvent(BaseTouchEvent): """Windows 右键点击事件 (新增)""" def send(self, device) -> bool: """发送右键点击事件到 Windows 设备""" x, y = self.x, self.y if self.view is not None: from .windows_device_state import WindowsDeviceState x, y = WindowsDeviceState.get_view_center(self.view) device.view_touch(int(x), int(y), button='right') return True class WindowsLongTouchEvent(BaseLongTouchEvent): """Windows 长按事件""" def send(self, device) -> bool: """发送长按事件到 Windows 设备""" x, y = self.x, self.y if self.view is not None: from .windows_device_state import WindowsDeviceState x, y = WindowsDeviceState.get_view_center(self.view) device.view_long_touch(int(x), int(y), self.duration) return True class WindowsSwipeEvent(BaseSwipeEvent): """Windows 滑动/拖拽事件""" def send(self, device) -> bool: """发送滑动事件到 Windows 设备""" device.view_drag( (self.start_x, self.start_y), (self.end_x, self.end_y), self.duration ) return True class WindowsScrollEvent(BaseScrollEvent): """Windows 滚动事件""" def send(self, device) -> bool: """发送滚动事件到 Windows 设备""" import pyautogui # 1. 确定滚动位置 start_x, start_y = None, None # 如果指定了目标 View,移动到 View 中心 if self.view is not None: from .windows_device_state import WindowsDeviceState start_x, start_y = WindowsDeviceState.get_view_center(self.view) # 转换为屏幕坐标 (device.view_drag 会处理,但这里是直接调用 pyautogui) # 注意:get_view_center 返回的是相对窗口坐标(如果是基于 device state 的话) # 我们需要检查 view_center 的逻辑。 # 现在的 WindowsDeviceState.get_view_center 是基于 cv_views,bounds 是相对窗口的吗? # 即使 Device 实现了 _update_window_rect,CV 识别是基于截图的。 # 如果截图是全屏截图(view_file 335行: path is None -> path=screen...png -> with mss -> grab monitor), # 那么 bounds 就是屏幕绝对坐标! pass if start_x is None: # 默认:窗口中心 display_info = device.get_display_info() width = display_info.get('width', 1920) height = display_info.get('height', 1080) target_x = display_info.get('left', 0) + width // 2 target_y = display_info.get('top', 0) + height // 2 else: # 必须转换为屏幕坐标 display_info = device.get_display_info() target_x = display_info.get('left', 0) + start_x target_y = display_info.get('top', 0) + start_y # 移动到目标位置 pyautogui.moveTo(target_x, target_y) # 执行滚动 scroll_amount = 3 # 滚动量 if self.direction == self.DIRECTION_UP: pyautogui.scroll(scroll_amount) # scroll up elif self.direction == self.DIRECTION_DOWN: pyautogui.scroll(-scroll_amount) # scroll down elif self.direction == self.DIRECTION_LEFT: pyautogui.hscroll(-scroll_amount) elif self.direction == self.DIRECTION_RIGHT: pyautogui.hscroll(scroll_amount) return True class WindowsSetTextEvent(BaseSetTextEvent): """Windows 文本输入事件""" def send(self, device) -> bool: """发送文本输入事件到 Windows 设备""" # 先点击目标视图 if self.view is not None: from .windows_device_state import WindowsDeviceState x, y = WindowsDeviceState.get_view_center(self.view) device.view_touch(int(x), int(y)) import time time.sleep(0.3) # 输入文本 device.view_set_text(self.text) return True class WindowsKeyEvent(BaseKeyEvent): """Windows 按键事件""" # Windows 按键映射 (通用名称 -> pyautogui 按键名) KEY_MAP = { 'BACK': 'backspace', 'HOME': 'win', 'MENU': 'apps', 'ENTER': 'enter', 'ESCAPE': 'escape', 'TAB': 'tab', 'DELETE': 'delete', 'UP': 'up', 'DOWN': 'down', 'LEFT': 'left', 'RIGHT': 'right', 'SPACE': 'space', 'CTRL': 'ctrl', 'ALT': 'alt', 'SHIFT': 'shift', } def send(self, device) -> bool: """发送按键事件到 Windows 设备""" key_code = self.KEY_MAP.get(self.key_name.upper(), self.key_name.lower()) device.key_press(key_code) return True class WindowsKillAppEvent(BaseKillAppEvent): """Windows 终止应用事件""" def send(self, device) -> bool: """终止 Windows 应用""" import subprocess if self.app is None: return False # 获取进程名 process_name = self.app if hasattr(self.app, 'get_process_name'): process_name = self.app.get_process_name() elif hasattr(self.app, 'exe_path'): import os process_name = os.path.basename(self.app.exe_path) try: # 使用 taskkill 强制终止进程 subprocess.run( ['taskkill', '/F', '/IM', process_name], capture_output=True, timeout=5 ) return True except Exception as e: device.logger.warning(f"Failed to kill app {process_name}: {e}") return False