浏览代码

separate text, value, source, content and visibility features more clearly

Falko Schindler 2 年之前
父节点
当前提交
419c7c8879

+ 3 - 10
nicegui/element.py

@@ -3,13 +3,13 @@ from abc import ABC
 from typing import Callable, Dict, List, Optional
 
 from . import globals
-from .elements.binding_mixins import BindVisibilityMixin
+from .elements.mixins.visibility import Visibility
 from .event_listener import EventListener
 from .slot import Slot
 from .task_logger import create_task
 
 
-class Element(ABC, BindVisibilityMixin):
+class Element(ABC, Visibility):
 
     def __init__(self, tag: str) -> None:
         self.client = globals.client_stack[-1]
@@ -23,19 +23,12 @@ class Element(ABC, BindVisibilityMixin):
         self._text: str = ''
         self.slots: Dict[str, Slot] = {}
         self.default_slot = self.add_slot('default')
-        self.visible = True
 
         self.client.elements[self.id] = self
         if self.client.slot_stack:
             self.client.slot_stack[-1].children.append(self)
 
-    def on_visibility_change(self, visible: str) -> None:
-        if visible and 'hidden' in self._classes:
-            self._classes.remove('hidden')
-            self.update()
-        if not visible and 'hidden' not in self._classes:
-            self._classes.append('hidden')
-            self.update()
+        self.init_visibility()
 
     def add_slot(self, name: str) -> Slot:
         self.slots[name] = Slot(self, name)

+ 3 - 2
nicegui/elements/badge.py

@@ -1,4 +1,4 @@
-from .text_element import TextElement
+from .mixins.text_element import TextElement
 
 
 class Badge(TextElement):
@@ -15,7 +15,8 @@ class Badge(TextElement):
         :param text_color: overrides text color (if needed); color name from the Quasar Color Palette (default: "white")
         :param outline: use 'outline' design (colored text and borders only) (default: False)
         """
-        super().__init__('q-badge', text)
+        super().__init__('q-badge')
+        self.init_text(text)
         self._props['color'] = color
         self._props['text_color'] = text_color
         self._props['outline'] = outline

+ 0 - 138
nicegui/elements/binding_mixins.py

@@ -1,138 +0,0 @@
-from typing import Any, Callable
-
-from ..binding import BindableProperty, bind, bind_from, bind_to
-
-
-class BindTextMixin:
-    """
-    Mixin providing bind methods for attribute text.
-    """
-    text = BindableProperty(on_change=lambda sender, text: sender.on_text_change(text))
-
-    def bind_text_to(self, target_object: Any, target_name: str = 'text', forward: Callable = lambda x: x):
-        bind_to(self, 'text', target_object, target_name, forward)
-        return self
-
-    def bind_text_from(self, target_object: Any, target_name: str = 'text', backward: Callable = lambda x: x):
-        bind_from(self, 'text', target_object, target_name, backward)
-        return self
-
-    def bind_text(self, target_object: Any, target_name: str = 'text', *,
-                  forward: Callable = lambda x: x, backward: Callable = lambda x: x):
-        bind(self, 'text', target_object, target_name, forward=forward, backward=backward)
-        return self
-
-    def set_text(self, text: str) -> None:
-        self.text = text
-
-    def on_text_change(self, text: str) -> None:
-        pass
-
-
-class BindValueMixin:
-    """
-    Mixin providing bind methods for attribute value.
-    """
-    value = BindableProperty(on_change=lambda sender, value: sender.on_value_change(value))
-
-    def bind_value_to(self, target_object: Any, target_name: str = 'value', forward: Callable = lambda x: x):
-        bind_to(self, 'value', target_object, target_name, forward)
-        return self
-
-    def bind_value_from(self, target_object: Any, target_name: str = 'value', backward: Callable = lambda x: x):
-        bind_from(self, 'value', target_object, target_name, backward)
-        return self
-
-    def bind_value(self, target_object: Any, target_name: str = 'value', *,
-                   forward: Callable = lambda x: x, backward: Callable = lambda x: x):
-        bind(self, 'value', target_object, target_name, forward=forward, backward=backward)
-        return self
-
-    def set_value(self, value: str) -> None:
-        self.value = value
-
-    def on_value_change(self, value: str) -> None:
-        pass
-
-
-class BindContentMixin:
-    """
-    Mixin providing bind methods for attribute content.
-    """
-    content = BindableProperty(on_change=lambda sender, content: sender.on_content_change(content))
-
-    def bind_content_to(self, target_object: Any, target_name: str = 'content', forward: Callable = lambda x: x):
-        bind_to(self, 'content', target_object, target_name, forward)
-        return self
-
-    def bind_content_from(self, target_object: Any, target_name: str = 'content', backward: Callable = lambda x: x):
-        bind_from(self, 'content', target_object, target_name, backward)
-        return self
-
-    def bind_content(self, target_object: Any, target_name: str = 'content', *,
-                     forward: Callable = lambda x: x, backward: Callable = lambda x: x):
-        bind(self, 'content', target_object, target_name, forward=forward, backward=backward)
-        return self
-
-    def set_content(self, content: str) -> None:
-        self.content = content
-
-    def on_content_change(self, content: str) -> None:
-        pass
-
-
-class BindVisibilityMixin:
-    """
-    Mixin providing bind methods for attribute visible.
-    """
-    visible = BindableProperty(on_change=lambda sender, visible: sender.on_visibility_change(visible))
-
-    def bind_visibility_to(self, target_object: Any, target_name: str = 'visible', forward: Callable = lambda x: x):
-        bind_to(self, 'visible', target_object, target_name, forward)
-        return self
-
-    def bind_visibility_from(self, target_object: Any, target_name: str = 'visible',
-                             backward: Callable = lambda x: x, *, value: Any = None):
-        if value is not None:
-            def backward(x): return x == value
-        bind_from(self, 'visible', target_object, target_name, backward)
-        return self
-
-    def bind_visibility(self, target_object: Any, target_name: str = 'visible', *,
-                        forward: Callable = lambda x: x, backward: Callable = lambda x: x, value: Any = None):
-        if value is not None:
-            def backward(x): return x == value
-        bind(self, 'visible', target_object, target_name, forward=forward, backward=backward)
-        return self
-
-    def set_visibility(self, visible: str) -> None:
-        self.visible = visible
-
-    def on_visibility_change(self, visible: str) -> None:
-        pass
-
-
-class BindSourceMixin:
-    """
-    Mixin providing bind methods for attribute source.
-    """
-    source = BindableProperty(on_change=lambda sender, source: sender.on_source_change(source))
-
-    def bind_source_to(self, target_object: Any, target_name: str = 'source', forward: Callable = lambda x: x):
-        bind_to(self, 'source', target_object, target_name, forward)
-        return self
-
-    def bind_source_from(self, target_object: Any, target_name: str = 'source', backward: Callable = lambda x: x):
-        bind_from(self, 'source', target_object, target_name, backward)
-        return self
-
-    def bind_source(self, target_object: Any, target_name: str = 'source', *,
-                    forward: Callable = lambda x: x, backward: Callable = lambda x: x):
-        bind(self, 'source', target_object, target_name, forward=forward, backward=backward)
-        return self
-
-    def set_source(self, source: str) -> None:
-        self.source = source
-
-    def on_source_change(self, source: str) -> None:
-        pass

+ 0 - 9
nicegui/elements/bool_element.py

@@ -1,9 +0,0 @@
-from typing import Callable, Optional
-
-from .value_element import ValueElement
-
-
-class BoolElement(ValueElement):
-
-    def __init__(self, tag: str, *, value: bool, on_change: Optional[Callable]) -> None:
-        super().__init__(tag, value=value, on_change=on_change)

+ 4 - 4
nicegui/elements/button.py

@@ -1,7 +1,7 @@
 from typing import Callable, Optional
 
 from ..events import ClickEventArguments, handle_event
-from .text_element import TextElement
+from .mixins.text_element import TextElement
 
 
 class Button(TextElement):
@@ -12,8 +12,8 @@ class Button(TextElement):
         :param text: the label of the button
         :param on_click: callback which is invoked when button is pressed
         """
-        super().__init__('q-btn', text)
-        self.text = text
-        self.props('color=primary')
+        super().__init__('q-btn')
+        self.init_text(text)
+        self._props['color'] = 'primary'
 
         self.on('click', lambda _: handle_event(on_click, ClickEventArguments(sender=self, client=self.client)))

+ 6 - 10
nicegui/elements/checkbox.py

@@ -1,10 +1,10 @@
 from typing import Callable, Optional
 
-from .binding_mixins import BindTextMixin
-from .bool_element import BoolElement
+from .mixins.text_element import TextElement
+from .mixins.value_element import ValueElement
 
 
-class Checkbox(BoolElement, BindTextMixin):
+class Checkbox(TextElement, ValueElement):
 
     def __init__(self, text: str = '', *, value: bool = False, on_change: Optional[Callable] = None) -> None:
         """Checkbox
@@ -13,10 +13,6 @@ class Checkbox(BoolElement, BindTextMixin):
         :param value: whether it should be checked initially (default: `False`)
         :param on_change: callback to execute when value changes
         """
-        super().__init__('q-checkbox', value=value, on_change=on_change)
-        self.text = text
-        self._text = text
-
-    def on_text_change(self, text: str) -> None:
-        self._text = text
-        self.update()
+        super().__init__('q-checkbox')
+        self.init_text(text)
+        self.init_value(value, on_change)

+ 3 - 11
nicegui/elements/html.py

@@ -1,8 +1,7 @@
-from ..element import Element
-from .binding_mixins import BindContentMixin
+from .mixins.content_element import ContentElement
 
 
-class Html(Element, BindContentMixin):
+class Html(ContentElement):
 
     def __init__(self, content: str = '') -> None:
         """HTML Element
@@ -15,11 +14,4 @@ class Html(Element, BindContentMixin):
         :param content: the HTML code to be displayed
         """
         super().__init__('div')
-        self.content = content
-        self.on_content_change(content)
-
-    def on_content_change(self, content: str) -> None:
-        if '</script>' in content:
-            raise ValueError('HTML elements must not contain <script> tags. Use ui.add_body_html() instead.')
-        self._text = content
-        self.update()
+        self.init_content(content)

+ 3 - 9
nicegui/elements/image.py

@@ -1,8 +1,7 @@
-from ..element import Element
-from .binding_mixins import BindSourceMixin
+from .mixins.source_element import SourceElement
 
 
-class Image(Element, BindSourceMixin):
+class Image(SourceElement):
 
     def __init__(self, source: str = '') -> None:
         """Image
@@ -12,9 +11,4 @@ class Image(Element, BindSourceMixin):
         :param source: the source of the image; can be a URL or a base64 string
         """
         super().__init__('q-img')
-        self.source = source
-        self._props['src'] = source
-
-    def on_source_change(self, source: str) -> None:
-        self._props['src'] = source
-        self.update()
+        self.init_source(source)

+ 3 - 2
nicegui/elements/label.py

@@ -1,4 +1,4 @@
-from .text_element import TextElement
+from .mixins.text_element import TextElement
 
 
 class Label(TextElement):
@@ -10,4 +10,5 @@ class Label(TextElement):
 
         :param text: the content of the label
         """
-        super().__init__('div', text)
+        super().__init__('div')
+        self.init_text(text)

+ 34 - 0
nicegui/elements/mixins/content_element.py

@@ -0,0 +1,34 @@
+from typing import Any, Callable
+
+from ...binding import BindableProperty, bind, bind_from, bind_to
+from ...element import Element
+
+
+class ContentElement(Element):
+    content = BindableProperty(on_change=lambda sender, content: sender.on_content_change(content))
+
+    def init_content(self, content: str) -> None:
+        self.content = content
+        self.on_content_change(content)
+
+    def bind_content_to(self, target_object: Any, target_name: str = 'content', forward: Callable = lambda x: x):
+        bind_to(self, 'content', target_object, target_name, forward)
+        return self
+
+    def bind_content_from(self, target_object: Any, target_name: str = 'content', backward: Callable = lambda x: x):
+        bind_from(self, 'content', target_object, target_name, backward)
+        return self
+
+    def bind_content(self, target_object: Any, target_name: str = 'content', *,
+                     forward: Callable = lambda x: x, backward: Callable = lambda x: x):
+        bind(self, 'content', target_object, target_name, forward=forward, backward=backward)
+        return self
+
+    def set_content(self, content: str) -> None:
+        self.content = content
+
+    def on_content_change(self, content: str) -> None:
+        if '</script>' in content:
+            raise ValueError('HTML elements must not contain <script> tags. Use ui.add_body_html() instead.')
+        self._text = content
+        self.update()

+ 32 - 0
nicegui/elements/mixins/source_element.py

@@ -0,0 +1,32 @@
+from typing import Any, Callable
+
+from ...binding import BindableProperty, bind, bind_from, bind_to
+from ...element import Element
+
+
+class SourceElement(Element):
+    source = BindableProperty(on_change=lambda sender, source: sender.on_source_change(source))
+
+    def init_source(self, source: str) -> None:
+        self.source = source
+        self._props['src'] = source
+
+    def bind_source_to(self, target_object: Any, target_name: str = 'source', forward: Callable = lambda x: x):
+        bind_to(self, 'source', target_object, target_name, forward)
+        return self
+
+    def bind_source_from(self, target_object: Any, target_name: str = 'source', backward: Callable = lambda x: x):
+        bind_from(self, 'source', target_object, target_name, backward)
+        return self
+
+    def bind_source(self, target_object: Any, target_name: str = 'source', *,
+                    forward: Callable = lambda x: x, backward: Callable = lambda x: x):
+        bind(self, 'source', target_object, target_name, forward=forward, backward=backward)
+        return self
+
+    def set_source(self, source: str) -> None:
+        self.source = source
+
+    def on_source_change(self, source: str) -> None:
+        self._props['src'] = source
+        self.update()

+ 32 - 0
nicegui/elements/mixins/text_element.py

@@ -0,0 +1,32 @@
+from typing import Any, Callable
+
+from ...binding import BindableProperty, bind, bind_from, bind_to
+from ...element import Element
+
+
+class TextElement(Element):
+    text = BindableProperty(on_change=lambda sender, text: sender.on_text_change(text))
+
+    def init_text(self, text: str) -> None:
+        self.text = text
+        self._text = text
+
+    def bind_text_to(self, target_object: Any, target_name: str = 'text', forward: Callable = lambda x: x):
+        bind_to(self, 'text', target_object, target_name, forward)
+        return self
+
+    def bind_text_from(self, target_object: Any, target_name: str = 'text', backward: Callable = lambda x: x):
+        bind_from(self, 'text', target_object, target_name, backward)
+        return self
+
+    def bind_text(self, target_object: Any, target_name: str = 'text', *,
+                  forward: Callable = lambda x: x, backward: Callable = lambda x: x):
+        bind(self, 'text', target_object, target_name, forward=forward, backward=backward)
+        return self
+
+    def set_text(self, text: str) -> None:
+        self.text = text
+
+    def on_text_change(self, text: str) -> None:
+        self._text = text
+        self.update()

+ 39 - 0
nicegui/elements/mixins/value_element.py

@@ -0,0 +1,39 @@
+from typing import Any, Callable, Dict
+
+from ...binding import BindableProperty, bind, bind_from, bind_to
+from ...element import Element
+from ...events import ValueChangeEventArguments, handle_event
+
+
+class ValueElement(Element):
+    value = BindableProperty(on_change=lambda sender, value: sender.on_value_change(value))
+
+    def init_value(self, value: Any, on_change: Callable) -> None:
+        self.value = value
+        self._props['model-value'] = value
+        self.change_handler = on_change
+
+        def handle_change(msg: Dict) -> None:
+            self.value = msg['args']
+        self.on('update:model-value', handle_change)
+
+    def bind_value_to(self, target_object: Any, target_name: str = 'value', forward: Callable = lambda x: x):
+        bind_to(self, 'value', target_object, target_name, forward)
+        return self
+
+    def bind_value_from(self, target_object: Any, target_name: str = 'value', backward: Callable = lambda x: x):
+        bind_from(self, 'value', target_object, target_name, backward)
+        return self
+
+    def bind_value(self, target_object: Any, target_name: str = 'value', *,
+                   forward: Callable = lambda x: x, backward: Callable = lambda x: x):
+        bind(self, 'value', target_object, target_name, forward=forward, backward=backward)
+        return self
+
+    def set_value(self, value: str) -> None:
+        self.value = value
+
+    def on_value_change(self, value: str) -> None:
+        self._props['model-value'] = value
+        self.update()
+        handle_event(self.change_handler, ValueChangeEventArguments(sender=self, client=self.client, value=value))

+ 42 - 0
nicegui/elements/mixins/visibility.py

@@ -0,0 +1,42 @@
+from typing import TYPE_CHECKING, Any, Callable
+
+from ...binding import BindableProperty, bind, bind_from, bind_to
+
+if TYPE_CHECKING:
+    from ...element import Element
+
+
+class Visibility:
+    visible = BindableProperty(on_change=lambda sender, visible: sender.on_visibility_change(visible))
+
+    def init_visibility(self) -> None:
+        self.visible = True
+
+    def bind_visibility_to(self, target_object: Any, target_name: str = 'visible', forward: Callable = lambda x: x):
+        bind_to(self, 'visible', target_object, target_name, forward)
+        return self
+
+    def bind_visibility_from(self, target_object: Any, target_name: str = 'visible',
+                             backward: Callable = lambda x: x, *, value: Any = None):
+        if value is not None:
+            def backward(x): return x == value
+        bind_from(self, 'visible', target_object, target_name, backward)
+        return self
+
+    def bind_visibility(self, target_object: Any, target_name: str = 'visible', *,
+                        forward: Callable = lambda x: x, backward: Callable = lambda x: x, value: Any = None):
+        if value is not None:
+            def backward(x): return x == value
+        bind(self, 'visible', target_object, target_name, forward=forward, backward=backward)
+        return self
+
+    def set_visibility(self, visible: str) -> None:
+        self.visible = visible
+
+    def on_visibility_change(self: 'Element', visible: str) -> None:
+        if visible and 'hidden' in self._classes:
+            self._classes.remove('hidden')
+            self.update()
+        if not visible and 'hidden' not in self._classes:
+            self._classes.append('hidden')
+            self.update()

+ 0 - 15
nicegui/elements/text_element.py

@@ -1,15 +0,0 @@
-from ..element import Element
-from .binding_mixins import BindTextMixin
-
-
-class TextElement(Element, BindTextMixin):
-    """An element with a bindable text property."""
-
-    def __init__(self, tag: str, text: str) -> None:
-        super().__init__(tag)
-        self.text = text
-        self._text = text
-
-    def on_text_change(self, text: str) -> None:
-        self._text = text
-        self.update()

+ 3 - 2
nicegui/elements/tooltip.py

@@ -1,4 +1,4 @@
-from .text_element import TextElement
+from .mixins.text_element import TextElement
 
 
 class Tooltip(TextElement):
@@ -10,4 +10,5 @@ class Tooltip(TextElement):
 
         :param text: the content of the tooltip
         """
-        super().__init__('q-tooltip', text)
+        super().__init__('q-tooltip')
+        self.init_text(text)

+ 0 - 24
nicegui/elements/value_element.py

@@ -1,24 +0,0 @@
-from typing import Any, Callable, Dict, Optional
-
-from ..element import Element
-from ..events import ValueChangeEventArguments, handle_event
-from .binding_mixins import BindValueMixin
-
-
-class ValueElement(Element, BindValueMixin):
-
-    def __init__(self, tag: str, *, value: Any, on_change: Optional[Callable]) -> None:
-        super().__init__(tag)
-        self.change_handler = on_change
-
-        self.value = value
-        self._props['model-value'] = value
-
-        def handle_change(msg: Dict) -> None:
-            self.value = msg['args']
-        self.on('update:model-value', handle_change)
-
-    def on_value_change(self, value: str) -> None:
-        self._props['model-value'] = value
-        self.update()
-        handle_event(self.change_handler, ValueChangeEventArguments(sender=self, client=self.client, value=value))