Module textual.cli.previews.easing
Expand source code
from __future__ import annotations
from rich.console import RenderableType
from textual._easing import EASING
from textual.app import App, ComposeResult
from textual.cli.previews.borders import TEXT
from textual.containers import Container, Horizontal, Vertical
from textual.reactive import Reactive
from textual.scrollbar import ScrollBarRender
from textual.widget import Widget
from textual.widgets import Button, Footer, Static, Input
VIRTUAL_SIZE = 100
WINDOW_SIZE = 10
START_POSITION = 0.0
END_POSITION = float(VIRTUAL_SIZE - WINDOW_SIZE)
class EasingButtons(Widget):
def compose(self) -> ComposeResult:
for easing in sorted(EASING, reverse=True):
yield Button(easing, id=easing)
class Bar(Widget):
position = Reactive.init(START_POSITION)
animation_running = Reactive(False)
DEFAULT_CSS = """
Bar {
background: $surface;
color: $error;
}
Bar.-active {
background: $surface;
color: $success;
}
"""
def watch_animation_running(self, running: bool) -> None:
self.set_class(running, "-active")
def render(self) -> RenderableType:
return ScrollBarRender(
virtual_size=VIRTUAL_SIZE,
window_size=WINDOW_SIZE,
position=self.position,
style=self.rich_style,
)
class EasingApp(App):
position = Reactive.init(START_POSITION)
duration = Reactive.var(1.0)
def on_load(self):
self.bind(
"ctrl+p", "focus('duration-input')", description="Focus: Duration Input"
)
self.bind("ctrl+b", "toggle_dark", description="Toggle Dark")
def compose(self) -> ComposeResult:
self.animated_bar = Bar()
self.animated_bar.position = START_POSITION
duration_input = Input("1.0", placeholder="Duration", id="duration-input")
self.opacity_widget = Static(
f"[b]Welcome to Textual![/]\n\n{TEXT}", id="opacity-widget"
)
yield EasingButtons()
yield Vertical(
Horizontal(
Static("Animation Duration:", id="label"), duration_input, id="inputs"
),
Horizontal(
self.animated_bar,
Container(self.opacity_widget, id="other"),
),
Footer(),
)
def on_button_pressed(self, event: Button.Pressed) -> None:
self.bell()
self.animated_bar.animation_running = True
def _animation_complete():
self.animated_bar.animation_running = False
target_position = (
END_POSITION if self.position == START_POSITION else START_POSITION
)
self.animate(
"position",
value=target_position,
final_value=target_position,
duration=self.duration,
easing=event.button.id,
on_complete=_animation_complete,
)
def watch_position(self, value: int):
self.animated_bar.position = value
self.opacity_widget.styles.opacity = 1 - value / END_POSITION
def on_input_changed(self, event: Input.Changed):
if event.sender.id == "duration-input":
new_duration = _try_float(event.value)
if new_duration is not None:
self.duration = new_duration
def action_toggle_dark(self):
self.dark = not self.dark
def _try_float(string: str) -> float | None:
try:
return float(string)
except ValueError:
return None
app = EasingApp(css_path="easing.css")
if __name__ == "__main__":
app.run()
Classes
class Bar (*children: Widget, name: str | None = None, id: str | None = None, classes: str | None = None)
-
A Widget is the base class for Textual widgets.
See also [static][textual.widgets._static.Static] for starting point for your own widgets.
Expand source code
class Bar(Widget): position = Reactive.init(START_POSITION) animation_running = Reactive(False) DEFAULT_CSS = """ Bar { background: $surface; color: $error; } Bar.-active { background: $surface; color: $success; } """ def watch_animation_running(self, running: bool) -> None: self.set_class(running, "-active") def render(self) -> RenderableType: return ScrollBarRender( virtual_size=VIRTUAL_SIZE, window_size=WINDOW_SIZE, position=self.position, style=self.rich_style, )
Ancestors
Class variables
var COMPONENT_CLASSES : ClassVar[set[str]]
var DEFAULT_CSS
Instance variables
var animation_running : ReactiveType
-
Reactive descriptor.
Args
- default (ReactiveType | Callable[[], ReactiveType]): A default value or callable that returns a default.
layout
:bool
, optional- Perform a layout on change. Defaults to False.
repaint
:bool
, optional- Perform a repaint on change. Defaults to True.
init
:bool
, optional- Call watchers on initialize (post mount). Defaults to False.
Expand source code
def __get__(self, obj: Reactable, obj_type: type[object]) -> ReactiveType: value: _NotSet | ReactiveType = getattr(obj, self.internal_name, _NOT_SET) if isinstance(value, _NotSet): # No value present, we need to set the default init_name = f"_default_{self.name}" default = getattr(obj, init_name) default_value = default() if callable(default) else default # Set and return the value setattr(obj, self.internal_name, default_value) if self._init: self._check_watchers(obj, self.name, default_value, first_set=True) return default_value return value
var position : ReactiveType
-
Reactive descriptor.
Args
- default (ReactiveType | Callable[[], ReactiveType]): A default value or callable that returns a default.
layout
:bool
, optional- Perform a layout on change. Defaults to False.
repaint
:bool
, optional- Perform a repaint on change. Defaults to True.
init
:bool
, optional- Call watchers on initialize (post mount). Defaults to False.
Expand source code
def __get__(self, obj: Reactable, obj_type: type[object]) -> ReactiveType: value: _NotSet | ReactiveType = getattr(obj, self.internal_name, _NOT_SET) if isinstance(value, _NotSet): # No value present, we need to set the default init_name = f"_default_{self.name}" default = getattr(obj, init_name) default_value = default() if callable(default) else default # Set and return the value setattr(obj, self.internal_name, default_value) if self._init: self._check_watchers(obj, self.name, default_value, first_set=True) return default_value return value
Methods
def watch_animation_running(self, running: bool) ‑> None
-
Expand source code
def watch_animation_running(self, running: bool) -> None: self.set_class(running, "-active")
Inherited members
Widget
:action
add_class
allow_horizontal_scroll
allow_vertical_scroll
ancestors
animate
app
auto_height
auto_links
auto_width
background_colors
call_later
can_focus
can_focus_children
capture_mouse
check_idle
classes
colors
compose
container_size
container_viewport
content_offset
content_region
content_size
css_identifier
css_identifier_styled
css_path_nodes
disable_messages
dispatch_key
display
displayed_children
emit
emit_no_wait
enable_messages
expand
focus
focusable_children
get_child
get_component_rich_style
get_component_styles
get_content_height
get_content_width
get_default_css
get_pseudo_classes
get_style_at
gutter
has_class
has_focus
has_pseudo_class
highlight_link_id
horizontal_scrollbar
hover_style
id
is_container
is_scrollable
is_transparent
layer
layers
link_hover_style
link_style
log
max_scroll_x
max_scroll_y
mount
mouse_over
offset
on_event
outer_size
parent
post_message
post_message_no_wait
post_render
pseudo_classes
query
query_one
refresh
region
release_mouse
remove
remove_class
render
render_line
render_lines
reset_focus
reset_styles
rich_style
screen
scroll_down
scroll_end
scroll_home
scroll_left
scroll_offset
scroll_page_down
scroll_page_left
scroll_page_right
scroll_page_up
scroll_relative
scroll_right
scroll_target_x
scroll_target_y
scroll_to
scroll_to_region
scroll_to_widget
scroll_up
scroll_visible
scroll_x
scroll_y
scrollbar_corner
scrollbar_gutter
scrollbar_size_horizontal
scrollbar_size_vertical
scrollbars_enabled
set_class
set_interval
set_styles
set_timer
show_horizontal_scrollbar
show_vertical_scrollbar
shrink
siblings
size
text_style
toggle_class
tree
vertical_scrollbar
virtual_region
virtual_region_with_margin
virtual_size
visible
visible_siblings
walk_children
watch_has_focus
watch_mouse_over
window_region
class EasingApp (driver_class: Type[Driver] | None = None, css_path: CSSPathType = None, watch_css: bool = False)
-
The base class for Textual Applications.
Args
- driver_class (Type[Driver] | None, optional): Driver class or
None
to auto-detect. Defaults to None. - title (str | None, optional): Title of the application. If
None
, the title is set to the name of theApp
subclass. Defaults toNone
. - css_path (str | PurePath | None, optional): Path to CSS or
None
for no CSS file. Defaults to None. watch_css
:bool
, optional- Watch CSS for changes. Defaults to False.
Expand source code
class EasingApp(App): position = Reactive.init(START_POSITION) duration = Reactive.var(1.0) def on_load(self): self.bind( "ctrl+p", "focus('duration-input')", description="Focus: Duration Input" ) self.bind("ctrl+b", "toggle_dark", description="Toggle Dark") def compose(self) -> ComposeResult: self.animated_bar = Bar() self.animated_bar.position = START_POSITION duration_input = Input("1.0", placeholder="Duration", id="duration-input") self.opacity_widget = Static( f"[b]Welcome to Textual![/]\n\n{TEXT}", id="opacity-widget" ) yield EasingButtons() yield Vertical( Horizontal( Static("Animation Duration:", id="label"), duration_input, id="inputs" ), Horizontal( self.animated_bar, Container(self.opacity_widget, id="other"), ), Footer(), ) def on_button_pressed(self, event: Button.Pressed) -> None: self.bell() self.animated_bar.animation_running = True def _animation_complete(): self.animated_bar.animation_running = False target_position = ( END_POSITION if self.position == START_POSITION else START_POSITION ) self.animate( "position", value=target_position, final_value=target_position, duration=self.duration, easing=event.button.id, on_complete=_animation_complete, ) def watch_position(self, value: int): self.animated_bar.position = value self.opacity_widget.styles.opacity = 1 - value / END_POSITION def on_input_changed(self, event: Input.Changed): if event.sender.id == "duration-input": new_duration = _try_float(event.value) if new_duration is not None: self.duration = new_duration def action_toggle_dark(self): self.dark = not self.dark
Ancestors
- App
- typing.Generic
- DOMNode
- MessagePump
Class variables
var CSS_PATH : CSSPathType
var SCREENS : dict[str, Screen]
var SUB_TITLE : str | None
var TITLE : str | None
Instance variables
var duration : ReactiveType
-
Reactive descriptor.
Args
- default (ReactiveType | Callable[[], ReactiveType]): A default value or callable that returns a default.
layout
:bool
, optional- Perform a layout on change. Defaults to False.
repaint
:bool
, optional- Perform a repaint on change. Defaults to True.
init
:bool
, optional- Call watchers on initialize (post mount). Defaults to False.
Expand source code
def __get__(self, obj: Reactable, obj_type: type[object]) -> ReactiveType: value: _NotSet | ReactiveType = getattr(obj, self.internal_name, _NOT_SET) if isinstance(value, _NotSet): # No value present, we need to set the default init_name = f"_default_{self.name}" default = getattr(obj, init_name) default_value = default() if callable(default) else default # Set and return the value setattr(obj, self.internal_name, default_value) if self._init: self._check_watchers(obj, self.name, default_value, first_set=True) return default_value return value
var position : ReactiveType
-
Reactive descriptor.
Args
- default (ReactiveType | Callable[[], ReactiveType]): A default value or callable that returns a default.
layout
:bool
, optional- Perform a layout on change. Defaults to False.
repaint
:bool
, optional- Perform a repaint on change. Defaults to True.
init
:bool
, optional- Call watchers on initialize (post mount). Defaults to False.
Expand source code
def __get__(self, obj: Reactable, obj_type: type[object]) -> ReactiveType: value: _NotSet | ReactiveType = getattr(obj, self.internal_name, _NOT_SET) if isinstance(value, _NotSet): # No value present, we need to set the default init_name = f"_default_{self.name}" default = getattr(obj, init_name) default_value = default() if callable(default) else default # Set and return the value setattr(obj, self.internal_name, default_value) if self._init: self._check_watchers(obj, self.name, default_value, first_set=True) return default_value return value
Methods
-
Expand source code
def on_button_pressed(self, event: Button.Pressed) -> None: self.bell() self.animated_bar.animation_running = True def _animation_complete(): self.animated_bar.animation_running = False target_position = ( END_POSITION if self.position == START_POSITION else START_POSITION ) self.animate( "position", value=target_position, final_value=target_position, duration=self.duration, easing=event.button.id, on_complete=_animation_complete, )
def on_input_changed(self, event: Input.Changed)
-
Expand source code
def on_input_changed(self, event: Input.Changed): if event.sender.id == "duration-input": new_duration = _try_float(event.value) if new_duration is not None: self.duration = new_duration
def on_load(self)
-
Expand source code
def on_load(self): self.bind( "ctrl+p", "focus('duration-input')", description="Focus: Duration Input" ) self.bind("ctrl+b", "toggle_dark", description="Toggle Dark")
def watch_position(self, value: int)
-
Expand source code
def watch_position(self, value: int): self.animated_bar.position = value self.opacity_widget.styles.opacity = 1 - value / END_POSITION
Inherited members
App
:action
action_bell
action_focus
action_pop_screen
action_push_screen
action_quit
action_screenshot
action_switch_screen
action_toggle_dark
add_class
ancestors
animate
app
background_colors
bell
bind
call_later
capture_mouse
check_bindings
check_idle
classes
colors
compose
css_identifier
css_identifier_styled
css_path_nodes
dark
debug
disable_messages
dispatch_key
display
displayed_children
emit
emit_no_wait
enable_messages
exit
export_screenshot
fatal_error
focused
get_child
get_component_styles
get_css_variables
get_default_css
get_driver_class
get_pseudo_classes
get_screen
get_widget_at
has_class
has_pseudo_class
id
install_screen
is_headless
is_mounted
is_screen_installed
log
mount
mount_all
namespace_bindings
on_event
panic
parent
pop_screen
post_message
post_message_no_wait
pseudo_classes
push_screen
query
query_one
refresh_css
remove_class
reset_styles
rich_style
run
save_screenshot
screen
screen_stack
set_class
set_focus
set_interval
set_styles
set_timer
size
sub_title
switch_screen
text_style
title
toggle_class
tree
uninstall_screen
update_styles
visible
walk_children
watch_dark
- driver_class (Type[Driver] | None, optional): Driver class or
class EasingButtons (*children: Widget, name: str | None = None, id: str | None = None, classes: str | None = None)
-
A Widget is the base class for Textual widgets.
See also [static][textual.widgets._static.Static] for starting point for your own widgets.
Expand source code
class EasingButtons(Widget): def compose(self) -> ComposeResult: for easing in sorted(EASING, reverse=True): yield Button(easing, id=easing)
Ancestors
Class variables
var COMPONENT_CLASSES : ClassVar[set[str]]
Inherited members
Widget
:action
add_class
allow_horizontal_scroll
allow_vertical_scroll
ancestors
animate
app
auto_height
auto_links
auto_width
background_colors
call_later
can_focus
can_focus_children
capture_mouse
check_idle
classes
colors
compose
container_size
container_viewport
content_offset
content_region
content_size
css_identifier
css_identifier_styled
css_path_nodes
disable_messages
dispatch_key
display
displayed_children
emit
emit_no_wait
enable_messages
expand
focus
focusable_children
get_child
get_component_rich_style
get_component_styles
get_content_height
get_content_width
get_default_css
get_pseudo_classes
get_style_at
gutter
has_class
has_focus
has_pseudo_class
highlight_link_id
horizontal_scrollbar
hover_style
id
is_container
is_scrollable
is_transparent
layer
layers
link_hover_style
link_style
log
max_scroll_x
max_scroll_y
mount
mouse_over
offset
on_event
outer_size
parent
post_message
post_message_no_wait
post_render
pseudo_classes
query
query_one
refresh
region
release_mouse
remove
remove_class
render
render_line
render_lines
reset_focus
reset_styles
rich_style
screen
scroll_down
scroll_end
scroll_home
scroll_left
scroll_offset
scroll_page_down
scroll_page_left
scroll_page_right
scroll_page_up
scroll_relative
scroll_right
scroll_target_x
scroll_target_y
scroll_to
scroll_to_region
scroll_to_widget
scroll_up
scroll_visible
scroll_x
scroll_y
scrollbar_corner
scrollbar_gutter
scrollbar_size_horizontal
scrollbar_size_vertical
scrollbars_enabled
set_class
set_interval
set_styles
set_timer
show_horizontal_scrollbar
show_vertical_scrollbar
shrink
siblings
size
text_style
toggle_class
tree
vertical_scrollbar
virtual_region
virtual_region_with_margin
virtual_size
visible
visible_siblings
walk_children
watch_has_focus
watch_mouse_over
window_region