size、locations等合并到rect属性

This commit is contained in:
g1879 2023-11-16 22:33:21 +08:00
parent a089bcbffc
commit dbdb4528ab
21 changed files with 297 additions and 300 deletions

View File

@ -12,4 +12,5 @@ from ._pages.web_page import WebPage
from ._configs.chromium_options import ChromiumOptions
from ._configs.session_options import SessionOptions
__all__ = ['ChromiumPage', 'ChromiumOptions', 'SessionOptions', 'SessionPage', 'WebPage']
__all__ = ['ChromiumPage', 'ChromiumOptions', 'SessionOptions', 'SessionPage', 'WebPage', '__version__']
__version__ = '4.0.0b8'

View File

@ -170,7 +170,10 @@ class Browser(object):
end_time = perf_counter() + timeout
while perf_counter() < end_time:
p = popen(txt)
if f' {self.process_id} ' not in p.read():
try:
if f' {self.process_id} ' not in p.read():
return
except TypeError:
return
sleep(.2)

View File

@ -16,7 +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.ids import ShadowRootIds, ElementIds
from .._units.locations import Locations
from .._units.rect import ElementRect
from .._units.scroller import ElementScroller
from .._units.select_element import SelectElement
from .._units.setter import ChromiumElementSetter
@ -39,7 +39,7 @@ class ChromiumElement(DrissionElement):
super().__init__(page)
self._select = None
self._scroll = None
self._locations = None
self._rect = None
self._set = None
self._states = None
self._pseudo = None
@ -126,12 +126,6 @@ class ChromiumElement(DrissionElement):
"""返回获取内置id的对象"""
return self._ids
@property
def size(self):
"""返回元素宽和高组成的元组"""
border = self.page.run_cdp('DOM.getBoxModel', backendNodeId=self._backend_id)['model']['border']
return border[2] - border[0], border[5] - border[1]
@property
def set(self):
"""返回用于设置元素属性的对象"""
@ -154,16 +148,11 @@ class ChromiumElement(DrissionElement):
return self._pseudo
@property
def location(self):
"""返回元素左上角的绝对坐标"""
return self.locations.location
@property
def locations(self):
def rect(self):
"""返回用于获取元素位置的对象"""
if self._locations is None:
self._locations = Locations(self)
return self._locations
if self._rect is None:
self._rect = ElementRect(self)
return self._rect
@property
def shadow_root(self):
@ -565,8 +554,8 @@ class ChromiumElement(DrissionElement):
if scroll_to_center:
self.scroll.to_see(center=True)
left, top = self.location
width, height = self.size
left, top = self.rect.location
width, height = self.rect.size
left_top = (left, top)
right_bottom = (left + width, top + height)
if not name:
@ -657,7 +646,7 @@ class ChromiumElement(DrissionElement):
:param duration: 拖动用时传入0即瞬间到j达
:return: None
"""
curr_x, curr_y = self.locations.midpoint
curr_x, curr_y = self.rect.midpoint
offset_x += curr_x
offset_y += curr_y
self.drag_to((offset_x, offset_y), duration)
@ -669,7 +658,7 @@ class ChromiumElement(DrissionElement):
:return: None
"""
if isinstance(ele_or_loc, ChromiumElement):
ele_or_loc = ele_or_loc.locations.midpoint
ele_or_loc = ele_or_loc.rect.midpoint
elif not isinstance(ele_or_loc, (list, tuple)):
raise TypeError('需要ChromiumElement对象或坐标。')
@ -754,6 +743,18 @@ class ChromiumElement(DrissionElement):
files = [str(Path(i).absolute()) for i in files]
self.page.run_cdp('DOM.setFileInputFiles', files=files, backendNodeId=self._backend_id)
# -------------即将废弃-------------
@property
def location(self):
"""返回元素左上角的绝对坐标"""
return self.rect.location
@property
def size(self):
"""返回元素宽和高组成的元组"""
return self.rect.size
class ChromiumShadowRoot(BaseElement):
"""ChromiumShadowRoot是用于处理ShadowRoot的类使用方法和ChromiumElement基本一致"""

View File

@ -15,7 +15,7 @@ from .._pages.chromium_page import ChromiumPage
from .._pages.web_page import WebPage
from .._units.clicker import Clicker
from .._units.ids import ElementIds, ShadowRootIds
from .._units.locations import Locations
from .._units.rect import ElementRect
from .._units.scroller import ElementScroller
from .._units.select_element import SelectElement
from .._units.setter import ChromiumElementSetter
@ -37,7 +37,7 @@ class ChromiumElement(DrissionElement):
self._clicker: Clicker = ...
self._select: SelectElement = ...
self._wait: ElementWaiter = ...
self._locations: Locations = ...
self._rect: ElementRect = ...
self._set: ChromiumElementSetter = ...
self._states: ElementStates = ...
self._pseudo: Pseudo = ...
@ -69,9 +69,6 @@ class ChromiumElement(DrissionElement):
@property
def ids(self) -> ElementIds: ...
@property
def size(self) -> Tuple[float, float]: ...
@property
def set(self) -> ChromiumElementSetter: ...
@ -79,10 +76,7 @@ class ChromiumElement(DrissionElement):
def states(self) -> ElementStates: ...
@property
def location(self) -> Tuple[float, float]: ...
@property
def locations(self) -> Locations: ...
def rect(self) -> ElementRect: ...
@property
def pseudo(self) -> Pseudo: ...

View File

@ -10,6 +10,7 @@ from re import findall
from threading import Thread
from time import perf_counter, sleep
from .._units.rect import TabRect
from .._base.base import BasePage
from .._commons.constants import ERROR, NoneElement
from .._commons.locator import get_loc
@ -48,6 +49,7 @@ class ChromiumBase(BasePage):
self._states = None
self._has_alert = False
self._ready_state = None
self._rect = None
self._doc_got = False # 用于在LoadEventFired和FrameStoppedLoading间标记是否已获取doc
self._download_path = str(Path('.').absolute())
@ -316,6 +318,14 @@ class ChromiumBase(BasePage):
self._scroll = PageScroller(self)
return self._scroll
@property
def rect(self):
"""返回获取窗口坐标和大小的对象"""
# self.wait.load_complete()
if self._rect is None:
self._rect = TabRect(self)
return self._rect
@property
def timeouts(self):
"""返回timeouts设置"""
@ -373,12 +383,6 @@ class ChromiumBase(BasePage):
"""返回当前标签页id"""
return self.driver.id if not self.driver._stopped.is_set() else ''
@property
def size(self):
"""返回页面总宽高,格式:(宽, 高)"""
r = self.run_cdp_loaded('Page.getLayoutMetrics')['contentSize']
return r['width'], r['height']
@property
def active_ele(self):
"""返回当前焦点所在元素"""
@ -973,7 +977,7 @@ class ChromiumBase(BasePage):
pic_type = path.suffix.lower()
pic_type = 'jpeg' if pic_type == '.jpg' else pic_type[1:]
width, height = self.size
width, height = self.rect.size
if full_page:
vp = {'x': 0, 'y': 0, 'width': width, 'height': height, 'scale': 1}
png = self.run_cdp_loaded('Page.captureScreenshot', format=pic_type,
@ -1030,6 +1034,11 @@ class ChromiumBase(BasePage):
def ready_state(self):
return self._ready_state
@property
def size(self):
"""返回页面总宽高,格式:(宽, 高)"""
return self.rect.size
class Timeout(object):
"""用于保存d模式timeout信息的类"""

View File

@ -6,6 +6,7 @@
from pathlib import Path
from typing import Union, Tuple, List, Any, Optional
from .._units.rect import TabRect
from .._base.base import BasePage
from .._base.browser import Browser
from .._base.chromium_driver import ChromiumDriver
@ -54,6 +55,7 @@ class ChromiumBase(BasePage):
self._has_alert: bool = ...
self._doc_got: bool = ...
self._ready_state: Optional[str] = ...
self._rect: TabRect = ...
def _connect_browser(self, tab_id: str = None) -> None: ...
@ -118,9 +120,6 @@ class ChromiumBase(BasePage):
@property
def tab_id(self) -> str: ...
@property
def size(self) -> Tuple[int, int]: ...
@property
def active_ele(self) -> ChromiumElement: ...
@ -133,6 +132,9 @@ class ChromiumBase(BasePage):
@property
def scroll(self) -> PageScroller: ...
@property
def rect(self) -> TabRect: ...
@property
def timeouts(self) -> Timeout: ...
@ -216,8 +218,8 @@ class ChromiumBase(BasePage):
left_top: Tuple[int, int] = None, right_bottom: Tuple[int, int] = None) -> Union[str, bytes]: ...
def _get_screenshot(self, path: [str, Path] = None, name: str = None, as_bytes: [bool, str] = None,
as_base64: [bool, str] = None, full_page: bool = False, left_top: Tuple[int, int] = None,
right_bottom: Tuple[int, int] = None, ele: ChromiumElement = None) -> Union[str, bytes]: ...
as_base64: [bool, str] = None, full_page: bool = False, left_top: Tuple[float, float] = None,
right_bottom: Tuple[float, float] = None, ele: ChromiumElement = None) -> Union[str, bytes]: ...
def clear_cache(self, session_storage: bool = True, local_storage: bool = True, cache: bool = True,
cookies: bool = True) -> None: ...

View File

@ -356,33 +356,11 @@ class ChromiumFrame(ChromiumBase):
"""返回frame元素所有attribute属性"""
return self.frame_ele.attrs
@property
def page_size(self):
"""返回frame内页面尺寸格式(长, 高)"""
w = self.doc_ele.run_js('return this.body.scrollWidth')
h = self.doc_ele.run_js('return this.body.scrollHeight')
return w, h
@property
def size(self):
"""返回frame元素大小"""
return self.frame_ele.size
@property
def active_ele(self):
"""返回当前焦点所在元素"""
return self.doc_ele.run_js('return this.activeElement;')
@property
def location(self):
"""返回frame元素左上角的绝对坐标"""
return self.frame_ele.location
@property
def locations(self):
"""返回用于获取元素位置的对象"""
return self.frame_ele.locations
@property
def xpath(self):
"""返回frame的xpath绝对路径"""
@ -597,8 +575,8 @@ class ChromiumFrame(ChromiumBase):
self.frame_ele.scroll.to_see(center=True)
self.scroll.to_see(ele, center=True)
cx, cy = ele.locations.viewport_location
w, h = ele.size
cx, cy = ele.rect.viewport_location
w, h = ele.rect.size
img_data = f'data:image/{pic_type};base64,{self.frame_ele.get_screenshot(as_base64=True)}'
body = self.tab('t:body')
first_child = body('c::first-child')
@ -652,3 +630,23 @@ class ChromiumFrame(ChromiumBase):
def is_alive(self):
"""返回是否仍可用"""
return self.states.is_alive
@property
def page_size(self):
"""返回frame内页面尺寸格式(宽,, 高)"""
return self.rect.size
@property
def size(self):
"""返回frame元素大小"""
return self.frame_ele.rect.size
@property
def location(self):
"""返回frame元素左上角的绝对坐标"""
return self.frame_ele.rect.location
@property
def locations(self):
"""返回用于获取元素位置的对象"""
return self.frame_ele.rect

View File

@ -13,7 +13,6 @@ from .web_page import WebPage
from .._elements.chromium_element import ChromiumElement
from .._units.states import FrameStates
from .._units.ids import FrameIds
from .._units.locations import Locations
from .._units.rect import FrameRect
from .._units.scroller import FrameScroller
from .._units.setter import ChromiumFrameSetter
@ -89,24 +88,12 @@ class ChromiumFrame(ChromiumBase):
@property
def attrs(self) -> dict: ...
@property
def page_size(self) -> Tuple[int, int]: ...
@property
def size(self) -> Tuple[int, int]: ...
@property
def rect(self) -> FrameRect: ...
@property
def active_ele(self) -> ChromiumElement: ...
@property
def location(self) -> Tuple[int, int]: ...
@property
def locations(self) -> Locations: ...
@property
def xpath(self) -> str: ...

View File

@ -13,7 +13,6 @@ from .._commons.browser import connect_browser
from .._configs.chromium_options import ChromiumOptions
from .._pages.chromium_base import ChromiumBase, Timeout
from .._pages.chromium_tab import ChromiumTab
from .._units.rect import TabRect
from .._units.setter import ChromiumPageSetter
from .._units.waiter import PageWaiter
from ..errors import BrowserConnectError
@ -104,14 +103,6 @@ class ChromiumPage(ChromiumBase):
self._set = ChromiumPageSetter(self)
return self._set
@property
def rect(self):
"""返回保存窗口方位信息的对象"""
self.wait.load_complete()
if self._rect is None:
self._rect = TabRect(self)
return self._rect
@property
def wait(self):
"""返回用于等待的对象"""

View File

@ -39,9 +39,6 @@ class ChromiumPage(ChromiumBase):
@property
def tabs(self) -> List[str]: ...
@property
def rect(self) -> TabRect: ...
@property
def wait(self) -> PageWaiter: ...

View File

@ -9,7 +9,6 @@ from .._base.base import BasePage
from .._commons.web import set_session_cookies, set_browser_cookies
from .._pages.chromium_base import ChromiumBase
from .._pages.session_page import SessionPage
from .._units.rect import TabRect
from .._units.setter import TabSetter, WebPageTabSetter
from .._units.waiter import TabWaiter
@ -44,13 +43,6 @@ class ChromiumTab(ChromiumBase):
"""返回总体page对象"""
return self._page
@property
def rect(self):
"""返回获取窗口坐标和大小的对象"""
if self._rect is None:
self._rect = TabRect(self)
return self._rect
@property
def set(self):
"""返回用于等待的对象"""

View File

@ -34,9 +34,6 @@ class ChromiumTab(ChromiumBase):
@property
def page(self) -> ChromiumPage: ...
@property
def rect(self) -> TabRect: ...
@property
def set(self) -> TabSetter: ...

View File

@ -49,6 +49,13 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
elif self._mode == 's':
return super().__call__(loc_or_str)
@property
def set(self):
"""返回用于等待的对象"""
if self._set is None:
self._set = WebPageSetter(self)
return self._set
@property
def url(self):
"""返回当前url"""
@ -134,13 +141,6 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
"""
self.set.timeouts(implicit=second)
@property
def set(self):
"""返回用于等待的对象"""
if self._set is None:
self._set = WebPageSetter(self)
return self._set
def get(self, url, show_errmsg=False, retry=None, interval=None, timeout=None, **kwargs):
"""跳转到一个url
:param url: 目标url

View File

@ -39,7 +39,7 @@ class ActionChains:
elif isinstance(ele_or_loc, str) or 'ChromiumElement' in str(type(ele_or_loc)):
ele_or_loc = self.page(ele_or_loc)
self.page.scroll.to_see(ele_or_loc)
x, y = ele_or_loc.location if offset_x or offset_y else ele_or_loc.locations.midpoint
x, y = ele_or_loc.rect.location if offset_x or offset_y else ele_or_loc.rect.midpoint
lx = x + offset_x
ly = y + offset_y
else:
@ -55,8 +55,8 @@ class ActionChains:
if is_loc:
cx, cy = location_to_client(self.page, lx, ly)
else:
x, y = ele_or_loc.locations.viewport_location if offset_x or offset_y \
else ele_or_loc.locations.viewport_midpoint
x, y = ele_or_loc.rect.viewport_location if offset_x or offset_y \
else ele_or_loc.rect.viewport_midpoint
cx = x + offset_x
cy = y + offset_y

View File

@ -42,7 +42,7 @@ class Clicker(object):
try:
self._ele.scroll.to_see()
if self._ele.states.is_enabled and self._ele.states.is_displayed:
rect = self._ele.locations.viewport_rect
rect = self._ele.rect.viewport_corners
can_click = True
except NoRectError:
if by_js is False:
@ -59,7 +59,7 @@ class Clicker(object):
self._ele.wait.stop_moving(timeout=end_time - perf_counter())
if rect:
self._ele.scroll.to_see()
rect = self._ele.locations.rect
rect = self._ele.rect.corners
while perf_counter() < end_time:
if self._ele.states.is_enabled and self._ele.states.is_displayed:
can_click = True
@ -79,12 +79,12 @@ class Clicker(object):
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
vx, vy = self._ele.rect.viewport_midpoint
else:
vx, vy = self._ele.locations.viewport_click_point
vx, vy = self._ele.rect.viewport_click_point
except CDPError:
vx, vy = self._ele.locations.viewport_midpoint
vx, vy = self._ele.rect.viewport_midpoint
self._click(vx, vy)
return True
@ -99,13 +99,13 @@ class Clicker(object):
def right(self):
"""右键单击"""
self._ele.page.scroll.to_see(self._ele)
x, y = self._ele.locations.viewport_click_point
x, y = self._ele.rect.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
x, y = self._ele.rect.viewport_click_point
self._click(x, y, 'middle')
def at(self, offset_x=None, offset_y=None, button='left', count=1):
@ -118,7 +118,7 @@ class Clicker(object):
"""
self._ele.page.scroll.to_see(self._ele)
if offset_x is None and offset_y is None:
w, h = self._ele.size
w, h = self._ele.rect.size
offset_x = w // 2
offset_y = h // 2
x, y = offset_scroll(self._ele, offset_x, offset_y)

View File

@ -1,103 +0,0 @@
# -*- 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 m[0], m[1]
@property
def viewport_midpoint(self):
"""返回元素中间点在视口中的坐标"""
m = self._get_viewport_rect('border')
return m[0] + (m[2] - m[0]) // 2, m[3] + (m[5] - m[3]) // 2
@property
def viewport_click_point(self):
"""返回元素接受点击的点视口坐标"""
m = self._get_viewport_rect('padding')
return self.viewport_midpoint[0], 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 (vx + ex) * pr, (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 (vx + ex) * pr, (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 (vx + ex) * pr, (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

View File

@ -1,50 +0,0 @@
# -*- 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[float, float]: ...
@property
def midpoint(self) -> Tuple[float, float]: ...
@property
def click_point(self) -> Tuple[float, float]: ...
@property
def viewport_location(self) -> Tuple[float, float]: ...
@property
def viewport_midpoint(self) -> Tuple[float, float]: ...
@property
def viewport_click_point(self) -> Tuple[float, float]: ...
@property
def screen_location(self) -> Tuple[float, float]: ...
@property
def screen_midpoint(self) -> Tuple[float, float]: ...
@property
def screen_click_point(self) -> Tuple[float, float]: ...
@property
def rect(self) -> List[Tuple[float, float], ...]: ...
@property
def viewport_rect(self) -> List[Tuple[float, float], ...]: ...
def _get_viewport_rect(self, quad: str) -> Union[list, None]: ...
def _get_page_coord(self, x: float, y: float) -> Tuple[float, float]: ...

View File

@ -5,6 +5,110 @@
"""
class ElementRect(object):
def __init__(self, ele):
"""
:param ele: ChromiumElement
"""
self._ele = ele
@property
def corners(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_corners(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])]
@property
def size(self):
"""返回元素大小,格式(宽, 高)"""
border = self._ele.page.run_cdp('DOM.getBoxModel', backendNodeId=self._ele._backend_id)['model']['border']
return border[2] - border[0], border[5] - border[1]
@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 m[0], m[1]
@property
def viewport_midpoint(self):
"""返回元素中间点在视口中的坐标"""
m = self._get_viewport_rect('border')
return m[0] + (m[2] - m[0]) // 2, m[3] + (m[5] - m[3]) // 2
@property
def viewport_click_point(self):
"""返回元素接受点击的点视口坐标"""
m = self._get_viewport_rect('padding')
return self.viewport_midpoint[0], 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 (vx + ex) * pr, (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 (vx + ex) * pr, (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 (vx + ex) * pr, (ey + vy) * pr
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 TabRect(object):
def __init__(self, page):
self._page = page
@ -49,7 +153,7 @@ class TabRect(object):
return w_bl + w_bs - w_vs, h_bl + h_bs - h_vs
@property
def page_size(self):
def size(self):
"""返回页面总宽高,格式:(宽, 高)"""
r = self._get_page_rect()['contentSize']
return r['width'], r['height']
@ -83,21 +187,38 @@ class FrameRect(object):
self._frame = frame
@property
def viewport_location(self):
"""返回视口在屏幕中坐标,左上角为(0, 0)"""
return self._frame.frame_ele.locations.screen_location
def location(self):
"""返回元素左上角的绝对坐标"""
return self._frame.frame_ele.rect.location
@property
def page_size(self):
"""返回页面总宽高,格式:(宽, 高)"""
return self._frame.page_size
def viewport_location(self):
"""返回视口在屏幕中坐标,左上角为(0, 0)"""
return self._frame.frame_ele.rect.viewport_location
@property
def screen_location(self):
"""返回元素左上角在屏幕上坐标,左上角为(0, 0)"""
return self._frame.frame_ele.rect.screen_location
@property
def size(self):
"""返回页面总宽高,格式:(宽, 高)"""
return self._frame.size
"""返回frame内页面尺寸格式(宽, 高)"""
w = self._frame.doc_ele.run_js('return this.body.scrollWidth')
h = self._frame.doc_ele.run_js('return this.body.scrollHeight')
return w, h
@property
def viewport_size(self):
"""返回视口宽高,不包括滚动条,格式:(宽, 高)"""
return self._frame.frame_ele.size
"""返回视口宽高,格式:(宽, 高)"""
return self._frame.frame_ele.rect.size
@property
def corners(self):
"""返回元素四个角坐标,顺序:坐上、右上、右下、左下"""
return self._frame.frame_ele.rect.corners
@property
def viewport_corners(self):
"""返回元素四个角视口坐标,顺序:坐上、右上、右下、左下"""
return self._frame.frame_ele.rect.viewport_corners

View File

@ -3,16 +3,64 @@
@Author : g1879
@Contact : g1879@qq.com
"""
from typing import Tuple, Union
from typing import Tuple, Union, List
from .._elements.chromium_element import ChromiumElement
from .._pages.chromium_base import ChromiumBase
from .._pages.chromium_frame import ChromiumFrame
from .._pages.chromium_page import ChromiumPage
from .._pages.chromium_tab import ChromiumTab, WebPageTab
from .._pages.web_page import WebPage
class ElementRect(object):
def __init__(self, ele: ChromiumElement):
self._ele: ChromiumElement = ...
@property
def size(self) -> Tuple[float, float]: ...
@property
def location(self) -> Tuple[float, float]: ...
@property
def midpoint(self) -> Tuple[float, float]: ...
@property
def click_point(self) -> Tuple[float, float]: ...
@property
def viewport_location(self) -> Tuple[float, float]: ...
@property
def viewport_midpoint(self) -> Tuple[float, float]: ...
@property
def viewport_click_point(self) -> Tuple[float, float]: ...
@property
def screen_location(self) -> Tuple[float, float]: ...
@property
def screen_midpoint(self) -> Tuple[float, float]: ...
@property
def screen_click_point(self) -> Tuple[float, float]: ...
@property
def corners(self) -> List[Tuple[float, float], ...]: ...
@property
def viewport_corners(self) -> List[Tuple[float, float], ...]: ...
def _get_viewport_rect(self, quad: str) -> Union[list, None]: ...
def _get_page_coord(self, x: float, y: float) -> Tuple[float, float]: ...
class TabRect(object):
def __init__(self, page: Union[ChromiumPage, ChromiumTab, WebPage, WebPageTab]):
def __init__(self, page: ChromiumBase):
self._page: Union[ChromiumPage, ChromiumTab, WebPage, WebPageTab] = ...
@property
@ -31,7 +79,7 @@ class TabRect(object):
def window_size(self) -> Tuple[int, int]: ...
@property
def page_size(self) -> Tuple[int, int]: ...
def size(self) -> Tuple[int, int]: ...
@property
def viewport_size(self) -> Tuple[int, int]: ...
@ -48,14 +96,23 @@ class FrameRect(object):
def __init__(self, frame: ChromiumFrame):
self._frame: ChromiumFrame = ...
@property
def location(self) -> Tuple[float, float]: ...
@property
def viewport_location(self) -> Tuple[float, float]: ...
@property
def screen_location(self) -> Tuple[float, float]: ...
@property
def size(self) -> Tuple[float, float]: ...
@property
def page_size(self) -> Tuple[float, float]: ...
def viewport_size(self) -> Tuple[float, float]: ...
@property
def viewport_size(self) -> Tuple[float, float]: ...
def corners(self) -> List[Tuple[float, float], ...]: ...
@property
def viewport_corners(self) -> List[Tuple[float, float], ...]: ...

View File

@ -48,21 +48,21 @@ class ElementStates(object):
@property
def is_in_viewport(self):
"""返回元素是否出现在视口中以元素click_point为判断"""
x, y = self._ele.locations.click_point
x, y = self._ele.rect.click_point
return location_in_viewport(self._ele.page, x, y) if x else False
@property
def is_whole_in_viewport(self):
"""返回元素是否整个都在视口内"""
x1, y1 = self._ele.location
w, h = self._ele.size
x1, y1 = self._ele.rect.location
w, h = self._ele.rect.size
x2, y2 = x1 + w, y1 + h
return location_in_viewport(self._ele.page, x1, y1) and location_in_viewport(self._ele.page, x2, y2)
@property
def is_covered(self):
"""返回元素是否被覆盖,与是否在视口中无关"""
lx, ly = self._ele.locations.click_point
lx, ly = self._ele.rect.click_point
try:
r = self._ele.page.run_cdp('DOM.getNodeForLocation', x=lx, y=ly)
except CDPError:
@ -77,7 +77,7 @@ class ElementStates(object):
def has_rect(self):
"""返回元素是否拥有位置和大小没有返回False有返回四个角在页面中坐标组成的列表"""
try:
return self._ele.locations.rect
return self._ele.rect.corners
except NoRectError:
return False

View File

@ -376,7 +376,7 @@ class ElementWaiter(object):
while perf_counter() < end_time:
try:
size = self._ele.states.has_rect
location = self._ele.location
location = self._ele.rect.location
break
except NoRectError:
pass
@ -385,10 +385,10 @@ class ElementWaiter(object):
while perf_counter() < end_time:
sleep(gap)
if self._ele.size == size and location == self._ele.location:
if self._ele.rect.size == size and location == self._ele.rect.location:
return True
size = self._ele.size
location = self._ele.location
size = self._ele.rect.size
location = self._ele.rect.location
if raise_err is True or Settings.raise_when_wait_failed is True:
raise WaitTimeoutError('等待元素停止运动失败。')