Textualize / textual

The lean application framework for Python. Build sophisticated user interfaces with a simple Python API. Run your apps in the terminal and a web browser.
https://textual.textualize.io/
MIT License
25.46k stars 782 forks source link

start_value error when attempting to animate offset from code (Was: [BUG] AssertionError on value that isn't user-defined) #1115

Closed epi052 closed 1 year ago

epi052 commented 1 year ago

Please give a brief but clear explanation of what the issue is. Let us know what the behaviour you expect is, and what is actually happening. Let us know what operating system you are running on, and what terminal you are using.

I was playing around with animating a dock's slide and tried to do it programmatically. When calling .animate on offset, I get the assertion error shown below. Changing offset to opacity shows a working animation.

This could absolutely be user-error, however, the asserted value isn't something I see as controllable from the user's api.

If you can, include a complete working example that demonstrates the bug. Please check it can run without modifications.

class Demo(App):
    TITLE = "Demonstration"
    BINDINGS = [
        ("b", "toggle_sidebar", "Sidebar"),
    ]
    CSS = """
    #sidebar {
        width: 40;
        background: $panel;
    }
    """

    def compose(self) -> ComposeResult:
        self.bar = Container(Static("Textual Demo"), id="sidebar")
        yield self.bar

    async def on_mount(self) -> None:
        self.bar.styles.animate("offset", value=0.0, duration=2.5)

if __name__ == "__main__":
    Demo().run()
╭────────────────────────────────────────────────────────────────────────── Traceback (most recent call last) ──────────────────────────────────────────────────────────────────────────╮
│ /home/epi/.cache/pypoetry/virtualenvs/textual-issue-65zq2x-i-py3.10/lib/python3.10/site-packages/textual/_animator.py:330 in __call__                                                 │
│                                                                                                                                                                                       │
│   327 │   │   │   animation_keys = list(self._animations.keys())                                                                                                                      │
│   328 │   │   │   for animation_key in animation_keys:                                                                                                                                │
│   329 │   │   │   │   animation = self._animations[animation_key]                                                                                                                     │
│ ❱ 330 │   │   │   │   animation_complete = animation(animation_time)                                                                                                                  │
│   331 │   │   │   │   if animation_complete:                                                                                                                                          │
│   332 │   │   │   │   │   completion_callback = animation.on_complete                                                                                                                 │
│   333 │   │   │   │   │   if completion_callback is not None:                                                                                                                         │
│                                                                                                                                                                                       │
│ ╭──────────────────────────────────────────────── locals ────────────────────────────────────────────────╮                                                                            │
│ │      animation = SimpleAnimation(                                                                      │                                                                            │
│ │                  │   obj=RenderStyles(                                                                 │                                                                            │
│ │                  │   │   layout=<vertical>,                                                            │                                                                            │
│ │                  │   │   background=Color(36, 41, 47, a=1.0),                                          │                                                                            │
│ │                  │   │   width=Scalar(                                                                 │                                                                            │
│ │                  │   │   │   value=40.0,                                                               │                                                                            │
│ │                  │   │   │   unit=<Unit.CELLS: 1>,                                                     │                                                                            │
│ │                  │   │   │   percent_unit=<Unit.WIDTH: 4>                                              │                                                                            │
│ │                  │   │   ),                                                                            │                                                                            │
│ │                  │   │   overflow_x='auto',                                                            │                                                                            │
│ │                  │   │   overflow_y='auto',                                                            │                                                                            │
│ │                  │   │   scrollbar_color=Color(35, 86, 139, a=1.0),                                    │                                                                            │
│ │                  │   │   scrollbar_color_active=Color(231, 146, 13, a=1.0),                            │                                                                            │
│ │                  │   │   scrollbar_corner_color=Color(20, 25, 31, a=1.0),                              │                                                                            │
│ │                  │   │   scrollbar_background=Color(20, 25, 31, a=1.0),                                │                                                                            │
│ │                  │   │   scrollbar_background_hover=Color(0, 5, 15, a=1.0),                            │                                                                            │
│ │                  │   │   scrollbar_size_vertical=2,                                                    │                                                                            │
│ │                  │   │   scrollbar_size_horizontal=1,                                                  │                                                                            │
│ │                  │   │   link_color=Color(255, 255, 255, a=0.87),                                      │                                                                            │
│ │                  │   │   auto_link_color=True,                                                         │                                                                            │
│ │                  │   │   link_style=Style(underline=True),                                             │                                                                            │
│ │                  │   │   link_hover_color=Color(255, 255, 255, a=0.87),                                │                                                                            │
│ │                  │   │   auto_link_hover_color=True,                                                   │                                                                            │
│ │                  │   │   link_hover_background=Color(1, 120, 212, a=1.0),                              │                                                                            │
│ │                  │   │   link_hover_style=Style(bold=True, underline=False)                            │                                                                            │
│ │                  │   ),                                                                                │                                                                            │
│ │                  │   attribute='offset',                                                               │                                                                            │
│ │                  │   start_time=987184.832620575,                                                      │                                                                            │
│ │                  │   duration=2.5,                                                                     │                                                                            │
│ │                  │   start_value=<ScalarOffset '0' '0'>,                                               │                                                                            │
│ │                  │   end_value=0.0,                                                                    │                                                                            │
│ │                  │   final_value=0.0,                                                                  │                                                                            │
│ │                  │   easing=<function <lambda> at 0x7ff0cd11a440>,                                     │                                                                            │
│ │                  │   on_complete=None                                                                  │                                                                            │
│ │                  )                                                                                     │                                                                            │
│ │  animation_key = (140672206350096, 'offset')                                                           │                                                                            │
│ │ animation_keys = [(140672206350096, 'offset')]                                                         │                                                                            │
│ │ animation_time = 987184.856866305                                                                      │                                                                            │
│ │           self = <textual._animator.Animator object at 0x7ff0ccdd8850>                                 │                                                                            │
│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯                                                                            │
│                                                                                                                                                                                       │
│ /home/epi/.cache/pypoetry/virtualenvs/textual-issue-65zq2x-i-py3.10/lib/python3.10/site-packages/textual/_animator.py:83 in __call__                                                  │
│                                                                                                                                                                                       │
│    80 │   │   │   ), "end_value must be animatable"                                                                                                                                   │
│    81 │   │   │   value = self.start_value.blend(self.end_value, eased_factor)                                                                                                        │
│    82 │   │   else:                                                                                                                                                                   │
│ ❱  83 │   │   │   assert isinstance(                                                                                                                                                  │
│    84 │   │   │   │   self.start_value, (int, float)                                                                                                                                  │
│    85 │   │   │   ), f"`start_value` must be float, not {self.start_value!r}"                                                                                                         │
│    86 │   │   │   assert isinstance(                                                                                                                                                  │
│                                                                                                                                                                                       │
│ ╭─────────────────────────────────────────────── locals ───────────────────────────────────────────────╮                                                                              │
│ │ eased_factor = 3.6487638795663746e-06                                                                │                                                                              │
│ │       factor = 0.009698292007669806                                                                  │                                                                              │
│ │         self = SimpleAnimation(                                                                      │                                                                              │
│ │                │   obj=RenderStyles(                                                                 │                                                                              │
│ │                │   │   layout=<vertical>,                                                            │                                                                              │
│ │                │   │   background=Color(36, 41, 47, a=1.0),                                          │                                                                              │
│ │                │   │   width=Scalar(                                                                 │                                                                              │
│ │                │   │   │   value=40.0,                                                               │                                                                              │
│ │                │   │   │   unit=<Unit.CELLS: 1>,                                                     │                                                                              │
│ │                │   │   │   percent_unit=<Unit.WIDTH: 4>                                              │                                                                              │
│ │                │   │   ),                                                                            │                                                                              │
│ │                │   │   overflow_x='auto',                                                            │                                                                              │
│ │                │   │   overflow_y='auto',                                                            │                                                                              │
│ │                │   │   scrollbar_color=Color(35, 86, 139, a=1.0),                                    │                                                                              │
│ │                │   │   scrollbar_color_active=Color(231, 146, 13, a=1.0),                            │                                                                              │
│ │                │   │   scrollbar_corner_color=Color(20, 25, 31, a=1.0),                              │                                                                              │
│ │                │   │   scrollbar_background=Color(20, 25, 31, a=1.0),                                │                                                                              │
│ │                │   │   scrollbar_background_hover=Color(0, 5, 15, a=1.0),                            │                                                                              │
│ │                │   │   scrollbar_size_vertical=2,                                                    │                                                                              │
│ │                │   │   scrollbar_size_horizontal=1,                                                  │                                                                              │
│ │                │   │   link_color=Color(255, 255, 255, a=0.87),                                      │                                                                              │
│ │                │   │   auto_link_color=True,                                                         │                                                                              │
│ │                │   │   link_style=Style(underline=True),                                             │                                                                              │
│ │                │   │   link_hover_color=Color(255, 255, 255, a=0.87),                                │                                                                              │
│ │                │   │   auto_link_hover_color=True,                                                   │                                                                              │
│ │                │   │   link_hover_background=Color(1, 120, 212, a=1.0),                              │                                                                              │
│ │                │   │   link_hover_style=Style(bold=True, underline=False)                            │                                                                              │
│ │                │   ),                                                                                │                                                                              │
│ │                │   attribute='offset',                                                               │                                                                              │
│ │                │   start_time=987184.832620575,                                                      │                                                                              │
│ │                │   duration=2.5,                                                                     │                                                                              │
│ │                │   start_value=<ScalarOffset '0' '0'>,                                               │                                                                              │
│ │                │   end_value=0.0,                                                                    │                                                                              │
│ │                │   final_value=0.0,                                                                  │                                                                              │
│ │                │   easing=<function <lambda> at 0x7ff0cd11a440>,                                     │                                                                              │
│ │                │   on_complete=None                                                                  │                                                                              │
│ │                )                                                                                     │                                                                              │
│ │         time = 987184.856866305                                                                      │                                                                              │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯                                                                              │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

AssertionError: `start_value` must be float, not <ScalarOffset '0' '0'>
willmcgugan commented 1 year ago

Offset should be a textual.geometry.Offset object.

I'll see if we can raise a better error.

davep commented 1 year ago

@willmcgugan Just flagging up that after the recent work on animations, this particular situation still throws the error. Testing with this:

from textual.app import App, ComposeResult
from textual.geometry import Offset
from textual.widgets import Static
from textual.containers import Container

class Demo(App):

    TITLE = "Demonstration"

    BINDINGS = [
        ("b", "toggle_sidebar", "Sidebar"),
    ]

    CSS = """
    #sidebar {
        width: 40;
        background: $panel;
    }
    """

    def compose(self) -> ComposeResult:
        self.bar = Container(Static("Textual Demo"), id="sidebar")
        yield self.bar

    def action_toggle_sidebar(self) -> None:
        self.bar.styles.animate("offset", value=Offset( 0, 0 ), duration=2.5)

if __name__ == "__main__":
    Demo().run()
Isy89 commented 1 year ago

I'm also getting a similar error when trying to animate the width.

from textual.app import App, ComposeResult
from textual.containers import Container
from textual.widgets import Static

class Demo(App):

    TITLE = "Demonstration"

    BINDINGS = [
        ("b", "toggle_sidebar", "Sidebar"),
    ]

    CSS = """
    #sidebar {
        width: 40;
        background: $panel;
    }
    """

    def compose(self) -> ComposeResult:
        self.bar = Container(Static("Textual Demo"), id="sidebar")
        yield self.bar

    def action_toggle_sidebar(self) -> None:
        self.bar.styles.animate("width", value=0, duration=2.5)

if __name__ == "__main__":
    Demo().run()
AssertionError: `start_value` must be float, not Scalar(value=40.0, unit=<Unit.CELLS: 1>, percent_unit=<Unit.WIDTH: 4>)
willmcgugan commented 1 year ago

Last example works. Assuming fixed.

github-actions[bot] commented 1 year ago

Don't forget to star the repository!

Follow @textualizeio for Textual updates.