Skip to content

Marquee

A scrolling ticker display for continuous information streams like stock prices or news.

AAPL $185.32 +1.2% GOOGL $141.80 -0.5% MSFT $378.91 +0.8% AMZN $178.25 +2.1%

Basic Usage

from pywry import Marquee, TickerItem

items = [
    TickerItem(ticker="AAPL", text="AAPL $185.32 +1.2%", class_name="stock-up"),
    TickerItem(ticker="GOOGL", text="GOOGL $141.80 -0.5%", class_name="stock-down"),
    TickerItem(ticker="MSFT", text="MSFT $378.91 +0.8%", class_name="stock-up"),
]

ticker = Marquee(
    text=" ".join(item.build_html() for item in items),
)

Speed Control

Marquee(
    items=[...],
    speed=20,  # Seconds per scroll cycle — lower is faster (default: 15.0)
)

Direction

# Right to left (default)
Marquee(items=[...], direction="left")

# Left to right
Marquee(items=[...], direction="right")

Common Patterns

Stock Ticker

stocks = [
    {"symbol": "AAPL", "price": 185.32, "change": 1.2},
    {"symbol": "GOOGL", "price": 141.80, "change": -0.5},
    {"symbol": "MSFT", "price": 378.91, "change": 0.8},
    {"symbol": "AMZN", "price": 178.25, "change": 2.1},
]

items = [
    TickerItem(
        ticker=s["symbol"],
        text=f"{s['symbol']} ${s['price']:.2f} {'+'if s['change'] > 0 else ''}{s['change']:.1f}%",
        class_name="stock-up" if s["change"] > 0 else "stock-down",
    )
    for s in stocks
]

ticker = Marquee(
    component_id="stock-ticker",
    text=" ".join(item.build_html() for item in items),
)

toolbar = Toolbar(
    position="header",
    items=[ticker],
)

Updating Ticker Dynamically

from pywry import PyWry, Toolbar, Marquee, TickerItem, Button

app = PyWry()

def on_refresh_prices(data, event_type, label):
    # Simulate updated prices
    new_prices = [
        {"symbol": "AAPL", "price": 186.50, "change": 1.8},
        {"symbol": "GOOGL", "price": 142.20, "change": -0.2},
        {"symbol": "MSFT", "price": 380.00, "change": 1.1},
    ]
    new_items = [
        TickerItem(
            ticker=p["symbol"],
            text=f"{p['symbol']} ${p['price']:.2f} {'+'if p['change'] > 0 else ''}{p['change']:.1f}%",
            class_name="stock-up" if p["change"] > 0 else "stock-down",
        )
        for p in new_prices
    ]
    app.emit("toolbar:marquee-set-content", {
        "id": "stock-ticker",
        "text": " ".join(item.build_html() for item in new_items),
    }, label)
    app.emit("pywry:alert", {"message": "Prices refreshed!", "type": "success"}, label)

app.show(
    "<h1>Stock Dashboard</h1>",
    toolbars=[
        Toolbar(position="header", items=[
            Marquee(
                component_id="stock-ticker",
                text=" ".join(
                    TickerItem(ticker=s, text=f"{s} --").build_html()
                    for s in ["AAPL", "GOOGL", "MSFT"]
                ),
            )
        ]),
        Toolbar(position="top", items=[
            Button(label="Refresh Prices", event="prices:refresh", variant="primary")
        ]),
    ],
    callbacks={"prices:refresh": on_refresh_prices},
)

News Ticker

headlines = [
    "Breaking: Major tech earnings beat expectations",
    "Fed signals steady rates through Q2",
    "New AI regulations proposed in Congress",
]

news_items = [
    TickerItem(ticker=f"news-{i}", text=f"📰 {headline}")
    for i, headline in enumerate(headlines)
]

news_ticker = Marquee(
    text=" • ".join(item.build_html() for item in news_items),
    speed=30,  # Slower for readability
)

Metrics Dashboard

metric_items = [
    TickerItem(ticker="users", text="Users: 12,453 +5%", class_name="stock-up"),
    TickerItem(ticker="revenue", text="Revenue: $1.2M +12%", class_name="stock-up"),
    TickerItem(ticker="orders", text="Orders: 834 +8%", class_name="stock-up"),
    TickerItem(ticker="uptime", text="Uptime: 99.9%"),
]

metrics = Marquee(
    text=" ".join(item.build_html() for item in metric_items),
)

Attributes

component_id : str | None
    Unique identifier for state tracking and dynamic updates (auto-generated if not provided)
label : str | None
    Display label (rarely used for marquees)
description : str | None
    Tooltip/hover text for accessibility and user guidance
event : str
    Event name emitted on click when clickable=True (format: namespace:event-name)
style : str | None
    Optional inline CSS
disabled : bool
    Whether the marquee is disabled (default: False)
text : str
    Text or HTML content to scroll (default: "")
speed : float
    Duration in seconds for one scroll cycle — lower is faster (default: 15.0, range: 1.0–300.0)
direction : str
    Scroll direction: "left", "right", "up", or "down" (default: "left")
behavior : str
    Animation behavior: "scroll", "alternate", "slide", or "static" (default: "scroll")
pause_on_hover : bool
    Pause animation when mouse hovers (default: True)
gap : int
    Gap in pixels between repeated content (default: 50, range: 0–500)
clickable : bool
    Whether clicking the marquee emits an event (default: False)
separator : str
    Optional separator between repeated content, e.g. " • " (default: "")
items : list[str] | None
    List of content items for static behavior with auto-cycling (default: None)
children : list[ToolbarItem] | None
    Nested toolbar items to scroll (alternative to text) (default: None)

Events

When clickable=True, emits the event name with payload:

{"value": "AAPL $185.32 +1.2%", "componentId": "marquee-abc123"}
  • value — text content of the clicked item

Dynamic update event: Use toolbar:marquee-set-content to update items at runtime (see Updating Ticker Dynamically example above).

Styling

Customize appearance with CSS:

.pywry-marquee {
    background: linear-gradient(to right, #1a1a2e, #16213e);
    padding: 8px 0;
}

.pywry-ticker-item {
    margin: 0 24px;
}

.pywry-ticker-symbol {
    font-weight: bold;
    color: #fff;
}

.pywry-ticker-change.positive {
    color: #00ff88;
}

.pywry-ticker-change.negative {
    color: #ff4444;
}

API Reference

Bases: ToolbarItem

A scrolling text/content ticker component using CSS animations.

Displays content that scrolls horizontally or vertically across the container. Useful for news tickers, announcements, or any content that should scroll continuously.

Uses CSS animations instead of the deprecated <marquee> HTML element for better browser compatibility, performance, and accessibility.

Emits {value: ..., componentId: ...} when clicked (if clickable=True).

Attributes:

Name Type Description
text str

The text content to scroll. Can include simple inline HTML.

speed float

Animation duration in seconds for one complete scroll cycle. Lower values = faster scrolling (default: 15.0, range: 1.0 - 300.0).

direction str

Scroll direction: "left" (default), "right", "up", or "down".

behavior str

Animation behavior: "scroll" (default, continuous loop), "alternate" (bounces), "slide" (scrolls once), or "static" (no scrolling).

pause_on_hover bool

Pause animation when mouse hovers over marquee (default: True).

gap int

Gap in pixels between repeated content for seamless looping (default: 50, range: 0 - 500).

clickable bool

Whether clicking the marquee emits an event (default: False).

separator str

Optional separator string between repeated content, e.g., " • " (default: "").

items list of str or None

List of content items to cycle through for static behavior (default: None).

children list or None

Nested toolbar items to scroll (alternative to text for complex content).

Examples:

Simple news ticker:

>>> Marquee(
...     text="Breaking News: Stock prices are up 5% today!",
...     event="ticker:click",
...     speed=20,
...     pause_on_hover=True,
... )

Bouncing alert:

>>> Marquee(
...     text="⚠️ System maintenance scheduled",
...     behavior="alternate",
...     speed=8,
...     style="background: var(--pywry-accent); padding: 4px 8px;",
... )

Vertical scrolling credits:

>>> Marquee(
...     text="Thank you for using PyWry!",
...     direction="up",
...     speed=10,
... )

Functions

validate_content

validate_content() -> Marquee

Ensure either text or children is provided.

build_html

build_html(parent_id: str | None = None) -> str

Build marquee HTML with CSS animation.

Parameters:

Name Type Description Default
parent_id str | None

Parent component ID for context chain inheritance.

None

Returns:

Type Description
str

HTML string for the marquee component.

collect_scripts

collect_scripts() -> list[str]

Collect scripts from nested children (for consistency with Div).

Returns:

Type Description
list[str]

List of script content strings from children.

update_payload

update_payload(text: str | None = None, html_content: str | None = None, speed: float | None = None, paused: bool | None = None, separator: str | None = None) -> tuple[str, dict[str, Any]]

Generate event name and payload for dynamic marquee updates.

Use this with widget.emit() to update marquee content dynamically.

Parameters:

Name Type Description Default
text str | None

New plain text content (will be escaped).

None
html_content str | None

New HTML content (use instead of text for rich content).

None
speed float | None

New animation speed in seconds.

None
paused bool | None

True to pause, False to resume animation.

None
separator str | None

New separator between repeated content.

None

Returns:

Type Description
tuple[str, dict[str, Any]]

Event name and payload dict for widget.emit().

Example
ticker = Marquee(text="Loading...", component_id="news-ticker")
# ... later ...
event, data = ticker.update_payload(text="Breaking news!")
widget.emit(event, data)

# Or with speed change:
event, data = ticker.update_payload(text="Urgent!", speed=8)
widget.emit(event, data)

auto_generate_component_id

auto_generate_component_id() -> ToolbarItem

Auto-generate component_id based on type if not provided.

validate_event_name classmethod

validate_event_name(v: str) -> str

Validate event follows namespace:event-name pattern.