Skip to main content

Integrations

megane integrates with your existing tools and workflows. This guide covers Plotly event-driven control, MDX embedding, ipywidgets events, and framework-agnostic rendering.

Plotly

Control the megane viewer from Plotly charts using ipywidgets event interop. Click a data point on a Plotly FigureWidget to jump to the corresponding trajectory frame.

Click to Select Frame

import plotly.graph_objects as go
import ipywidgets as widgets
import megane

viewer = megane.view_traj("protein.pdb", xtc="trajectory.xtc")

# Create a Plotly time-series chart
fig = go.FigureWidget(
data=[go.Scatter(x=frames, y=energy, mode="lines+markers", name="Energy")],
layout=go.Layout(
xaxis_title="Frame", yaxis_title="Energy (kJ/mol)",
),
)

# Plotly click → megane frame selection
def on_click(trace, points, state):
if points.point_inds:
viewer.frame_index = points.point_inds[0]

fig.data[0].on_click(on_click)

# Display side by side
widgets.VBox([fig, viewer])

Sync Plotly Marker with Current Frame

Use megane's on_event("frame_change") callback to update a marker on the Plotly chart:

# megane → Plotly (update red marker on the chart)
@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]]

Dihedral Trajectory Analysis

Collect dihedral angles across all frames and plot the result:

import time

viewer.selected_atoms = [10, 20, 30, 40] # define the dihedral
dihedrals = []

@viewer.on_event("measurement")
def collect(data):
if data and data["type"] == "dihedral":
dihedrals.append(data["value"])

# Scan all frames
for i in range(viewer.total_frames):
viewer.frame_index = i
time.sleep(0.01) # allow widget sync

viewer.off_event("measurement", collect) # clean up

# Plot with Plotly
fig = go.FigureWidget(
data=[go.Scatter(y=dihedrals, mode="lines", name="Dihedral")],
layout=go.Layout(xaxis_title="Frame", yaxis_title="Angle (°)"),
)
fig

See examples/external_events.ipynb for complete working examples.

MDX / Next.js

Embed the megane viewer in MDX-based documentation frameworks like Next.js. Drop <MeganeViewer /> or <Viewport /> into your .mdx files — WASM parsing works out of the box with a one-line webpack config.

For full code examples and Next.js configuration, see the Web / React Guide — MDX Usage.

ipywidgets Events

The MolecularViewer widget supports event callbacks for reacting to user interactions.

Available Events

EventTriggerData
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:

import megane

viewer = megane.view("protein.pdb")

# As decorator
@viewer.on_event("measurement")
def on_measurement(data):
print(f"Measured: {data}")

# As method call
def on_frame(data):
print(f"Frame: {data['frame_index']}")

viewer.on_event("frame_change", on_frame)

# React to atom selection changes
@viewer.on_event("selection_change")
def on_selection(data):
print(f"Selected atoms: {data['atoms']}")

Removing Callbacks

Use off_event() to unregister:

viewer.off_event("measurement", on_measurement)  # remove specific callback
viewer.off_event("frame_change") # remove all frame_change callbacks

Programmatic Atom Selection

Select atoms from Python and read the measurement result directly:

viewer.selected_atoms = [10, 20, 30, 40]  # select 4 atoms → triggers selection_change
result = viewer.measurement # read measurement without callback
# {"type": "dihedral", "value": 120.5, "label": "120.5°", "atoms": [10, 20, 30, 40]}

The measurement property returns:

  • None if fewer than 2 atoms are selected
  • A dict with type ("distance", "angle", or "dihedral"), value (float), label (formatted string), and atoms (list of indices)

Framework-Agnostic Renderer

MoleculeRenderer is a plain Three.js class that can be mounted in any framework.

import { MoleculeRenderer } from "megane-viewer/lib";

// Create and mount
const renderer = new MoleculeRenderer();
renderer.mount(document.getElementById("viewer")!);

// Load data
renderer.loadSnapshot(snapshot);
renderer.updateFrame(frame);

// Clean up
renderer.dispose();

Use this to embed megane in Vue, Svelte, or any other framework — or directly in a vanilla <div>. See the Web / React Guide for detailed examples.