Marquee¶
A scrolling ticker display for continuous information streams like stock prices or news.
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¶
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— 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:
Functions¶
build_html
¶
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 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 follows namespace:event-name pattern.