autool/DroidBot/platforms/windows/windows_input_event.py
2026-06-17 19:44:18 +08:00

201 lines
6.6 KiB
Python
Raw Permalink 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.

"""
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_viewsbounds 是相对窗口的吗?
# 即使 Device 实现了 _update_window_rectCV 识别是基于截图的。
# 如果截图是全屏截图(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