spaday-perspective
Perspective for spaday
Overview
spaday-perspective moves the existing PerspectivePanel integration out of spaday core. Its
self-contained <perspective-panel> bundle includes Perspective's client, viewer, workspace, datagrid,
themes, and viewer WASM.
Documentation
- Build a live market workspace — guided first application.
- Stream data and change layouts — task-focused server and UI recipes.
- API reference — component props, layout shape, and package descriptor.
- Why Perspective uses its own data channel — integration design and tradeoffs.
Quick example
This complete app serves a live Perspective table. Add trade streams a new row into the grid, the select switches between flat and grouped layouts, and the checkbox changes the viewer theme.
import perspective
from perspective.handlers.starlette import PerspectiveStarletteHandler
from starlette.responses import JSONResponse
from starlette.routing import Route, WebSocketRoute
from starlette.websockets import WebSocket
from spaday import CallEndpoint, cond, element, eq, field, obj
from spaday.backends.starlette import serve
from spaday_perspective import PerspectivePanel
server = perspective.Server()
client = server.new_local_client()
trades = client.table(
[
{"id": 1, "symbol": "AAPL", "side": "buy", "price": 211.10},
{"id": 2, "symbol": "MSFT", "side": "sell", "price": 503.02},
],
name="trades",
)
next_id = 2
async def add_trade(_request):
global next_id
next_id += 1
trades.update(
[
{
"id": next_id,
"symbol": "AAPL" if next_id % 2 else "MSFT",
"side": "buy" if next_id % 2 else "sell",
"price": 210 + next_id,
}
]
)
return JSONResponse({"id": next_id})
async def perspective_socket(websocket: WebSocket):
await PerspectiveStarletteHandler(perspective_server=server, websocket=websocket).run()
def layout(*, grouped=False):
viewer = {"table": "trades", "plugin": "Datagrid", "columns": ["symbol", "side", "price"]}
if grouped:
viewer.update({"group_by": ["symbol"], "columns": ["price"], "aggregates": {"price": "avg"}})
return {
"sizes": [1],
"detail": {"main": {"type": "tab-area", "widgets": ["trades"], "currentIndex": 0}},
"master": {"sizes": [], "widgets": []},
"mode": "globalFilters",
"viewers": {"trades": viewer},
}
panel = (
PerspectivePanel()
.compute("theme", cond(field("dark"), "dark", "light"))
.compute(
"config",
obj(
{
"ws_url": "/perspective",
"tables": ["trades"],
"layout": cond(eq(field("view"), "grouped"), layout(grouped=True), layout()),
}
),
)
.style(display="block", height="70vh", margin_top="1rem")
)
view = element("select").bind("value", "view", mode="two-way").child(
element("option", "Blotter", value="blotter"),
element("option", "Average by symbol", value="grouped"),
)
dark = element("input", type="checkbox").bind("checked", "dark", mode="two-way")
page = (
element("main")
.style(margin="1rem", font_family="system-ui")
.child(element("button").text("Add trade").on("click", CallEndpoint("POST", "/api/trades")))
.child(view)
.child(element("label").child(dark, " Dark theme"))
.child(panel)
)
app = serve(
page,
packages=["perspective"],
store={"view": "blotter", "dark": False},
routes=[
Route("/api/trades", add_trade, methods=["POST"]),
WebSocketRoute("/perspective", perspective_socket),
],
)
Save this as app.py, then run
pip install spaday-perspective perspective-python starlette uvicorn and uvicorn app:app.
Open http://127.0.0.1:8000.
This keeps Perspective's bulk data on its native websocket. A spaday/transports model only needs to
carry the small serializable connection/layout config. Replacing config reconnects when ws_url
changes and restores changed layouts; theme accepts light, dark, or a Perspective theme name.
Installing this project registers the perspective entry point with spaday. The equivalent explicit
forms are packages=[spaday_perspective.package] and packages=["spaday_perspective:package"].
Run the local example
python -m pip install -e ".[examples]"
python -m spaday_perspective.example
Open http://127.0.0.1:8015 to inspect the complete market-monitor example: live native
Perspective streaming, flat and grouped layouts, server-authoritative order submission, reactive metrics,
and light/dark themes. It passes the local package descriptor directly, so it does not install or resolve
the integration from GitHub.
[!NOTE] This library was generated using copier from the Base Python Project Template repository.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file spaday_perspective-0.2.0.tar.gz.
File metadata
- Download URL: spaday_perspective-0.2.0.tar.gz
- Upload date:
- Size: 2.0 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a4648cbf15cdd6163fac1096846fe2fd47fbcac2779e0fca26add2f666d303be
|
|
| MD5 |
39b57240e55e1083541fadc448abced7
|
|
| BLAKE2b-256 |
f798a0d7f79593bc83d09cb098674b12bd1ccbffda32972270b9093926a02f46
|
File details
Details for the file spaday_perspective-0.2.0-py3-none-any.whl.
File metadata
- Download URL: spaday_perspective-0.2.0-py3-none-any.whl
- Upload date:
- Size: 2.0 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
85594ffc1f8ea9b102d9c0d769d30128ab40eca2a321628e30d8c4532a500bd9
|
|
| MD5 |
25296df6073fcce7ed18e02391e177d1
|
|
| BLAKE2b-256 |
e8764e354e2ae3da475597d2f5e1b87779666db7aae74fb151e9c8e0b40577a4
|