mirror of
https://gitee.com/g1879/DrissionPage.git
synced 2024-12-10 04:00:23 +08:00
优化点击逻辑
This commit is contained in:
parent
3dfbfb957f
commit
c6273d9bf2
@ -16,6 +16,7 @@ from .._commons.tools import get_usable_path
|
||||
from .._commons.web import make_absolute_link, get_ele_txt, format_html, is_js_func, offset_scroll
|
||||
from .._units.clicker import Clicker
|
||||
from .._units.element_states import ChromiumElementStates, ShadowRootStates
|
||||
from .._units.locations import Locations
|
||||
from .._units.select_element import SelectElement
|
||||
from .._units.setter import ChromiumElementSetter
|
||||
from .._units.waiter import ChromiumElementWaiter
|
||||
@ -1438,103 +1439,6 @@ def send_key(ele, modifier, key):
|
||||
ele.page.run_cdp('Input.dispatchKeyEvent', **data)
|
||||
|
||||
|
||||
class Locations(object):
|
||||
def __init__(self, ele):
|
||||
"""
|
||||
:param ele: ChromiumElement
|
||||
"""
|
||||
self._ele = ele
|
||||
|
||||
@property
|
||||
def location(self):
|
||||
"""返回元素左上角的绝对坐标"""
|
||||
cl = self.viewport_location
|
||||
return self._get_page_coord(cl[0], cl[1])
|
||||
|
||||
@property
|
||||
def midpoint(self):
|
||||
"""返回元素中间点的绝对坐标"""
|
||||
cl = self.viewport_midpoint
|
||||
return self._get_page_coord(cl[0], cl[1])
|
||||
|
||||
@property
|
||||
def click_point(self):
|
||||
"""返回元素接受点击的点的绝对坐标"""
|
||||
cl = self.viewport_click_point
|
||||
return self._get_page_coord(cl[0], cl[1])
|
||||
|
||||
@property
|
||||
def viewport_location(self):
|
||||
"""返回元素左上角在视口中的坐标"""
|
||||
m = self._get_viewport_rect('border')
|
||||
return int(m[0]), int(m[1])
|
||||
|
||||
@property
|
||||
def viewport_midpoint(self):
|
||||
"""返回元素中间点在视口中的坐标"""
|
||||
m = self._get_viewport_rect('border')
|
||||
return int(m[0] + (m[2] - m[0]) // 2), int(m[3] + (m[5] - m[3]) // 2)
|
||||
|
||||
@property
|
||||
def viewport_click_point(self):
|
||||
"""返回元素接受点击的点视口坐标"""
|
||||
m = self._get_viewport_rect('padding')
|
||||
return int(self.viewport_midpoint[0]), int(m[1]) + 3
|
||||
|
||||
@property
|
||||
def screen_location(self):
|
||||
"""返回元素左上角在屏幕上坐标,左上角为(0, 0)"""
|
||||
vx, vy = self._ele.page.rect.viewport_location
|
||||
ex, ey = self.viewport_location
|
||||
pr = self._ele.page.run_js('return window.devicePixelRatio;')
|
||||
return int((vx + ex) * pr), int((ey + vy) * pr)
|
||||
|
||||
@property
|
||||
def screen_midpoint(self):
|
||||
"""返回元素中点在屏幕上坐标,左上角为(0, 0)"""
|
||||
vx, vy = self._ele.page.rect.viewport_location
|
||||
ex, ey = self.viewport_midpoint
|
||||
pr = self._ele.page.run_js('return window.devicePixelRatio;')
|
||||
return int((vx + ex) * pr), int((ey + vy) * pr)
|
||||
|
||||
@property
|
||||
def screen_click_point(self):
|
||||
"""返回元素中点在屏幕上坐标,左上角为(0, 0)"""
|
||||
vx, vy = self._ele.page.rect.viewport_location
|
||||
ex, ey = self.viewport_click_point
|
||||
pr = self._ele.page.run_js('return window.devicePixelRatio;')
|
||||
return int((vx + ex) * pr), int((ey + vy) * pr)
|
||||
|
||||
@property
|
||||
def rect(self):
|
||||
"""返回元素四个角坐标,顺序:坐上、右上、右下、左下,没有大小的元素抛出NoRectError"""
|
||||
vr = self._get_viewport_rect('border')
|
||||
r = self._ele.page.run_cdp_loaded('Page.getLayoutMetrics')['visualViewport']
|
||||
sx = r['pageX']
|
||||
sy = r['pageY']
|
||||
return [(vr[0] + sx, vr[1] + sy), (vr[2] + sx, vr[3] + sy), (vr[4] + sx, vr[5] + sy), (vr[6] + sx, vr[7] + sy)]
|
||||
|
||||
@property
|
||||
def viewport_rect(self):
|
||||
"""返回元素四个角视口坐标,顺序:坐上、右上、右下、左下,没有大小的元素抛出NoRectError"""
|
||||
r = self._get_viewport_rect('border')
|
||||
return [(r[0], r[1]), (r[2], r[3]), (r[4], r[5]), (r[6], r[7])]
|
||||
|
||||
def _get_viewport_rect(self, quad):
|
||||
"""按照类型返回在可视窗口中的范围
|
||||
:param quad: 方框类型,margin border padding
|
||||
:return: 四个角坐标
|
||||
"""
|
||||
return self._ele.page.run_cdp('DOM.getBoxModel', backendNodeId=self._ele.ids.backend_id)['model'][quad]
|
||||
|
||||
def _get_page_coord(self, x, y):
|
||||
"""根据视口坐标获取绝对坐标"""
|
||||
r = self._ele.page.run_cdp_loaded('Page.getLayoutMetrics')['visualViewport']
|
||||
sx = r['pageX']
|
||||
sy = r['pageY']
|
||||
return x + sx, y + sy
|
||||
|
||||
|
||||
class ChromiumScroll(object):
|
||||
"""用于滚动的对象"""
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
@Contact : g1879@qq.com
|
||||
"""
|
||||
from pathlib import Path
|
||||
from typing import Union, Tuple, List, Any, Optional
|
||||
from typing import Union, Tuple, List, Any
|
||||
|
||||
from .._base.base import DrissionElement, BaseElement
|
||||
from .._commons.constants import NoneElement
|
||||
@ -15,6 +15,7 @@ from .._pages.chromium_page import ChromiumPage
|
||||
from .._pages.web_page import WebPage
|
||||
from .._units.clicker import Clicker
|
||||
from .._units.element_states import ShadowRootStates, ChromiumElementStates
|
||||
from .._units.locations import Locations
|
||||
from .._units.select_element import SelectElement
|
||||
from .._units.setter import ChromiumElementSetter
|
||||
from .._units.waiter import ChromiumElementWaiter
|
||||
@ -363,48 +364,6 @@ def send_enter(ele: ChromiumElement) -> None: ...
|
||||
def send_key(ele: ChromiumElement, modifier: int, key: str) -> None: ...
|
||||
|
||||
|
||||
class Locations(object):
|
||||
def __init__(self, ele: ChromiumElement):
|
||||
self._ele: ChromiumElement = ...
|
||||
|
||||
@property
|
||||
def location(self) -> Tuple[int, int]: ...
|
||||
|
||||
@property
|
||||
def midpoint(self) -> Tuple[int, int]: ...
|
||||
|
||||
@property
|
||||
def click_point(self) -> Tuple[int, int]: ...
|
||||
|
||||
@property
|
||||
def viewport_location(self) -> Tuple[int, int]: ...
|
||||
|
||||
@property
|
||||
def viewport_midpoint(self) -> Tuple[int, int]: ...
|
||||
|
||||
@property
|
||||
def viewport_click_point(self) -> Tuple[int, int]: ...
|
||||
|
||||
@property
|
||||
def screen_location(self) -> Tuple[int, int]: ...
|
||||
|
||||
@property
|
||||
def screen_midpoint(self) -> Tuple[int, int]: ...
|
||||
|
||||
@property
|
||||
def screen_click_point(self) -> Tuple[int, int]: ...
|
||||
|
||||
@property
|
||||
def rect(self) -> list: ...
|
||||
|
||||
@property
|
||||
def viewport_rect(self) -> list: ...
|
||||
|
||||
def _get_viewport_rect(self, quad: str) -> Union[list, None]: ...
|
||||
|
||||
def _get_page_coord(self, x: int, y: int) -> Tuple[int, int]: ...
|
||||
|
||||
|
||||
class ChromiumScroll(object):
|
||||
def __init__(self, page_or_ele: Union[ChromiumBase, ChromiumElement, ChromiumFrame]):
|
||||
self.t1: str = ...
|
||||
|
@ -103,6 +103,11 @@ class ChromiumBase(BasePage):
|
||||
self._driver.call_method('Page.enable')
|
||||
self._driver.call_method('Emulation.setFocusEmulationEnabled', enabled=True)
|
||||
|
||||
r = self.run_cdp('Page.getFrameTree')
|
||||
for i in findall(r"'id': '(.*?)'", str(r)):
|
||||
self.browser._frames[i] = self.tab_id
|
||||
self._frame_id = r['frameTree']['frame']['id']
|
||||
|
||||
self._driver.set_listener('Page.frameStartedLoading', self._onFrameStartedLoading)
|
||||
self._driver.set_listener('Page.frameNavigated', self._onFrameNavigated)
|
||||
self._driver.set_listener('Page.domContentEventFired', self._onDomContentEventFired)
|
||||
@ -111,10 +116,6 @@ class ChromiumBase(BasePage):
|
||||
self._driver.set_listener('Page.frameAttached', self._onFrameAttached)
|
||||
self._driver.set_listener('Page.frameDetached', self._onFrameDetached)
|
||||
|
||||
r = self.run_cdp('Page.getFrameTree')
|
||||
for i in findall(r"'id': '(.*?)'", str(r)):
|
||||
self.browser._frames[i] = self.tab_id
|
||||
self._frame_id = r['frameTree']['frame']['id']
|
||||
|
||||
def _get_document(self):
|
||||
if self._is_reading:
|
||||
|
@ -7,7 +7,7 @@ from time import perf_counter, sleep
|
||||
|
||||
from .._commons.constants import Settings
|
||||
from .._commons.web import offset_scroll
|
||||
from ..errors import CanNotClickError, CDPError
|
||||
from ..errors import CanNotClickError, CDPError, NoRectError
|
||||
|
||||
|
||||
class Clicker(object):
|
||||
@ -26,7 +26,7 @@ class Clicker(object):
|
||||
"""
|
||||
return self.left(by_js, timeout)
|
||||
|
||||
def left(self, by_js=False, timeout=1):
|
||||
def left(self, by_js=False, timeout=2):
|
||||
"""点击元素,可选择是否用js点击
|
||||
:param by_js: 是否用js点击,为None时先用模拟点击,遇到遮挡改用js,为True时直接用js点击,为False时只用模拟点击
|
||||
:param timeout: 模拟点击的超时时间,等待元素可见、不被遮挡、进入视口
|
||||
@ -35,34 +35,48 @@ class Clicker(object):
|
||||
if not by_js: # 模拟点击
|
||||
can_click = False
|
||||
timeout = self._ele.page.timeout if timeout is None else timeout
|
||||
if timeout == 0 and self._ele.states.has_rect:
|
||||
self._ele.scroll.to_see()
|
||||
if self._ele.states.is_in_viewport and self._ele.states.is_enabled and self._ele.states.is_displayed:
|
||||
can_click = True
|
||||
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 self._ele.states.has_rect and perf_counter() < end_time:
|
||||
while not rect and perf_counter() < end_time:
|
||||
rect = self._ele.states.has_rect
|
||||
sleep(.001)
|
||||
if self._ele.states.has_rect:
|
||||
|
||||
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_in_viewport and self._ele.states.is_enabled
|
||||
and self._ele.states.is_displayed):
|
||||
if self._ele.states.is_enabled and self._ele.states.is_displayed:
|
||||
can_click = True
|
||||
break
|
||||
sleep(.001)
|
||||
|
||||
if not self._ele.states.has_rect or not self._ele.states.is_in_viewport:
|
||||
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):
|
||||
vx, vy = self._ele.locations.click_point
|
||||
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=vx, y=vy, includeUserAgentShadowDOM=True,
|
||||
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_click_point
|
||||
vx, vy = self._ele.locations.viewport_midpoint
|
||||
else:
|
||||
vx, vy = self._ele.locations.viewport_click_point
|
||||
|
||||
|
@ -1,4 +1,8 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
"""
|
||||
@Author : g1879
|
||||
@Contact : g1879@qq.com
|
||||
"""
|
||||
from .._commons.web import location_in_viewport
|
||||
from ..errors import CDPError, NoRectError
|
||||
|
||||
@ -71,9 +75,9 @@ class ChromiumElementStates(object):
|
||||
|
||||
@property
|
||||
def has_rect(self):
|
||||
"""返回元素是否拥有位置和大小,没有返回False,有返回大小元组"""
|
||||
"""返回元素是否拥有位置和大小,没有返回False,有返回四个角在页面中坐标组成的列表"""
|
||||
try:
|
||||
return self._ele.size
|
||||
return self._ele.locations.rect
|
||||
except NoRectError:
|
||||
return False
|
||||
|
||||
|
@ -1,5 +1,9 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
from typing import Union, Tuple
|
||||
"""
|
||||
@Author : g1879
|
||||
@Contact : g1879@qq.com
|
||||
"""
|
||||
from typing import Union, Tuple, List
|
||||
|
||||
from .._elements.chromium_element import ChromiumShadowRoot, ChromiumElement
|
||||
|
||||
@ -33,7 +37,7 @@ class ChromiumElementStates(object):
|
||||
def is_covered(self) -> bool: ...
|
||||
|
||||
@property
|
||||
def has_rect(self) -> Union[bool, Tuple[int, int]]: ...
|
||||
def has_rect(self) -> Union[bool, List[Tuple[float, float]]]: ...
|
||||
|
||||
|
||||
class ShadowRootStates(object):
|
||||
|
103
DrissionPage/_units/locations.py
Normal file
103
DrissionPage/_units/locations.py
Normal file
@ -0,0 +1,103 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
"""
|
||||
@Author : g1879
|
||||
@Contact : g1879@qq.com
|
||||
"""
|
||||
|
||||
|
||||
class Locations(object):
|
||||
def __init__(self, ele):
|
||||
"""
|
||||
:param ele: ChromiumElement
|
||||
"""
|
||||
self._ele = ele
|
||||
|
||||
@property
|
||||
def location(self):
|
||||
"""返回元素左上角的绝对坐标"""
|
||||
cl = self.viewport_location
|
||||
return self._get_page_coord(cl[0], cl[1])
|
||||
|
||||
@property
|
||||
def midpoint(self):
|
||||
"""返回元素中间点的绝对坐标"""
|
||||
cl = self.viewport_midpoint
|
||||
return self._get_page_coord(cl[0], cl[1])
|
||||
|
||||
@property
|
||||
def click_point(self):
|
||||
"""返回元素接受点击的点的绝对坐标"""
|
||||
cl = self.viewport_click_point
|
||||
return self._get_page_coord(cl[0], cl[1])
|
||||
|
||||
@property
|
||||
def viewport_location(self):
|
||||
"""返回元素左上角在视口中的坐标"""
|
||||
m = self._get_viewport_rect('border')
|
||||
return int(m[0]), int(m[1])
|
||||
|
||||
@property
|
||||
def viewport_midpoint(self):
|
||||
"""返回元素中间点在视口中的坐标"""
|
||||
m = self._get_viewport_rect('border')
|
||||
return int(m[0] + (m[2] - m[0]) // 2), int(m[3] + (m[5] - m[3]) // 2)
|
||||
|
||||
@property
|
||||
def viewport_click_point(self):
|
||||
"""返回元素接受点击的点视口坐标"""
|
||||
m = self._get_viewport_rect('padding')
|
||||
return int(self.viewport_midpoint[0]), int(m[1]) + 3
|
||||
|
||||
@property
|
||||
def screen_location(self):
|
||||
"""返回元素左上角在屏幕上坐标,左上角为(0, 0)"""
|
||||
vx, vy = self._ele.page.rect.viewport_location
|
||||
ex, ey = self.viewport_location
|
||||
pr = self._ele.page.run_js('return window.devicePixelRatio;')
|
||||
return int((vx + ex) * pr), int((ey + vy) * pr)
|
||||
|
||||
@property
|
||||
def screen_midpoint(self):
|
||||
"""返回元素中点在屏幕上坐标,左上角为(0, 0)"""
|
||||
vx, vy = self._ele.page.rect.viewport_location
|
||||
ex, ey = self.viewport_midpoint
|
||||
pr = self._ele.page.run_js('return window.devicePixelRatio;')
|
||||
return int((vx + ex) * pr), int((ey + vy) * pr)
|
||||
|
||||
@property
|
||||
def screen_click_point(self):
|
||||
"""返回元素中点在屏幕上坐标,左上角为(0, 0)"""
|
||||
vx, vy = self._ele.page.rect.viewport_location
|
||||
ex, ey = self.viewport_click_point
|
||||
pr = self._ele.page.run_js('return window.devicePixelRatio;')
|
||||
return int((vx + ex) * pr), int((ey + vy) * pr)
|
||||
|
||||
@property
|
||||
def rect(self):
|
||||
"""返回元素四个角坐标,顺序:坐上、右上、右下、左下,没有大小的元素抛出NoRectError"""
|
||||
vr = self._get_viewport_rect('border')
|
||||
r = self._ele.page.run_cdp_loaded('Page.getLayoutMetrics')['visualViewport']
|
||||
sx = int(r['pageX'])
|
||||
sy = int(r['pageY'])
|
||||
return [(vr[0] + sx, vr[1] + sy), (vr[2] + sx, vr[3] + sy),
|
||||
(vr[4] + sx, vr[5] + sy), (vr[6] + sx, vr[7] + sy)]
|
||||
|
||||
@property
|
||||
def viewport_rect(self):
|
||||
"""返回元素四个角视口坐标,顺序:坐上、右上、右下、左下,没有大小的元素抛出NoRectError"""
|
||||
r = self._get_viewport_rect('border')
|
||||
return [(int(r[0]), int(r[1])), (int(r[2]), int(r[3])), (int(r[4]), int(r[5])), (int(r[6]), int(r[7]))]
|
||||
|
||||
def _get_viewport_rect(self, quad):
|
||||
"""按照类型返回在可视窗口中的范围
|
||||
:param quad: 方框类型,margin border padding
|
||||
:return: 四个角坐标
|
||||
"""
|
||||
return self._ele.page.run_cdp('DOM.getBoxModel', backendNodeId=self._ele.ids.backend_id)['model'][quad]
|
||||
|
||||
def _get_page_coord(self, x, y):
|
||||
"""根据视口坐标获取绝对坐标"""
|
||||
r = self._ele.page.run_cdp_loaded('Page.getLayoutMetrics')['visualViewport']
|
||||
sx = r['pageX']
|
||||
sy = r['pageY']
|
||||
return x + sx, y + sy
|
50
DrissionPage/_units/locations.pyi
Normal file
50
DrissionPage/_units/locations.pyi
Normal file
@ -0,0 +1,50 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
"""
|
||||
@Author : g1879
|
||||
@Contact : g1879@qq.com
|
||||
"""
|
||||
from typing import Tuple, Union, List
|
||||
|
||||
from .._elements.chromium_element import ChromiumElement
|
||||
|
||||
|
||||
class Locations(object):
|
||||
def __init__(self, ele: ChromiumElement):
|
||||
self._ele: ChromiumElement = ...
|
||||
|
||||
@property
|
||||
def location(self) -> Tuple[int, int]: ...
|
||||
|
||||
@property
|
||||
def midpoint(self) -> Tuple[int, int]: ...
|
||||
|
||||
@property
|
||||
def click_point(self) -> Tuple[int, int]: ...
|
||||
|
||||
@property
|
||||
def viewport_location(self) -> Tuple[int, int]: ...
|
||||
|
||||
@property
|
||||
def viewport_midpoint(self) -> Tuple[int, int]: ...
|
||||
|
||||
@property
|
||||
def viewport_click_point(self) -> Tuple[int, int]: ...
|
||||
|
||||
@property
|
||||
def screen_location(self) -> Tuple[int, int]: ...
|
||||
|
||||
@property
|
||||
def screen_midpoint(self) -> Tuple[int, int]: ...
|
||||
|
||||
@property
|
||||
def screen_click_point(self) -> Tuple[int, int]: ...
|
||||
|
||||
@property
|
||||
def rect(self) -> List[Tuple[int, int], ...]: ...
|
||||
|
||||
@property
|
||||
def viewport_rect(self) -> List[Tuple[int, int], ...]: ...
|
||||
|
||||
def _get_viewport_rect(self, quad: str) -> Union[list, None]: ...
|
||||
|
||||
def _get_page_coord(self, x: int, y: int) -> Tuple[int, int]: ...
|
Loading…
x
Reference in New Issue
Block a user