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.chromium_options import ChromiumOptions
from ._configs.session_options import SessionOptions 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,8 +170,11 @@ class Browser(object):
end_time = perf_counter() + timeout end_time = perf_counter() + timeout
while perf_counter() < end_time: while perf_counter() < end_time:
p = popen(txt) p = popen(txt)
try:
if f' {self.process_id} ' not in p.read(): if f' {self.process_id} ' not in p.read():
return return
except TypeError:
return
sleep(.2) sleep(.2)
if force: if force:

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

View File

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

View File

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

View File

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

View File

@ -356,33 +356,11 @@ class ChromiumFrame(ChromiumBase):
"""返回frame元素所有attribute属性""" """返回frame元素所有attribute属性"""
return self.frame_ele.attrs 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 @property
def active_ele(self): def active_ele(self):
"""返回当前焦点所在元素""" """返回当前焦点所在元素"""
return self.doc_ele.run_js('return this.activeElement;') 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 @property
def xpath(self): def xpath(self):
"""返回frame的xpath绝对路径""" """返回frame的xpath绝对路径"""
@ -597,8 +575,8 @@ class ChromiumFrame(ChromiumBase):
self.frame_ele.scroll.to_see(center=True) self.frame_ele.scroll.to_see(center=True)
self.scroll.to_see(ele, center=True) self.scroll.to_see(ele, center=True)
cx, cy = ele.locations.viewport_location cx, cy = ele.rect.viewport_location
w, h = ele.size w, h = ele.rect.size
img_data = f'data:image/{pic_type};base64,{self.frame_ele.get_screenshot(as_base64=True)}' img_data = f'data:image/{pic_type};base64,{self.frame_ele.get_screenshot(as_base64=True)}'
body = self.tab('t:body') body = self.tab('t:body')
first_child = body('c::first-child') first_child = body('c::first-child')
@ -652,3 +630,23 @@ class ChromiumFrame(ChromiumBase):
def is_alive(self): def is_alive(self):
"""返回是否仍可用""" """返回是否仍可用"""
return self.states.is_alive 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 .._elements.chromium_element import ChromiumElement
from .._units.states import FrameStates from .._units.states import FrameStates
from .._units.ids import FrameIds from .._units.ids import FrameIds
from .._units.locations import Locations
from .._units.rect import FrameRect from .._units.rect import FrameRect
from .._units.scroller import FrameScroller from .._units.scroller import FrameScroller
from .._units.setter import ChromiumFrameSetter from .._units.setter import ChromiumFrameSetter
@ -89,24 +88,12 @@ class ChromiumFrame(ChromiumBase):
@property @property
def attrs(self) -> dict: ... def attrs(self) -> dict: ...
@property
def page_size(self) -> Tuple[int, int]: ...
@property
def size(self) -> Tuple[int, int]: ...
@property @property
def rect(self) -> FrameRect: ... def rect(self) -> FrameRect: ...
@property @property
def active_ele(self) -> ChromiumElement: ... def active_ele(self) -> ChromiumElement: ...
@property
def location(self) -> Tuple[int, int]: ...
@property
def locations(self) -> Locations: ...
@property @property
def xpath(self) -> str: ... def xpath(self) -> str: ...

View File

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

View File

@ -39,9 +39,6 @@ class ChromiumPage(ChromiumBase):
@property @property
def tabs(self) -> List[str]: ... def tabs(self) -> List[str]: ...
@property
def rect(self) -> TabRect: ...
@property @property
def wait(self) -> PageWaiter: ... 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 .._commons.web import set_session_cookies, set_browser_cookies
from .._pages.chromium_base import ChromiumBase from .._pages.chromium_base import ChromiumBase
from .._pages.session_page import SessionPage from .._pages.session_page import SessionPage
from .._units.rect import TabRect
from .._units.setter import TabSetter, WebPageTabSetter from .._units.setter import TabSetter, WebPageTabSetter
from .._units.waiter import TabWaiter from .._units.waiter import TabWaiter
@ -44,13 +43,6 @@ class ChromiumTab(ChromiumBase):
"""返回总体page对象""" """返回总体page对象"""
return self._page return self._page
@property
def rect(self):
"""返回获取窗口坐标和大小的对象"""
if self._rect is None:
self._rect = TabRect(self)
return self._rect
@property @property
def set(self): def set(self):
"""返回用于等待的对象""" """返回用于等待的对象"""

View File

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

View File

@ -49,6 +49,13 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
elif self._mode == 's': elif self._mode == 's':
return super().__call__(loc_or_str) return super().__call__(loc_or_str)
@property
def set(self):
"""返回用于等待的对象"""
if self._set is None:
self._set = WebPageSetter(self)
return self._set
@property @property
def url(self): def url(self):
"""返回当前url""" """返回当前url"""
@ -134,13 +141,6 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
""" """
self.set.timeouts(implicit=second) 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): def get(self, url, show_errmsg=False, retry=None, interval=None, timeout=None, **kwargs):
"""跳转到一个url """跳转到一个url
:param url: 目标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)): elif isinstance(ele_or_loc, str) or 'ChromiumElement' in str(type(ele_or_loc)):
ele_or_loc = self.page(ele_or_loc) ele_or_loc = self.page(ele_or_loc)
self.page.scroll.to_see(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 lx = x + offset_x
ly = y + offset_y ly = y + offset_y
else: else:
@ -55,8 +55,8 @@ class ActionChains:
if is_loc: if is_loc:
cx, cy = location_to_client(self.page, lx, ly) cx, cy = location_to_client(self.page, lx, ly)
else: else:
x, y = ele_or_loc.locations.viewport_location if offset_x or offset_y \ x, y = ele_or_loc.rect.viewport_location if offset_x or offset_y \
else ele_or_loc.locations.viewport_midpoint else ele_or_loc.rect.viewport_midpoint
cx = x + offset_x cx = x + offset_x
cy = y + offset_y cy = y + offset_y

View File

@ -42,7 +42,7 @@ class Clicker(object):
try: try:
self._ele.scroll.to_see() self._ele.scroll.to_see()
if self._ele.states.is_enabled and self._ele.states.is_displayed: 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 can_click = True
except NoRectError: except NoRectError:
if by_js is False: if by_js is False:
@ -59,7 +59,7 @@ class Clicker(object):
self._ele.wait.stop_moving(timeout=end_time - perf_counter()) self._ele.wait.stop_moving(timeout=end_time - perf_counter())
if rect: if rect:
self._ele.scroll.to_see() self._ele.scroll.to_see()
rect = self._ele.locations.rect rect = self._ele.rect.corners
while perf_counter() < end_time: while perf_counter() < end_time:
if 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 can_click = True
@ -79,12 +79,12 @@ class Clicker(object):
r = self._ele.page.run_cdp('DOM.getNodeForLocation', x=x, y=y, includeUserAgentShadowDOM=True, r = self._ele.page.run_cdp('DOM.getNodeForLocation', x=x, y=y, includeUserAgentShadowDOM=True,
ignorePointerEventsNone=True) ignorePointerEventsNone=True)
if r['backendNodeId'] != self._ele.ids.backend_id: if r['backendNodeId'] != self._ele.ids.backend_id:
vx, vy = self._ele.locations.viewport_midpoint vx, vy = self._ele.rect.viewport_midpoint
else: else:
vx, vy = self._ele.locations.viewport_click_point vx, vy = self._ele.rect.viewport_click_point
except CDPError: except CDPError:
vx, vy = self._ele.locations.viewport_midpoint vx, vy = self._ele.rect.viewport_midpoint
self._click(vx, vy) self._click(vx, vy)
return True return True
@ -99,13 +99,13 @@ class Clicker(object):
def right(self): def right(self):
"""右键单击""" """右键单击"""
self._ele.page.scroll.to_see(self._ele) 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') self._click(x, y, 'right')
def middle(self): def middle(self):
"""中键单击""" """中键单击"""
self._ele.page.scroll.to_see(self._ele) 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') self._click(x, y, 'middle')
def at(self, offset_x=None, offset_y=None, button='left', count=1): 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) self._ele.page.scroll.to_see(self._ele)
if offset_x is None and offset_y is None: 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_x = w // 2
offset_y = h // 2 offset_y = h // 2
x, y = offset_scroll(self._ele, offset_x, offset_y) 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): class TabRect(object):
def __init__(self, page): def __init__(self, page):
self._page = page self._page = page
@ -49,7 +153,7 @@ class TabRect(object):
return w_bl + w_bs - w_vs, h_bl + h_bs - h_vs return w_bl + w_bs - w_vs, h_bl + h_bs - h_vs
@property @property
def page_size(self): def size(self):
"""返回页面总宽高,格式:(宽, 高)""" """返回页面总宽高,格式:(宽, 高)"""
r = self._get_page_rect()['contentSize'] r = self._get_page_rect()['contentSize']
return r['width'], r['height'] return r['width'], r['height']
@ -83,21 +187,38 @@ class FrameRect(object):
self._frame = frame self._frame = frame
@property @property
def viewport_location(self): def location(self):
"""返回视口在屏幕中坐标,左上角为(0, 0)""" """返回元素左上角的绝对坐标"""
return self._frame.frame_ele.locations.screen_location return self._frame.frame_ele.rect.location
@property @property
def page_size(self): def viewport_location(self):
"""返回页面总宽高,格式:(宽, 高)""" """返回视口在屏幕中坐标,左上角为(0, 0)"""
return self._frame.page_size return self._frame.frame_ele.rect.viewport_location
@property
def screen_location(self):
"""返回元素左上角在屏幕上坐标,左上角为(0, 0)"""
return self._frame.frame_ele.rect.screen_location
@property @property
def size(self): def size(self):
"""返回页面总宽高,格式:(宽, 高)""" """返回frame内页面尺寸格式(宽, 高)"""
return self._frame.size 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 @property
def viewport_size(self): 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 @Author : g1879
@Contact : g1879@qq.com @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_frame import ChromiumFrame
from .._pages.chromium_page import ChromiumPage from .._pages.chromium_page import ChromiumPage
from .._pages.chromium_tab import ChromiumTab, WebPageTab from .._pages.chromium_tab import ChromiumTab, WebPageTab
from .._pages.web_page import WebPage 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): class TabRect(object):
def __init__(self, page: Union[ChromiumPage, ChromiumTab, WebPage, WebPageTab]): def __init__(self, page: ChromiumBase):
self._page: Union[ChromiumPage, ChromiumTab, WebPage, WebPageTab] = ... self._page: Union[ChromiumPage, ChromiumTab, WebPage, WebPageTab] = ...
@property @property
@ -31,7 +79,7 @@ class TabRect(object):
def window_size(self) -> Tuple[int, int]: ... def window_size(self) -> Tuple[int, int]: ...
@property @property
def page_size(self) -> Tuple[int, int]: ... def size(self) -> Tuple[int, int]: ...
@property @property
def viewport_size(self) -> Tuple[int, int]: ... def viewport_size(self) -> Tuple[int, int]: ...
@ -48,14 +96,23 @@ class FrameRect(object):
def __init__(self, frame: ChromiumFrame): def __init__(self, frame: ChromiumFrame):
self._frame: ChromiumFrame = ... self._frame: ChromiumFrame = ...
@property
def location(self) -> Tuple[float, float]: ...
@property @property
def viewport_location(self) -> Tuple[float, float]: ... def viewport_location(self) -> Tuple[float, float]: ...
@property
def screen_location(self) -> Tuple[float, float]: ...
@property @property
def size(self) -> Tuple[float, float]: ... def size(self) -> Tuple[float, float]: ...
@property @property
def page_size(self) -> Tuple[float, float]: ... def viewport_size(self) -> Tuple[float, float]: ...
@property @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 @property
def is_in_viewport(self): def is_in_viewport(self):
"""返回元素是否出现在视口中以元素click_point为判断""" """返回元素是否出现在视口中以元素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 return location_in_viewport(self._ele.page, x, y) if x else False
@property @property
def is_whole_in_viewport(self): def is_whole_in_viewport(self):
"""返回元素是否整个都在视口内""" """返回元素是否整个都在视口内"""
x1, y1 = self._ele.location x1, y1 = self._ele.rect.location
w, h = self._ele.size w, h = self._ele.rect.size
x2, y2 = x1 + w, y1 + h x2, y2 = x1 + w, y1 + h
return location_in_viewport(self._ele.page, x1, y1) and location_in_viewport(self._ele.page, x2, y2) return location_in_viewport(self._ele.page, x1, y1) and location_in_viewport(self._ele.page, x2, y2)
@property @property
def is_covered(self): def is_covered(self):
"""返回元素是否被覆盖,与是否在视口中无关""" """返回元素是否被覆盖,与是否在视口中无关"""
lx, ly = self._ele.locations.click_point lx, ly = self._ele.rect.click_point
try: try:
r = self._ele.page.run_cdp('DOM.getNodeForLocation', x=lx, y=ly) r = self._ele.page.run_cdp('DOM.getNodeForLocation', x=lx, y=ly)
except CDPError: except CDPError:
@ -77,7 +77,7 @@ class ElementStates(object):
def has_rect(self): def has_rect(self):
"""返回元素是否拥有位置和大小没有返回False有返回四个角在页面中坐标组成的列表""" """返回元素是否拥有位置和大小没有返回False有返回四个角在页面中坐标组成的列表"""
try: try:
return self._ele.locations.rect return self._ele.rect.corners
except NoRectError: except NoRectError:
return False return False

View File

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