Jupyter / Python
Use megane inside Jupyter notebooks as an interactive widget backed by anywidget. Works in JupyterLab, Jupyter Notebook, VS Code, and Google Colab.
Installation
pip install megane
Then launch JupyterLab (recommended) or classic Notebook:
jupyter lab
Quick Start
Display a structure
import megane
viewer = megane.view("caffeine.pdb")
viewer # display inline
The last expression in a cell renders the widget. Use display(viewer) to render it multiple times in the same notebook.
Play a trajectory
import megane
viewer = megane.view_traj("protein.pdb", xtc="trajectory.xtc", bonds="structure")
viewer
The timeline scrubber appears automatically when a trajectory is loaded.
Options
Both view() and view_traj() accept these keyword arguments:
| Parameter | Type | Default | Description |
|---|---|---|---|
bonds | "distance" | "structure" | None | "distance" | Bond detection method. None to hide bonds |
perspective | bool | False | Perspective projection instead of orthographic |
cell_axes_visible | bool | True | Show unit cell axes |
For full control over the visualization (filtering, multi-layer rendering, custom nodes), see Visual Pipeline — Python API.
Widget API
After displaying the widget, control it programmatically:
| Property / Method | Type | Description |
|---|---|---|
viewer.frame_index | int (read/write) | Current trajectory frame (0-based) |
viewer.total_frames | int (read) | Total number of frames |
viewer.selected_atoms | list[int] (read/write) | Indices of currently selected atoms |
viewer.measurement | dict | None (read) | Current measurement result |
viewer.set_pipeline(pipe) | method | Apply a pipeline to the viewer |
viewer.on_event(name, fn) | method | Register an event callback |
viewer.off_event(name, fn) | method | Remove an event callback |
Example — jump to a specific frame:
viewer.frame_index = 100
Example — read the current measurement:
viewer.selected_atoms = [10, 20, 30, 40] # select 4 atoms
print(viewer.measurement)
# {"type": "dihedral", "value": 120.5, "label": "120.5°", "atoms": [10, 20, 30, 40]}
Event Callbacks
React to user interactions with on_event():
Available events
| Event | Fires when | Payload |
|---|---|---|
frame_change | Frame index changes | {"frame_index": int} |
selection_change | Selected atoms change | {"atoms": list[int]} |
measurement | User selects 2–4 atoms | {"type": str, "value": float, "label": str, "atoms": list[int]} or None |
Registering callbacks
Use on_event() as a decorator or method call:
# Decorator style
@viewer.on_event("frame_change")
def on_frame(data):
print(f"Frame changed to {data['frame_index']}")
# Method-call style
def on_select(data):
print(f"Selected: {data['atoms']}")
viewer.on_event("selection_change", on_select)
Removing callbacks
viewer.off_event("frame_change", on_frame) # remove a specific callback
viewer.off_event("selection_change") # remove all callbacks for this event
Plotly Integration
Combine megane with Plotly FigureWidget to build interactive analysis dashboards.
Click a chart point to jump to a frame
import plotly.graph_objects as go
import ipywidgets as widgets
import megane
viewer = megane.view_traj("protein.pdb", xtc="trajectory.xtc")
fig = go.FigureWidget(
data=[go.Scatter(x=list(range(n_frames)), y=energy, mode="lines+markers")],
layout=go.Layout(xaxis_title="Frame", yaxis_title="Energy (kJ/mol)"),
)
def on_click(trace, points, state):
if points.point_inds:
viewer.frame_index = points.point_inds[0]
fig.data[0].on_click(on_click)
widgets.VBox([fig, viewer])
Sync a Plotly marker with the current frame
@viewer.on_event("frame_change")
def update_marker(data):
idx = data["frame_index"]
with fig.batch_update():
marker_trace.x = [idx]
marker_trace.y = [energy[idx]]
Collect dihedral angles across all frames
import time
viewer.selected_atoms = [10, 20, 30, 40]
dihedrals = []
@viewer.on_event("measurement")
def collect(data):
if data and data["type"] == "dihedral":
dihedrals.append(data["value"])
for i in range(viewer.total_frames):
viewer.frame_index = i
time.sleep(0.01) # allow widget sync
viewer.off_event("measurement", collect)
# Plot the result
fig = go.FigureWidget(
data=[go.Scatter(y=dihedrals, mode="lines")],
layout=go.Layout(xaxis_title="Frame", yaxis_title="Dihedral (°)"),
)
fig
Interactive Demos
The following are actual executed notebooks you can browse inline.
Demo Notebook
Basic usage — loading structures, navigating trajectories, atom selection, measurement, and side-by-side viewers.
Advanced: External Events
Event handling, Plotly integration, bidirectional sync, dihedral analysis, and custom widget composition.
Pipeline API Reference
For simple visualization, use megane.view() and megane.view_traj(). For advanced customization (filtering, multi-layer rendering, custom nodes), build a pipeline manually with the Pipeline class. See Visual Pipeline — Python API for the complete list of node classes, port names, and parameters.
All classes and convenience functions are importable from megane:
from megane import (
view, view_traj, # convenience wrappers
Pipeline, # pipeline builder
LoadStructure, LoadTrajectory, LoadVector, Streaming, # source nodes
Filter, Modify, # processing nodes
AddBonds, AddLabels, AddPolyhedra, # overlay nodes
VectorOverlay, Viewport, # display nodes
)
Quick Reference
Mouse Controls
| Action | Effect |
|---|---|
| Left-drag | Rotate |
| Scroll / Pinch | Zoom |
| Right-click atom | Select atom |
| Double-click | Center on atom |
Select 2–4 atoms to measure distances, angles, or dihedral angles.
Measurement Types
| Atoms Selected | Measurement |
|---|---|
| 2 | Distance (Å) |
| 3 | Angle (°) |
| 4 | Dihedral angle (°) |
API Reference
Methods
| Method | Description |
|---|---|
set_pipeline(pipeline) | Apply a Pipeline to the viewer, or None to clear |
on_event(name, callback=None) | Register an event callback. Use as @viewer.on_event("name") decorator or viewer.on_event("name", fn) |
off_event(name, callback=None) | Remove a specific callback, or all callbacks for the event if callback is None |
Properties
| Property | Type | Read/Write | Description |
|---|---|---|---|
frame_index | int | R/W | Current trajectory frame (0-based). Setting this fires frame_change |
total_frames | int | R | Total number of frames in the loaded trajectory |
selected_atoms | list[int] | R/W | Indices of selected atoms. Setting this fires selection_change |
measurement | dict | None | R | Current measurement result, or None if fewer than 2 atoms selected |
The measurement dict contains: type ("distance", "angle", or "dihedral"), value (float), label (formatted string like "120.5°"), and atoms (list of indices).
Tips
- Re-running cells — calling
viewer.set_pipeline(pipe)again updates the displayed pipeline in-place without re-creating the widget. - JupyterLab vs. classic Notebook — megane is tested on JupyterLab 4+. In classic Notebook, enable the
anywidgetextension if the viewer appears blank. - Large trajectories — megane streams frames over a local WebSocket rather than loading them into memory. Even multi-GB XTC files scrub smoothly.
- Sharing notebooks — exported HTML (
File → Export → HTML) captures the last-rendered frame as a static image.
Troubleshooting
VS Code: "Failed to load model class 'AnyModel' from module 'anywidget'"
This is a known issue with VS Code's Jupyter extension and affects many anywidget-based projects.
Workarounds:
-
Reload the VS Code window after installing megane:
- Open the Command Palette (
Ctrl+Shift+P/Cmd+Shift+P) - Run
Developer: Reload Window
- Open the Command Palette (
-
Ensure ipywidgets v8.x is installed:
pip install "ipywidgets>=8.0.0" -
Update the VS Code Jupyter extension to the latest version.
-
Restart the kernel and re-run the notebook cells.