← スキル一覧に戻る

panel-architecture
by AllenNeuralDynamics
Panel app for visualizing analysis results in AIND Analysis Framework
⭐ 0🍴 0📅 2026年1月22日
SKILL.md
name: panel-architecture description: Best practices for building Panel visualization apps using param.Parameterized classes, reactive binding with pn.bind(), and URL state sync.
Panel Application Architecture
Core Patterns
1. DataHolder for Reactive State
import param
import pandas as pd
class DataHolder(param.Parameterized):
"""Central state container that components watch."""
selected_id = param.String(default="")
filtered_df = param.DataFrame()
is_loaded = param.Boolean(default=False)
2. Reactive Binding with pn.bind()
# Function called when parameter changes
display = pn.bind(
self.render_content,
record_id=self.data_holder.param.selected_id,
df=self.data_holder.param.filtered_df,
)
pn.Column(display)
3. Component Pattern
class MyComponent:
def __init__(self, data_holder, config):
self.data_holder = data_holder
self.config = config
def create(self):
"""Return Panel viewable. Called once or reactively."""
return pn.bind(self._render, df=self.data_holder.param.filtered_df)
def _render(self, df):
# Create and return widget/pane
...
URL State Synchronization
When to Use Each Pattern
| Scenario | Use | Why |
|---|---|---|
| Widget created once | location.sync() | Simple, bidirectional |
| Widget in reactive render | One-way sync | Avoids race conditions |
| Multiple syncs updating URL | One-way sync | Prevents widget reversion |
Native Sync (for static widgets)
def _sync_url_state(self):
location = pn.state.location
location.sync(self.my_widget, {'value': 'param_name'})
One-Way Sync (for reactive widgets)
from urllib.parse import parse_qs
def get_url_param(name, default=None):
query = pn.state.location.search or ""
params = parse_qs(query.lstrip("?"))
return params.get(name, [default])[0]
def update_url_param(name, value):
pn.state.location.update_query(**{name: value})
# Usage
class MyComponent:
def __init__(self):
self._initial = get_url_param("my_param") # Read once
def create(self):
widget = pn.widgets.Select(value=self._initial, ...)
widget.param.watch(
lambda e: update_url_param("my_param", e.new), "value"
)
return widget
Auto-Load on Page Load
def main_layout(self):
# ... create layout ...
def _autoload():
if get_url_param("project"):
self.load_data()
pn.state.onload(_autoload)
return template
Layout Patterns
Tabulator for DataFrames
table = pn.widgets.Tabulator(
df,
selectable=1,
frozen_columns=["id"],
header_filters=True,
height=400,
sizing_mode="stretch_width",
)
def on_selection(event):
if event.new:
data_holder.selected_id = str(df.iloc[event.new[0]]["_id"])
table.param.watch(on_selection, "selection")
Bokeh Scatter with Hover Image
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, HoverTool
source = ColumnDataSource(df)
p = figure(tools="pan,wheel_zoom,reset,tap")
scatter = p.scatter(x="x", y="y", source=source)
tooltips = '<img src="@image_url{safe}" width="400">'
p.add_tools(HoverTool(tooltips=tooltips, renderers=[scatter]))
Best Practices
- Use
pn.bind()over callbacks - cleaner, more efficient - Throttle expensive updates:
slider.param.value_throttled - Update data sources, don't recreate widgets
- Cache data loading:
@pn.cache(ttl=3600)
Project Structure
code/
├── app.py # Entry point
├── config/ # Configuration (models.py, projects.py)
├── data/ # Data loaders with caching
├── core/ # State management (DataHolder)
├── components/ # UI components
└── utils/ # Helpers (url_state.py)
Running
# Development
panel serve code/app.py --dev --show
# Production
panel serve code/app.py --allow-websocket-origin=* --port 7860
スコア
総合スコア
60/100
リポジトリの品質指標に基づく評価
✓SKILL.md
SKILL.mdファイルが含まれている
+20
✓LICENSE
ライセンスが設定されている
+10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
○最近の活動
3ヶ月以内に更新がある
0/10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
○タグ
1つ以上のタグが設定されている
0/5
レビュー
💬
レビュー機能は近日公開予定です