textual
textual copied to clipboard
start_value error when attempting to animate offset from code (Was: [BUG] AssertionError on value that isn't user-defined)
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'>
Offset should be a textual.geometry.Offset
object.
I'll see if we can raise a better error.
@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()
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>)
Last example works. Assuming fixed.