pyhon学习之selenium模拟鼠标事件

 首先我们贴代码,源码解析好吧。

# Licensed to the Software Freedom Conservancy (SFC) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The SFC licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

"""
The ActionChains implementation,
"""

import time
'''
首先我们使用了time模块,因为在后面会用到sleep函数。也就是对应的 成员函数pause
'''

from selenium.webdriver.remote.command import Command
'''
然后我们会用到 Command,这个类定义了一些列的Key_value映射,类比宏定义。避免直接使用字符串编译器
不报错。因为字符串编译器不会检查,但是变量会检查呀。
'''


from .utils import keys_to_typing

'''
然后我们会用到 Command,这个类定义了一些列的Key_value映射,类比宏定义。避免直接使用字符串编译器
不报错。因为字符串编译器不会检查,但是变量会检查呀。

然后使用了 keys_to_typing方法,这个模拟打印,也就是变成字符,存入list中。
def keys_to_typing(value):
    """Processes the values that will be typed in the element."""
    typing = []
    for val in value:
        if isinstance(val, Keys):
            typing.append(val)
        elif isinstance(val, int):
            val = str(val)
            for i in range(len(val)):
                typing.append(val[i])
        else:
            for i in range(len(val)):
                typing.append(val[i])
    return typing

'''
from .actions.action_builder import ActionBuilder

'''
操作类,操作,模拟执行操作。
'''


class ActionChains(object):
    """
    ActionChains are a way to automate low level interactions such as
    mouse movements, mouse button actions, key press, and context menu interactions.
    This is useful for doing more complex actions like hover over and drag and drop.

    ActionChains是自动执行低级的操作,比如鼠标移动,鼠标点击时间,鼠标按下,
    或者是鼠标右键事件。这个在操作更复杂操作的时候很有用:比如 hover,over,drag,drop等等。

    Generate user actions.
       When you call methods for actions on the ActionChains object,
       the actions are stored in a queue in the ActionChains object.
       When you call perform(), the events are fired in the order they
       are queued up.

    一般的用户操作.
       当你调用这些方法的时候,这些操作将会以链表的形式存放在ActionChains类中。
       只有当你调用perform方法的时候这些时间才会根据排列顺序被依次执行。

    ActionChains can be used in a chain pattern::

        menu = driver.find_element_by_css_selector(".nav")
        hidden_submenu = driver.find_element_by_css_selector(".nav #submenu1")

        ActionChains(driver).move_to_element(menu).click(hidden_submenu).perform()

    ActionChains可以在chain中执行。
        这个案例的意思就是:
           通过驱动移动鼠标到menu按钮,然后点击hidden_submenu按钮。
           为什么是链式的呢,因为这些方法返回的都是self,也就是本体,
           这些方法都是这个类的成员方法。
           最后调用perform讲前面的所有操作在driver上面执行。 
        
    Or actions can be queued up one by one, then performed.::

        menu = driver.find_element_by_css_selector(".nav")
        hidden_submenu = driver.find_element_by_css_selector(".nav #submenu1")

        actions = ActionChains(driver)
        actions.move_to_element(menu)
        actions.click(hidden_submenu)
        actions.perform()

    或者actions也可以一个的排队然后执行。
    和上面一样,这个是一个个的安排,然后执行进去。
    推荐链式编程。


    Either way, the actions are performed in the order they are called, one after
    another.
    
    无论前面怎么做,都是一样的,都是一个操作一个操作的执行。
    """

    def __init__(self, driver):
        """
        Creates a new ActionChains.

        :Args:
         - driver: The WebDriver instance which performs user actions.

        创建一个新的ActionChains对象

        参数是一个webdriver类型的实例,这个对象将会被用来执行点击等操作。

        如果是符合 w3c 的标准的才会创建对象,否则创建另外一种。
        """
        self._driver = driver
        self._actions = []
        if self._driver.w3c:
            self.w3c_actions = ActionBuilder(driver)

    def perform(self):
        """
        Performs all stored actions.
        如果满足w3c的执行w3c的操作,不满足则执行self的操作
        """
        if self._driver.w3c:
            self.w3c_actions.perform()
        else:
            for action in self._actions:
                action()

    def reset_actions(self):
        """
            Clears actions that are already stored locally and on the remote end
            清空存储在远端和本地的所有操作
        """
        if self._driver.w3c:
            self.w3c_actions.clear_actions()
        self._actions = []

    def click(self, on_element=None):
        """
        Clicks an element.

        :Args:
         - on_element: The element to click.
           If None, clicks on current mouse position.

        只讲解一个案例,后面的自己看。
        点击一个元素。基本所有的元素都支持点击。
        
        参数:
            一个元素,将要被点击的元素。
            如果为空,讲会点击当前鼠标所在的位置。

        首先是如果不为空就移动到对应的位置的按钮上面去。
        如果是3c标准就存到3c里面
        如果是其他标准就存放到自己的里面。
        返回本身 ----- 支持链式编程
        """
        if on_element:
            self.move_to_element(on_element)
        if self._driver.w3c:
            self.w3c_actions.pointer_action.click()
            self.w3c_actions.key_action.pause()
            self.w3c_actions.key_action.pause()
        else:
            self._actions.append(lambda: self._driver.execute(
                                 Command.CLICK, {'button': 0}))
        return self

    def click_and_hold(self, on_element=None):
        """
        Holds down the left mouse button on an element.

        :Args:
         - on_element: The element to mouse down.
           If None, clicks on current mouse position.
        """
        if on_element:
            self.move_to_element(on_element)
        if self._driver.w3c:
            self.w3c_actions.pointer_action.click_and_hold()
            self.w3c_actions.key_action.pause()
        else:
            self._actions.append(lambda: self._driver.execute(
                                 Command.MOUSE_DOWN, {}))
        return self

    def context_click(self, on_element=None):
        """
        Performs a context-click (right click) on an element.

        :Args:
         - on_element: The element to context-click.
           If None, clicks on current mouse position.
        """
        if on_element:
            self.move_to_element(on_element)
        if self._driver.w3c:
            self.w3c_actions.pointer_action.context_click()
            self.w3c_actions.key_action.pause()
            self.w3c_actions.key_action.pause()
        else:
            self._actions.append(lambda: self._driver.execute(
                                 Command.CLICK, {'button': 2}))
        return self

    def double_click(self, on_element=None):
        """
        Double-clicks an element.

        :Args:
         - on_element: The element to double-click.
           If None, clicks on current mouse position.
        """
        if on_element:
            self.move_to_element(on_element)
        if self._driver.w3c:
            self.w3c_actions.pointer_action.double_click()
            for _ in range(4):
                self.w3c_actions.key_action.pause()
        else:
            self._actions.append(lambda: self._driver.execute(
                                 Command.DOUBLE_CLICK, {}))
        return self

    def drag_and_drop(self, source, target):
        """
        Holds down the left mouse button on the source element,
           then moves to the target element and releases the mouse button.

        :Args:
         - source: The element to mouse down.
         - target: The element to mouse up.

        鼠标左键保持按下状态,被选中的为source元素。
        然后移动到target元素并且释放按钮。
        
        参数:
           source  将被选中的按钮
           target  将被拖动到的位置并释放左键的元素范围,一般是拖动。
        """
        self.click_and_hold(source)
        self.release(target)
        return self

    def drag_and_drop_by_offset(self, source, xoffset, yoffset):
        """
        Holds down the left mouse button on the source element,
           then moves to the target offset and releases the mouse button.

        :Args:
         - source: The element to mouse down.
         - xoffset: X offset to move to.
         - yoffset: Y offset to move to.
        """
        self.click_and_hold(source)
        self.move_by_offset(xoffset, yoffset)
        self.release()
        return self

    def key_down(self, value, element=None):
        """
        Sends a key press only, without releasing it.
           Should only be used with modifier keys (Control, Alt and Shift).

        :Args:
         - value: The modifier key to send. Values are defined in `Keys` class.
         - element: The element to send keys.
           If None, sends a key to current focused element.

        Example, pressing ctrl+c::

            ActionChains(driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()

        """
        if element:
            self.click(element)
        if self._driver.w3c:
            self.w3c_actions.key_action.key_down(value)
            self.w3c_actions.pointer_action.pause()
        else:
            self._actions.append(lambda: self._driver.execute(
                Command.SEND_KEYS_TO_ACTIVE_ELEMENT,
                {"value": keys_to_typing(value)}))
        return self

    def key_up(self, value, element=None):
        """
        Releases a modifier key.

        :Args:
         - value: The modifier key to send. Values are defined in Keys class.
         - element: The element to send keys.
           If None, sends a key to current focused element.

        Example, pressing ctrl+c::

            ActionChains(driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()

        """
        if element:
            self.click(element)
        if self._driver.w3c:
            self.w3c_actions.key_action.key_up(value)
            self.w3c_actions.pointer_action.pause()
        else:
            self._actions.append(lambda: self._driver.execute(
                Command.SEND_KEYS_TO_ACTIVE_ELEMENT,
                {"value": keys_to_typing(value)}))
        return self

    def move_by_offset(self, xoffset, yoffset):
        """
        Moving the mouse to an offset from current mouse position.

        :Args:
         - xoffset: X offset to move to, as a positive or negative integer.
         - yoffset: Y offset to move to, as a positive or negative integer.
        """
        if self._driver.w3c:
            self.w3c_actions.pointer_action.move_by(xoffset, yoffset)
            self.w3c_actions.key_action.pause()
        else:
            self._actions.append(lambda: self._driver.execute(
                Command.MOVE_TO, {
                    'xoffset': int(xoffset),
                    'yoffset': int(yoffset)}))
        return self

    def move_to_element(self, to_element):
        """
        Moving the mouse to the middle of an element.

        :Args:
         - to_element: The WebElement to move to.
        将鼠标移动到元素中央
           to_element: 将要被移动到的webElement元素
        """
        if self._driver.w3c:
            self.w3c_actions.pointer_action.move_to(to_element)
            self.w3c_actions.key_action.pause()
        else:
            self._actions.append(lambda: self._driver.execute(
                                 Command.MOVE_TO, {'element': to_element.id}))
        return self

    def move_to_element_with_offset(self, to_element, xoffset, yoffset):
        """
        Move the mouse by an offset of the specified element.
           Offsets are relative to the top-left corner of the element.

        :Args:
         - to_element: The WebElement to move to.
         - xoffset: X offset to move to.
         - yoffset: Y offset to move to.

        移动鼠标移动到指定的元素的末端
          末端是相对元素的右上角
        
        """
        if self._driver.w3c:
            self.w3c_actions.pointer_action.move_to(to_element, xoffset, yoffset)
            self.w3c_actions.key_action.pause()
        else:
            self._actions.append(
                lambda: self._driver.execute(Command.MOVE_TO, {
                    'element': to_element.id,
                    'xoffset': int(xoffset),
                    'yoffset': int(yoffset)}))
        return self

    def pause(self, seconds):
        """ Pause all inputs for the specified duration in seconds """
        if self._driver.w3c:
            self.w3c_actions.pointer_action.pause(seconds)
            self.w3c_actions.key_action.pause(seconds)
        else:
            self._actions.append(lambda: time.sleep(seconds))
        return self

    def release(self, on_element=None):
        """
        Releasing a held mouse button on an element.

        :Args:
         - on_element: The element to mouse up.
           If None, releases on current mouse position.
        """
        if on_element:
            self.move_to_element(on_element)
        if self._driver.w3c:
            self.w3c_actions.pointer_action.release()
            self.w3c_actions.key_action.pause()
        else:
            self._actions.append(lambda: self._driver.execute(Command.MOUSE_UP, {}))
        return self

    def send_keys(self, *keys_to_send):
        """
        Sends keys to current focused element.

        :Args:
         - keys_to_send: The keys to send.  Modifier keys constants can be found in the
           'Keys' class.
        """
        typing = keys_to_typing(keys_to_send)
        if self._driver.w3c:
            for key in typing:
                self.key_down(key)
                self.key_up(key)
        else:
            self._actions.append(lambda: self._driver.execute(
                Command.SEND_KEYS_TO_ACTIVE_ELEMENT, {'value': typing}))
        return self

    def send_keys_to_element(self, element, *keys_to_send):
        """
        Sends keys to an element.

        :Args:
         - element: The element to send keys.
         - keys_to_send: The keys to send.  Modifier keys constants can be found in the
           'Keys' class.
        """
        self.click(element)
        self.send_keys(*keys_to_send)
        return self

    # Context manager so ActionChains can be used in a 'with .. as' statements.
    def __enter__(self):
        return self  # Return created instance of self.

    def __exit__(self, _type, _value, _traceback):
        pass  # Do nothing, does not require additional cleanup.

猜你喜欢

转载自blog.csdn.net/rubikchen/article/details/84586766