diff --git a/DrissionPage/_elements/chromium_element.py b/DrissionPage/_elements/chromium_element.py index 2d59f98..8fa2910 100644 --- a/DrissionPage/_elements/chromium_element.py +++ b/DrissionPage/_elements/chromium_element.py @@ -27,7 +27,8 @@ from .._units.selector import SelectElement from .._units.setter import ChromiumElementSetter from .._units.states import ElementStates, ShadowRootStates from .._units.waiter import ElementWaiter -from ..errors import ContextLostError, ElementLostError, JavaScriptError, CDPError, NoResourceError, AlertExistsError +from ..errors import ContextLostError, ElementLostError, JavaScriptError, CDPError, NoResourceError, AlertExistsError, \ + NoRectError __FRAME_ELEMENT__ = ('iframe', 'frame') @@ -346,6 +347,34 @@ class ChromiumElement(DrissionElement): """ return super().afters(locator, timeout, ele_only=ele_only) + def on(self, timeout=None): + """获取覆盖在本元素上最上层的元素 + :param timeout: 等待元素出现的超时时间(秒) + :return: 元素对象 + """ + timeout = timeout if timeout is None else self.owner.timeout + bid = self.wait.covered(timeout=timeout) + if bid: + return ChromiumElement(owner=self.owner, backend_id=bid) + else: + return NoneElement(page=self.owner, method='on()', args={'timeout': timeout}) + + def under(self, locator=None): + rect = self.states.has_rect + if not rect: + raise NoRectError + y = int(rect[2][1]) + x = int(self.rect.midpoint[0]) + while True: + y += 1 + try: + ele = self.owner.run_cdp('DOM.getNodeForLocation', x=x, y=y) + break + except: + raise + continue + return ChromiumElement(owner=self.owner, backend_id=ele['backendNodeId']) + def attr(self, attr): """返回一个attribute属性值 :param attr: 属性名 diff --git a/DrissionPage/_elements/chromium_element.pyi b/DrissionPage/_elements/chromium_element.pyi index c18794d..38fa917 100644 --- a/DrissionPage/_elements/chromium_element.pyi +++ b/DrissionPage/_elements/chromium_element.pyi @@ -161,6 +161,18 @@ class ChromiumElement(DrissionElement): timeout: float = None, ele_only: bool = True) -> List[Union[ChromiumElement, str]]: ... + def on(self, timeout: float = None) -> ChromiumElement: ... + + def under(self, locator: str = None) -> ChromiumElement: ... + + def above(self, locator: str = None) -> ChromiumElement: ... + + def left(self, locator: str = None) -> ChromiumElement: ... + + def right(self, locator: str = None) -> ChromiumElement: ... + + def offset(self, offset_x, offset_y) -> ChromiumElement: ... + @property def wait(self) -> ElementWaiter: ... diff --git a/DrissionPage/_units/states.pyi b/DrissionPage/_units/states.pyi index 99b5464..d9a0cb3 100644 --- a/DrissionPage/_units/states.pyi +++ b/DrissionPage/_units/states.pyi @@ -44,7 +44,7 @@ class ElementStates(object): def is_clickable(self) -> bool: ... @property - def has_rect(self) -> Union[bool, List[Tuple[float, float]]]: ... + def has_rect(self) -> Union[Literal[False], List[Tuple[float, float]]]: ... class ShadowRootStates(object): diff --git a/DrissionPage/_units/waiter.py b/DrissionPage/_units/waiter.py index 0e792bf..1133d7a 100644 --- a/DrissionPage/_units/waiter.py +++ b/DrissionPage/_units/waiter.py @@ -382,7 +382,7 @@ class ElementWaiter(OriginWaiter): """等待当前元素被遮盖 :param timeout: 超时时间,为None使用元素所在页面timeout属性 :param raise_err: 等待失败时是否报错,为None时根据Settings设置 - :return: 是否等待成功 + :return: 成功返回覆盖元素id,返回False """ return self._wait_state('is_covered', True, timeout, raise_err, err_text='等待元素被覆盖失败。') @@ -480,7 +480,7 @@ class ElementWaiter(OriginWaiter): """等待当前元素有大小及位置属性 :param timeout: 超时时间,为None使用元素所在页面timeout属性 :param raise_err: 等待失败时是否报错,为None时根据Settings设置 - :return: 是否等待成功 + :return: 成功返回元素四角坐标(左上 右上 右下 左下),失败返回False """ return self._wait_state('has_rect', True, timeout, raise_err, err_text='等待元素拥有大小及位置失败(等{}秒)。') @@ -493,16 +493,20 @@ class ElementWaiter(OriginWaiter): :param err_text: 抛出错误时显示的信息 :return: 是否等待成功 """ - err_text = err_text or '等待元素状态改变失败(等待{}秒)。' + a = self._ele.states.__getattribute__(attr) + if (a and mode) or (not a and not mode): + return a + if timeout is None: timeout = self._owner.timeout end_time = perf_counter() + timeout while perf_counter() < end_time: a = self._ele.states.__getattribute__(attr) if (a and mode) or (not a and not mode): - return True + return a sleep(.05) + err_text = err_text or '等待元素状态改变失败(等待{}秒)。' if raise_err is True or Settings.raise_when_wait_failed is True: raise WaitTimeoutError(err_text.format(timeout)) else: diff --git a/DrissionPage/_units/waiter.pyi b/DrissionPage/_units/waiter.pyi index e891bb2..f05ca94 100644 --- a/DrissionPage/_units/waiter.pyi +++ b/DrissionPage/_units/waiter.pyi @@ -5,7 +5,7 @@ @Copyright: (c) 2024 by g1879, Inc. All Rights Reserved. @License : BSD 3-Clause. """ -from typing import Union, Tuple +from typing import Union, Tuple, Literal, List from .downloader import DownloadMission from .._elements.chromium_element import ChromiumElement @@ -91,7 +91,7 @@ class ElementWaiter(OriginWaiter): def hidden(self, timeout: float = None, raise_err: bool = None) -> bool: ... - def covered(self, timeout: float = None, raise_err: bool = None) -> bool: ... + def covered(self, timeout: float = None, raise_err: bool = None) -> Union[Literal[False], int]: ... def not_covered(self, timeout: float = None, raise_err: bool = None) -> bool: ... @@ -101,7 +101,9 @@ class ElementWaiter(OriginWaiter): def clickable(self, wait_moved: bool = True, timeout: float = None, raise_err: bool = None) -> bool: ... - def has_rect(self, timeout: float = None, raise_err: bool = None) -> bool: ... + def has_rect(self, + timeout: float = None, + raise_err: bool = None) -> Union[Literal[False], List[Tuple[float, float]]]: ... def disabled_or_deleted(self, timeout: float = None, raise_err: bool = None) -> bool: ...