Skip to main content

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:

ParameterTypeDefaultDescription
bonds"distance" | "structure" | None"distance"Bond detection method. None to hide bonds
perspectiveboolFalsePerspective projection instead of orthographic
cell_axes_visibleboolTrueShow 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 / MethodTypeDescription
viewer.frame_indexint (read/write)Current trajectory frame (0-based)
viewer.total_framesint (read)Total number of frames
viewer.selected_atomslist[int] (read/write)Indices of currently selected atoms
viewer.measurementdict | None (read)Current measurement result
viewer.set_pipeline(pipe)methodApply a pipeline to the viewer
viewer.on_event(name, fn)methodRegister an event callback
viewer.off_event(name, fn)methodRemove 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

EventFires whenPayload
frame_changeFrame index changes{"frame_index": int}
selection_changeSelected atoms change{"atoms": list[int]}
measurementUser 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.

Loading notebook...

Advanced: External Events

Event handling, Plotly integration, bidirectional sync, dihedral analysis, and custom widget composition.

Loading notebook...

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

ActionEffect
Left-dragRotate
Scroll / PinchZoom
Right-click atomSelect atom
Double-clickCenter on atom

Select 2–4 atoms to measure distances, angles, or dihedral angles.

Measurement Types

Atoms SelectedMeasurement
2Distance (Å)
3Angle (°)
4Dihedral angle (°)

API Reference

Methods

MethodDescription
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

PropertyTypeRead/WriteDescription
frame_indexintR/WCurrent trajectory frame (0-based). Setting this fires frame_change
total_framesintRTotal number of frames in the loaded trajectory
selected_atomslist[int]R/WIndices of selected atoms. Setting this fires selection_change
measurementdict | NoneRCurrent 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 anywidget extension 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:

  1. Reload the VS Code window after installing megane:

    • Open the Command Palette (Ctrl+Shift+P / Cmd+Shift+P)
    • Run Developer: Reload Window
  2. Ensure ipywidgets v8.x is installed:

    pip install "ipywidgets>=8.0.0"
  3. Update the VS Code Jupyter extension to the latest version.

  4. Restart the kernel and re-run the notebook cells.