mirror of
https://gitee.com/g1879/DrissionPage.git
synced 2024-12-10 04:00:23 +08:00
141 lines
5.5 KiB
Python
141 lines
5.5 KiB
Python
# -*- coding:utf-8 -*-
|
||
"""
|
||
@Author : g1879
|
||
@Contact : g1879@qq.com
|
||
"""
|
||
from time import perf_counter, sleep
|
||
|
||
from .._commons.constants import Settings
|
||
from .._commons.web import offset_scroll
|
||
from ..errors import CanNotClickError, CDPError, NoRectError
|
||
|
||
|
||
class Clicker(object):
|
||
def __init__(self, ele):
|
||
"""
|
||
:param ele: ChromiumElement
|
||
"""
|
||
self._ele = ele
|
||
|
||
def __call__(self, by_js=False, timeout=1):
|
||
"""点击元素
|
||
如果遇到遮挡,可选择是否用js点击
|
||
:param by_js: 是否用js点击,为None时先用模拟点击,遇到遮挡改用js,为True时直接用js点击,为False时只用模拟点击
|
||
:param timeout: 模拟点击的超时时间,等待元素可见、不被遮挡、进入视口
|
||
:return: 是否点击成功
|
||
"""
|
||
return self.left(by_js, timeout)
|
||
|
||
def left(self, by_js=False, timeout=2):
|
||
"""点击元素,可选择是否用js点击
|
||
:param by_js: 是否用js点击,为None时先用模拟点击,遇到遮挡改用js,为True时直接用js点击,为False时只用模拟点击
|
||
:param timeout: 模拟点击的超时时间,等待元素可见、不被遮挡、进入视口
|
||
:return: 是否点击成功
|
||
"""
|
||
if not by_js: # 模拟点击
|
||
can_click = False
|
||
timeout = self._ele.page.timeout if timeout is None else timeout
|
||
rect = None
|
||
if timeout == 0:
|
||
try:
|
||
self._ele.scroll.to_see()
|
||
if self._ele.states.is_enabled and self._ele.states.is_displayed:
|
||
rect = self._ele.locations.viewport_rect
|
||
can_click = True
|
||
except NoRectError:
|
||
if by_js is False:
|
||
raise
|
||
|
||
else:
|
||
rect = self._ele.states.has_rect
|
||
end_time = perf_counter() + timeout
|
||
while not rect and perf_counter() < end_time:
|
||
rect = self._ele.states.has_rect
|
||
sleep(.001)
|
||
|
||
self._ele.wait.stop_moving(timeout=end_time - perf_counter())
|
||
if rect:
|
||
self._ele.scroll.to_see()
|
||
rect = self._ele.locations.rect
|
||
while perf_counter() < end_time:
|
||
if self._ele.states.is_enabled and self._ele.states.is_displayed:
|
||
can_click = True
|
||
break
|
||
sleep(.001)
|
||
|
||
elif by_js is False:
|
||
raise NoRectError
|
||
|
||
if can_click and not self._ele.states.is_in_viewport:
|
||
by_js = True
|
||
|
||
elif can_click and (by_js is False or not self._ele.states.is_covered):
|
||
x = int(rect[1][0] - (rect[1][0] - rect[0][0]) / 2)
|
||
y = rect[0][0] + 3
|
||
try:
|
||
r = self._ele.page.run_cdp('DOM.getNodeForLocation', x=x, y=y, includeUserAgentShadowDOM=True,
|
||
ignorePointerEventsNone=True)
|
||
if r['backendNodeId'] != self._ele.ids.backend_id:
|
||
vx, vy = self._ele.locations.viewport_midpoint
|
||
else:
|
||
vx, vy = self._ele.locations.viewport_click_point
|
||
|
||
except CDPError:
|
||
vx, vy = self._ele.locations.viewport_midpoint
|
||
|
||
self._click(vx, vy)
|
||
return True
|
||
|
||
if by_js is not False:
|
||
self._ele.run_js('this.click();')
|
||
return True
|
||
if Settings.raise_when_click_failed:
|
||
raise CanNotClickError
|
||
return False
|
||
|
||
def right(self):
|
||
"""右键单击"""
|
||
self._ele.page.scroll.to_see(self._ele)
|
||
x, y = self._ele.locations.viewport_click_point
|
||
self._click(x, y, 'right')
|
||
|
||
def middle(self):
|
||
"""中键单击"""
|
||
self._ele.page.scroll.to_see(self._ele)
|
||
x, y = self._ele.locations.viewport_click_point
|
||
self._click(x, y, 'middle')
|
||
|
||
def at(self, offset_x=None, offset_y=None, button='left', count=1):
|
||
"""带偏移量点击本元素,相对于左上角坐标。不传入x或y值时点击元素中间点
|
||
:param offset_x: 相对元素左上角坐标的x轴偏移量
|
||
:param offset_y: 相对元素左上角坐标的y轴偏移量
|
||
:param button: 点击哪个键,可选 left, middle, right, back, forward
|
||
:param count: 点击次数
|
||
:return: None
|
||
"""
|
||
self._ele.page.scroll.to_see(self._ele)
|
||
if offset_x is None and offset_y is None:
|
||
w, h = self._ele.size
|
||
offset_x = w // 2
|
||
offset_y = h // 2
|
||
x, y = offset_scroll(self._ele, offset_x, offset_y)
|
||
self._click(x, y, button, count)
|
||
|
||
def twice(self):
|
||
"""双击元素"""
|
||
self.at(count=2)
|
||
|
||
def _click(self, client_x, client_y, button='left', count=1):
|
||
"""实施点击
|
||
:param client_x: 视口中的x坐标
|
||
:param client_y: 视口中的y坐标
|
||
:param button: 'left' 'right' 'middle' 'back' 'forward'
|
||
:param count: 点击次数
|
||
:return: None
|
||
"""
|
||
self._ele.page.run_cdp('Input.dispatchMouseEvent', type='mousePressed',
|
||
x=client_x, y=client_y, button=button, clickCount=count)
|
||
# sleep(.05)
|
||
self._ele.page.run_cdp('Input.dispatchMouseEvent', type='mouseReleased',
|
||
x=client_x, y=client_y, button=button)
|