高级控件交互(操作)–ActionChains
- 说明:
- 1、appium中的ActionChains用到就是selenium中的ActionChains,导包的时候导入的是selenium的包;
- 2、而具体用法却和selenium有点区别:
- selenium中:直接初始化ActionChains 后就可以直接调用相关方法;
- appium中:初始化ActionChains之后,需要使用w3c_actions属性去创建输入源,之后执行w3c_actions下的方法;
- 3、actions-w3c官网
(1)ActionChains构造方法源码—初始化actions:
actions = ActionChains(driver)
class ActionChains:
def __init__(self, driver: WebDriver, duration: int = 250, devices: list[AnyDevice] | None = None) -> None:
"""Creates a new ActionChains.
:Args:
- driver: The WebDriver instance which performs user actions.
- duration: override the default 250 msecs of DEFAULT_MOVE_DURATION in PointerInput
"""
self._driver = driver
mouse = None
keyboard = None
wheel = None
if devices is not None and isinstance(devices, list):
for device in devices:
if isinstance(device, PointerInput):
mouse = device
if isinstance(device, KeyInput):
keyboard = device
if isinstance(device, WheelInput):
wheel = device
self.w3c_actions = ActionBuilder(driver, mouse=mouse, keyboard=keyboard, wheel=wheel, duration=duration)
-
self.w3c_actions
属性:就是用来定义appium中的事件; -
ActionBuilder(driver, mouse=mouse, keyboard=keyboard, wheel=wheel, duration=duration)
:用来定义输入源;- mouse:鼠标输入源
- keyboard:键盘输入源
- wheel:鼠标滚轮输入源
- duration:
(2)ActionBuilder源码—获取动作构造器
ActionBuilder的构造方法:
actions.w3c_actions = ActionBuilder(driver, mouse=PointerInput(interaction.POINTER_TOUCH, “touch”))
class ActionBuilder:
def __init__(
self,
driver,
mouse: Optional[PointerInput] = None,
wheel: Optional[WheelInput] = None,
keyboard: Optional[KeyInput] = None,
duration: int = 250,
) -> None:
mouse = mouse or PointerInput(interaction.POINTER_MOUSE, "mouse")
keyboard = keyboard or KeyInput(interaction.KEY)
wheel = wheel or WheelInput(interaction.WHEEL)
self.devices = [mouse, keyboard, wheel]
self._key_action = KeyActions(keyboard)
self._pointer_action = PointerActions(mouse, duration=duration)
self._wheel_action = WheelActions(wheel)
self.driver = driver
- PointerInput:鼠标指针输入选项
- WheelInput:鼠标滚轮输入选项
- KeyInput:键盘输入选项
ActionBuilder中的属性和方法:
def get_device_with(self, name: str) -> Optional[Union["WheelInput", "PointerInput", "KeyInput"]]:
return next(filter(lambda x: x == name, self.devices), None)
@property
def pointer_inputs(self) -> List[PointerInput]:
return [device for device in self.devices if device.type == interaction.POINTER]
@property
def key_inputs(self) -> List[KeyInput]:
return [device for device in self.devices if device.type == interaction.KEY]
@property
def key_action(self) -> KeyActions:
return self._key_action
@property
def pointer_action(self) -> PointerActions:
return self._pointer_action
@property
def wheel_action(self) -> WheelActions:
return self._wheel_action
def add_key_input(self, name: str) -> KeyInput:
new_input = KeyInput(name)
self._add_input(new_input)
return new_input
def add_pointer_input(self, kind: str, name: str) -> PointerInput:
new_input = PointerInput(kind, name)
self._add_input(new_input)
return new_input
def add_wheel_input(self, name: str) -> WheelInput:
new_input = WheelInput(name)
self._add_input(new_input)
return new_input
def perform(self) -> None:
enc = {"actions": []}
for device in self.devices:
encoded = device.encode()
if encoded["actions"]:
enc["actions"].append(encoded)
device.actions = []
self.driver.execute(Command.W3C_ACTIONS, enc)
def clear_actions(self) -> None:
"""Clears actions that are already stored on the remote end."""
self.driver.execute(Command.W3C_CLEAR_ACTIONS)
def _add_input(self, new_input: Union[KeyInput, PointerInput, WheelInput]) -> None:
self.devices.append(new_input)
(3)PointerInput构造方法源码—创建输入源
mouse=PointerInput(interaction.POINTER_TOUCH, "touch")
class PointerInput(InputDevice):
DEFAULT_MOVE_DURATION = 250
def __init__(self, kind, name):
super().__init__()
if kind not in POINTER_KINDS:
raise InvalidArgumentException(f"Invalid PointerInput kind '{kind}'")
self.type = POINTER
self.kind = kind
self.name = name
- kind:鼠标指针输入类型
- name:鼠标指针输入名字
(4)interaction模块源码—输入源类型
from typing import Dict
from typing import Union
KEY = "key"
POINTER = "pointer"
NONE = "none"
WHEEL = "wheel"
SOURCE_TYPES = {KEY, POINTER, NONE}
POINTER_MOUSE = "mouse"
POINTER_TOUCH = "touch"
POINTER_PEN = "pen"
POINTER_KINDS = {POINTER_MOUSE, POINTER_TOUCH, POINTER_PEN}
class Interaction:
PAUSE = "pause"
def __init__(self, source: str) -> None:
self.source = source
class Pause(Interaction):
def __init__(self, source, duration: float = 0) -> None:
super().__init__(source)
self.duration = duration
def encode(self) -> Dict[str, Union[str, int]]:
return {"type": self.PAUSE, "duration": int(self.duration * 1000)}
(5)actions.w3c_actions.pointer_action–指针动作
- pointer_down():鼠标左键按下—常用
- pointer_up():鼠标左前抬起—常用
- move_to():
- move_by():
- move_to_location():移动到位置—常用
- click():鼠标左键点击—常用
- context_click():鼠标右键点击—常用
- click_and_hold():鼠标左键按住—常用
- release():鼠标释放—常用
- double_click():双击
- pause():暂停
class PointerActions(Interaction):
def __init__(self, source: Optional[PointerInput] = None, duration: int = 250):
"""
Args:
- source: PointerInput instance
- duration: override the default 250 msecs of DEFAULT_MOVE_DURATION in source
"""
if not source:
source = PointerInput(interaction.POINTER_MOUSE, "mouse")
self.source = source
self._duration = duration
super().__init__(source)
def pointer_down(
self,
button=MouseButton.LEFT,
width=None,
height=None,
pressure=None,
tangential_pressure=None,
tilt_x=None,
tilt_y=None,
twist=None,
altitude_angle=None,
azimuth_angle=None,
):
self._button_action(
"create_pointer_down",
button=button,
width=width,
height=height,
pressure=pressure,
tangential_pressure=tangential_pressure,
tilt_x=tilt_x,
tilt_y=tilt_y,
twist=twist,
altitude_angle=altitude_angle,
azimuth_angle=azimuth_angle,
)
return self
def pointer_up(self, button=MouseButton.LEFT):
self._button_action("create_pointer_up", button=button)
return self
def move_to(
self,
element,
x=0,
y=0,
width=None,
height=None,
pressure=None,
tangential_pressure=None,
tilt_x=None,
tilt_y=None,
twist=None,
altitude_angle=None,
azimuth_angle=None,
):
if not isinstance(element, WebElement):
raise AttributeError("move_to requires a WebElement")
self.source.create_pointer_move(
origin=element,
duration=self._duration,
x=int(x),
y=int(y),
width=width,
height=height,
pressure=pressure,
tangential_pressure=tangential_pressure,
tilt_x=tilt_x,
tilt_y=tilt_y,
twist=twist,
altitude_angle=altitude_angle,
azimuth_angle=azimuth_angle,
)
return self
def move_by(
self,
x,
y,
width=None,
height=None,
pressure=None,
tangential_pressure=None,
tilt_x=None,
tilt_y=None,
twist=None,
altitude_angle=None,
azimuth_angle=None,
):
self.source.create_pointer_move(
origin=interaction.POINTER,
duration=self._duration,
x=int(x),
y=int(y),
width=width,
height=height,
pressure=pressure,
tangential_pressure=tangential_pressure,
tilt_x=tilt_x,
tilt_y=tilt_y,
twist=twist,
altitude_angle=altitude_angle,
azimuth_angle=azimuth_angle,
)
return self
def move_to_location(
self,
x,
y,
width=None,
height=None,
pressure=None,
tangential_pressure=None,
tilt_x=None,
tilt_y=None,
twist=None,
altitude_angle=None,
azimuth_angle=None,
):
self.source.create_pointer_move(
origin="viewport",
duration=self._duration,
x=int(x),
y=int(y),
width=width,
height=height,
pressure=pressure,
tangential_pressure=tangential_pressure,
tilt_x=tilt_x,
tilt_y=tilt_y,
twist=twist,
altitude_angle=altitude_angle,
azimuth_angle=azimuth_angle,
)
return self
def click(self, element: Optional[WebElement] = None, button=MouseButton.LEFT):
if element:
self.move_to(element)
self.pointer_down(button)
self.pointer_up(button)
return self
def context_click(self, element: Optional[WebElement] = None):
return self.click(element=element, button=MouseButton.RIGHT)
def click_and_hold(self, element: Optional[WebElement] = None, button=MouseButton.LEFT):
if element:
self.move_to(element)
self.pointer_down(button=button)
return self
def release(self, button=MouseButton.LEFT):
self.pointer_up(button=button)
return self
def double_click(self, element: Optional[WebElement] = None):
if element:
self.move_to(element)
self.pointer_down(MouseButton.LEFT)
self.pointer_up(MouseButton.LEFT)
self.pointer_down(MouseButton.LEFT)
self.pointer_up(MouseButton.LEFT)
return self
def pause(self, duration: float = 0):
self.source.create_pause(duration)
return self
def _button_action(self, action, **kwargs):
meth = getattr(self.source, action)
meth(**kwargs)
return self
(6)使用步骤
- 1、定义 ActionChains 实例:
ActionChains(driver)
- 2、定义输入源:
actions.w3c_actions = ActionBuilder(driver, mouse=PointerInput(interaction.POINTER_TOUCH, "touch"))
- ActionChains里有个属性w3c_actions 是ActionBuilder类型的, 使用的就是w3c协议,可以定义鼠标指针源,键盘源,滚轮源事件;
- 3、调用动作:移动、按下、滑动、抬起等等;
actions.w3c_actions.pointer_action.move_to_location(115, 183)
actions.w3c_actions.pointer_action.pointer_down()
actions.w3c_actions.pointer_action.move_to_location(362, 179)
- 4、释放指针:
actions.w3c_actions.pointer_action.release()
- 5、执行动作:
actions.perform()
(7)案例-使用ActionChains处理手势滑动解锁
- touchaction.apk下载地址如下-百度网盘:
- 链接:百度网盘 请输入提取码
- 提取码:diaz
- 说明:手势解锁一般都是封装好的api,通过常规定位无法定位到,只能通过坐标进行操作;
import time
from appium import webdriver
from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver import ActionChains
from selenium.webdriver.common.actions import interaction
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver.common.actions.pointer_input import PointerInput
caps = {
"platformName": "Android",
"appium:platformVersion": "6.0.1",
"appium:deviceName": "127.0.0.1:7555",
"appium:appPackage": "cn.kmob.screenfingermovelock",
"appium:appActivity": "com.samsung.ui.FlashActivity",
"appium:automationName": "UiAutomator2",
"appium:unicodeKeyboard": "true",
"appium:restKeyboard": "true",
"appium:noReset": "true"
}
surver_url = "http://127.0.0.1:4723"
driver = webdriver.Remote(surver_url,options=UiAutomator2Options().load_capabilities(caps=caps))
driver.implicitly_wait(10)
# 点击设置手势-进入手势密码设置界面
driver.find_element(AppiumBy.ID,"cn.kmob.screenfingermovelock:id/setPatternLayout").click()
# 1、创建动作实例
action = ActionChains(driver)
# 2、输入动作源--指针输入--触摸类型
action.w3c_actions = ActionBuilder(driver=driver,mouse=PointerInput(kind=interaction.POINTER_TOUCH,name="touch"))
# 3、添加动作--鼠标移动到第一个点之后按下
action.w3c_actions.pointer_action.move_to_location(x=120,y=175)
action.w3c_actions.pointer_action.pointer_down()
time.sleep(1)
# 指针移动到其他点
# 第二个点
action.w3c_actions.pointer_action.move_to_location(x=360,y=175)
time.sleep(1)
# 第三个点
action.w3c_actions.pointer_action.move_to_location(x=600,y=175)
time.sleep(1)
# 第四个点
action.w3c_actions.pointer_action.move_to_location(x=600,y=415)
time.sleep(0.1)
# 第五个点
action.w3c_actions.pointer_action.move_to_location(x=600,y=650)
time.sleep(0.1)
# 4、释放指针
action.w3c_actions.pointer_action.release()
# 5、执行动作
action.perform()