diff --git a/DrissionPage/__init__.py b/DrissionPage/__init__.py index ad912d6..5b6d740 100644 --- a/DrissionPage/__init__.py +++ b/DrissionPage/__init__.py @@ -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' diff --git a/DrissionPage/_base/browser.py b/DrissionPage/_base/browser.py index 05d12c9..9b9ebdf 100644 --- a/DrissionPage/_base/browser.py +++ b/DrissionPage/_base/browser.py @@ -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) diff --git a/DrissionPage/_elements/chromium_element.py b/DrissionPage/_elements/chromium_element.py index bf052c7..d28e9e7 100644 --- a/DrissionPage/_elements/chromium_element.py +++ b/DrissionPage/_elements/chromium_element.py @@ -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基本一致""" diff --git a/DrissionPage/_elements/chromium_element.pyi b/DrissionPage/_elements/chromium_element.pyi index 90f3caa..7b261fa 100644 --- a/DrissionPage/_elements/chromium_element.pyi +++ b/DrissionPage/_elements/chromium_element.pyi @@ -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: ... diff --git a/DrissionPage/_pages/chromium_base.py b/DrissionPage/_pages/chromium_base.py index 92c8c56..2158ce0 100644 --- a/DrissionPage/_pages/chromium_base.py +++ b/DrissionPage/_pages/chromium_base.py @@ -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信息的类""" diff --git a/DrissionPage/_pages/chromium_base.pyi b/DrissionPage/_pages/chromium_base.pyi index 41caec9..37f9225 100644 --- a/DrissionPage/_pages/chromium_base.pyi +++ b/DrissionPage/_pages/chromium_base.pyi @@ -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: ... diff --git a/DrissionPage/_pages/chromium_frame.py b/DrissionPage/_pages/chromium_frame.py index c0e15d1..7be33ba 100644 --- a/DrissionPage/_pages/chromium_frame.py +++ b/DrissionPage/_pages/chromium_frame.py @@ -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 diff --git a/DrissionPage/_pages/chromium_frame.pyi b/DrissionPage/_pages/chromium_frame.pyi index cfaba9e..7c93fd0 100644 --- a/DrissionPage/_pages/chromium_frame.pyi +++ b/DrissionPage/_pages/chromium_frame.pyi @@ -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: ... diff --git a/DrissionPage/_pages/chromium_page.py b/DrissionPage/_pages/chromium_page.py index bd2cdd5..6a8b60c 100644 --- a/DrissionPage/_pages/chromium_page.py +++ b/DrissionPage/_pages/chromium_page.py @@ -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): """返回用于等待的对象""" diff --git a/DrissionPage/_pages/chromium_page.pyi b/DrissionPage/_pages/chromium_page.pyi index 62f7e95..c834405 100644 --- a/DrissionPage/_pages/chromium_page.pyi +++ b/DrissionPage/_pages/chromium_page.pyi @@ -39,9 +39,6 @@ class ChromiumPage(ChromiumBase): @property def tabs(self) -> List[str]: ... - @property - def rect(self) -> TabRect: ... - @property def wait(self) -> PageWaiter: ... diff --git a/DrissionPage/_pages/chromium_tab.py b/DrissionPage/_pages/chromium_tab.py index 8f8cc7c..c67600a 100644 --- a/DrissionPage/_pages/chromium_tab.py +++ b/DrissionPage/_pages/chromium_tab.py @@ -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): """返回用于等待的对象""" diff --git a/DrissionPage/_pages/chromium_tab.pyi b/DrissionPage/_pages/chromium_tab.pyi index 5adb85f..687a88c 100644 --- a/DrissionPage/_pages/chromium_tab.pyi +++ b/DrissionPage/_pages/chromium_tab.pyi @@ -34,9 +34,6 @@ class ChromiumTab(ChromiumBase): @property def page(self) -> ChromiumPage: ... - @property - def rect(self) -> TabRect: ... - @property def set(self) -> TabSetter: ... diff --git a/DrissionPage/_pages/web_page.py b/DrissionPage/_pages/web_page.py index 679745a..a873b3a 100644 --- a/DrissionPage/_pages/web_page.py +++ b/DrissionPage/_pages/web_page.py @@ -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 diff --git a/DrissionPage/_units/action_chains.py b/DrissionPage/_units/action_chains.py index 8b177d1..df74134 100644 --- a/DrissionPage/_units/action_chains.py +++ b/DrissionPage/_units/action_chains.py @@ -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 diff --git a/DrissionPage/_units/clicker.py b/DrissionPage/_units/clicker.py index 0ab9234..aae2461 100644 --- a/DrissionPage/_units/clicker.py +++ b/DrissionPage/_units/clicker.py @@ -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) diff --git a/DrissionPage/_units/locations.py b/DrissionPage/_units/locations.py deleted file mode 100644 index 010a962..0000000 --- a/DrissionPage/_units/locations.py +++ /dev/null @@ -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 diff --git a/DrissionPage/_units/locations.pyi b/DrissionPage/_units/locations.pyi deleted file mode 100644 index 5e7ca31..0000000 --- a/DrissionPage/_units/locations.pyi +++ /dev/null @@ -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]: ... diff --git a/DrissionPage/_units/rect.py b/DrissionPage/_units/rect.py index 1b31ebb..4180b14 100644 --- a/DrissionPage/_units/rect.py +++ b/DrissionPage/_units/rect.py @@ -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 diff --git a/DrissionPage/_units/rect.pyi b/DrissionPage/_units/rect.pyi index 4c507fb..b45ef98 100644 --- a/DrissionPage/_units/rect.pyi +++ b/DrissionPage/_units/rect.pyi @@ -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], ...]: ... diff --git a/DrissionPage/_units/states.py b/DrissionPage/_units/states.py index 2b7c79f..0bf571b 100644 --- a/DrissionPage/_units/states.py +++ b/DrissionPage/_units/states.py @@ -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 diff --git a/DrissionPage/_units/waiter.py b/DrissionPage/_units/waiter.py index 0361c56..cb265d4 100644 --- a/DrissionPage/_units/waiter.py +++ b/DrissionPage/_units/waiter.py @@ -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('等待元素停止运动失败。')