From 84fbd51db66b491fa4067b6ac7c81eebf4e23f45 Mon Sep 17 00:00:00 2001 From: Rodja Trappe Date: Wed, 1 Nov 2023 07:58:43 +0100 Subject: [PATCH] introduce ui.get().not_within(...) --- nicegui/get.py | 24 ++++++++++++++++++++---- tests/test_get_elements.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/nicegui/get.py b/nicegui/get.py index 70f1cdcaa..8cf0c5f6c 100644 --- a/nicegui/get.py +++ b/nicegui/get.py @@ -28,9 +28,11 @@ def __init__(self, *, self._texts = [text] if isinstance(text, str) else text self._within_types: list[Element] = [] self._within_keys: list[str] = [] + self._not_within_types: list[Element] = [] + self._not_within_keys: list[str] = [] self._exclude_types: list[Element] = [] self._exclude_keys: list[str] = [] - self.exclude_texts: list[str] = [] + self._exclude_texts: list[str] = [] def __iter__(self) -> Iterator[T]: client = context.get_client() @@ -43,9 +45,11 @@ def iterate(self, parent: Element, *, visited: List[Element] = []) -> Iterator[T (not self._texts or hasattr(element, 'text') and all(text in element.text for text in self._texts)) and \ (not self._exclude_types or not any(isinstance(element, type) for type in self._exclude_types)) and \ (not self._exclude_keys or not any(key in element._keys for key in self._exclude_keys)) and \ - (not self.exclude_texts or ((hasattr(element, 'text') and not any(text in element.text for text in self.exclude_texts)))): + (not self._exclude_texts or ((hasattr(element, 'text') and not any(text in element.text for text in self._exclude_texts)))): if (not self._within_types or any(isinstance(element, type) for type in self._within_types for element in visited)) and \ - (not self._within_keys or any(key in element._keys for key in self._within_keys for element in visited)): + (not self._within_keys or any(key in element._keys for key in self._within_keys for element in visited)) and \ + (not self._not_within_types or not any(isinstance(element, type) for type in self._not_within_types for element in visited)) and \ + (not self._not_within_keys or not any(key in element._keys for key in self._not_within_keys for element in visited)): yield element yield from self.iterate(element, visited=visited + [element]) @@ -69,13 +73,25 @@ def within(self, *, type: Optional[Element] = None, key: str = None) -> Self: return self def exclude(self, *, type: Optional[Element] = None, key: Optional[str] = None, text: Optional[str] = None) -> Self: + """Exclude elements with specific type, key or text.""" + if type is not None: assert issubclass(type, Element) self._exclude_types.append(type) if key is not None: self._exclude_keys.append(key) if text is not None: - self.exclude_texts.append(text) + self._exclude_texts.append(text) + return self + + def not_within(self, *, type: Optional[Element] = None, key: str = None) -> Self: + """Exclude elements which have a parent of a specific type or key.""" + + if type is not None: + assert issubclass(type, Element) + self._not_within_types.append(type) + if key is not None: + self._not_within_keys.append(key) return self def classes(self, add: Optional[str] = None, *, remove: Optional[str] = None, replace: Optional[str] = None) -> Self: diff --git a/tests/test_get_elements.py b/tests/test_get_elements.py index 1756c70d7..c64234862 100644 --- a/tests/test_get_elements.py +++ b/tests/test_get_elements.py @@ -212,3 +212,31 @@ def test_get_with_excluding_text(screen: Screen): screen.open('/') assert len(result) == 1 assert result[0].text == 'button B' + + +def test_get_not_within_type(screen: Screen): + ui.button('button A') + ui.label('label A') + with ui.row(): + ui.button('button B') + ui.label('label B') + + result = [e for e in ui.get(type=ui.button).not_within(type=ui.row)] + + screen.open('/') + assert len(result) == 1 + assert result[0].text == 'button A' + + +def test_get_not_within_key(screen: Screen): + ui.button('button A') + ui.label('label A') + with ui.row().keys('horizontal'): + ui.button('button B') + ui.label('label B') + + result = [e for e in ui.get(type=ui.button).not_within(key='horizontal')] + + screen.open('/') + assert len(result) == 1 + assert result[0].text == 'button A'