Skip to content

Commit

Permalink
Merge pull request mdtraj#935 from rmcgibbo/ipython-html
Browse files Browse the repository at this point in the history
Get the IPython notebook viewer working with IPython/Jupyter 4
  • Loading branch information
rmcgibbo committed Oct 11, 2015
2 parents 47cc323 + c55ddad commit af6628b
Show file tree
Hide file tree
Showing 10 changed files with 214 additions and 81 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,4 @@ nosetests.xml

# PyCharm
.idea
mdtraj/tests/.ipynb_checkpoints
3 changes: 3 additions & 0 deletions devtools/conda-recipe/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ test:
- nose
- scripttest
- networkx
- ipython-notebook
- ipywidgets
- matplotlib
# Hack to install shiftx2 on travis
{{ '- shiftx2' if environ.get('TRAVIS', False) == 'true' }}

Expand Down
6 changes: 3 additions & 3 deletions docs/viewer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@ And then at the end of a cell, to render the viewer in the output space, use a :
Requirements
------------

`IPython <http://ipython.org/>`_ >= 3.0
`IPython <http://ipython.org/>`_ >= 4.0
To install IPython and the notebook with ``conda``,
use ``conda install ipython-notebook``.
use ``conda install ipython-notebook ipywidgets``.

Modern web browser with WebGL
We've had the best luck with Chrome and modern versions
of Firefox.
of Firefox.
9 changes: 3 additions & 6 deletions mdtraj/html/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
try:
from .install import enable_notebook
from .trajectory_widget import TrajectoryView, TrajectorySliderView
from .linked_heatmap import TrajectoryHeatmap
except ImportError:
pass
from .install import enable_notebook
from .trajectory_widget import TrajectoryView, TrajectorySliderView
from .linked_heatmap import TrajectoryHeatmap
22 changes: 13 additions & 9 deletions mdtraj/html/imagebutton_widget.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import base64
import matplotlib.pyplot as pp
from six.moves import StringIO
from IPython.utils.traitlets import Any
from IPython.html.widgets import DOMWidget, CallbackDispatcher
from IPython.utils.traitlets import Unicode, CUnicode, Bool, Bytes
try:
from ipywidgets.widgets import DOMWidget, CallbackDispatcher
from traitlets import Any, Unicode, CUnicode, Bool, Bytes
except ImportError:
from IPython.html.widgets import DOMWidget, CallbackDispatcher
from IPython.utils.traitlets import Any, Unicode, CUnicode, Bool, Bytes

from matplotlib.backends.backend_agg import FigureCanvasAgg

__all__ = ['MPLFigureButton', 'ImageButton']
Expand All @@ -17,7 +21,7 @@ class ImageButton(DOMWidget):
width = CUnicode(sync=True)
height = CUnicode(sync=True)
_b64value = Unicode(sync=True)

value = Bytes()
def _value_changed(self, name, old, new):
self._b64value = base64.b64encode(new)
Expand All @@ -29,23 +33,23 @@ def __init__(self, **kwargs):

def on_click(self, callback, remove=False):
self._click_handlers.register_callback(callback, remove=remove)

def _handle_button_msg(self, _, content):
if content.get('event', '') == 'click':
self._click_handlers(self, content)
self._click_handlers(self, content)


class MPLFigureButton(ImageButton):
fig = Any()

def __init__(self, **kwargs):
super(MPLFigureButton, self).__init__(**kwargs)
if self.fig is None:
self.fig = pp.gcf()

def _fig_changed(self, name, old, new):
self._sync_b64value(new)

def _sync_b64value(self, figure):
buf = StringIO()
FigureCanvasAgg(figure).print_png(buf)
Expand Down
33 changes: 17 additions & 16 deletions mdtraj/html/install.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import os
import warnings
from IPython.display import display, Javascript
from IPython.html.nbextensions import install_nbextension
try:
from notebook.nbextensions import install_nbextension
except ImportError:
from IPython.html.nbextensions import install_nbextension

with warnings.catch_warnings():
warnings.simplefilter("ignore")
from pkg_resources import resource_filename
Expand All @@ -12,12 +16,12 @@
require.config({
paths: {
'three': '//cdnjs.cloudflare.com/ajax/libs/three.js/r68/three.min',
'iview' : '/nbextensions/iview',
'surface' : '/nbextensions/surface.min',
'exporter' : '/nbextensions/objexporter',
'filesaver' : '/nbextensions/filesaver',
'iview' : '/nbextensions/mdtraj/iview',
'surface' : '/nbextensions/mdtraj/surface.min',
'objexporter' : '/nbextensions/mdtraj/objexporter',
'filesaver' : '/nbextensions/mdtraj/filesaver',
'jqueryui': '//ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/jquery-ui.min',
'contextmenu': '/nbextensions/context',
'contextmenu': '/nbextensions/mdtraj/context',
},
shim: {
three: {
Expand All @@ -30,13 +34,17 @@
surface: {
exports: 'ProteinSurface'
},
exporter: {
objexporter: {
deps: ['three'],
exports: 'THREE.OBJExporter'
},
jqueryui: {
exports: "$"
},
contextmenu: {
deps: ['jquery'],
exports: "context"
},
},
});
''',
Expand All @@ -48,13 +56,6 @@ def enable_notebook():
This function should be called before using TrajectoryWidget.
"""
libs = ['iview.js','surface.min.js','objexporter.js','filesaver.js','context.js']
fns = [resource_filename('mdtraj', os.path.join('html', 'static', f)) for f in libs]
for fn in fns:
install_nbextension(fn, verbose=0, user=True)
display(_REQUIRE_CONFIG)

widgets = ['widget_trajectory.js', 'widget_imagebutton.js']
for fn in widgets:
fn = resource_filename('mdtraj', os.path.join('html', 'static', fn))
display(Javascript(filename=fn))
staticdir = resource_filename('mdtraj', os.path.join('html', 'static'))
install_nbextension(staticdir, destination='mdtraj', user=True)
7 changes: 5 additions & 2 deletions mdtraj/html/linked_heatmap.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import numpy as np
from scipy.spatial import cKDTree
import matplotlib.pyplot as pp
from IPython.html.widgets import ContainerWidget
try:
from ipywidgets.widgets import Box
except ImportError:
from IPython.html.widgets import Box
from .trajectory_widget import TrajectoryView
from .imagebutton_widget import MPLFigureButton

Expand All @@ -28,4 +31,4 @@ def callback(b, content):
viewer.frame = index

heatmap.on_click(callback)
return ContainerWidget(children=(heatmap, viewer))
return Box(children=(heatmap, viewer))
75 changes: 39 additions & 36 deletions mdtraj/html/static/widget_trajectory.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,21 @@ protein visualization) hooked in. Changes to the class on the python side
propagate here and modify `this.model.attributes`, and re-call `update`.
*/

require([

define([
"jquery",
"widgets/js/widget",
"widgets/js/manager",
"nbextensions/widgets/widgets/js/widget",
"iview",
"exporter",
"filesaver",
"contextmenu",
"three",
"filesaver",
// only loaded, not used
'jqueryui',
],
"objexporter",
"jqueryui",

], function ($, widget, iview, context, THREE) {
"use strict";

function($, widget, manager, iview) {
var HEIGHT = 300,
WIDTH = 300,
HEIGHT_PX = '300px',
Expand All @@ -33,23 +35,24 @@ function($, widget, manager, iview) {
resize: function(event, ui) {
iv.renderer.setSize(ui.size.width, ui.size.height);
},
stop : function(event, ui) {
iv.render()
},
stop : function(event, ui) {
iv.render()
},
});

container.append(canvas);
this.setElement(container);
this.iv = iv;
this.setupContextMenu(iv);
this.setupFullScreen(canvas, container);
this.update();
var options = this.getOptions()
var options = this.getOptions()
this.iv.zoomInto(options);


// debugging
window.iv = this.iv;
window.model = this.model;
console.log('Render!');
},

update : function () {
Expand All @@ -65,14 +68,14 @@ function($, widget, manager, iview) {
this.iv.loadTopology(this.model.attributes._topology);
this.iv.loadCoordinates(this.model.attributes._frameData.coordinates);
this.iv.loadAtomAttributes(this.model.attributes._frameData.secondaryStructure);
var options = this.getOptions()

var options = this.getOptions()
this.iv.rebuildScene(options)
this.iv.render()

return TrajectoryView.__super__.update.apply(this);
},

setupContextMenu : function(iv) {
context.init({preventDoubleContext: true});
var menu = [{header: 'Export as...'},
Expand All @@ -82,12 +85,12 @@ function($, widget, manager, iview) {
var data = atob( dataURL.substring( "data:image/png;base64,".length ) ),
asArray = new Uint8Array(data.length);
for( var i = 0, len = data.length; i < len; ++i ) {
asArray[i] = data.charCodeAt(i);
asArray[i] = data.charCodeAt(i);
}
var blob = new Blob( [ asArray.buffer ], {type: "image/png"} );
saveAs(blob,"mol.png")
}
}, {
}, {
text: 'OBJ',
action: function () {
var obj = '';
Expand All @@ -100,7 +103,7 @@ function($, widget, manager, iview) {
}
}];
context.attach('canvas',menu)

},

getOptions : function() {
Expand All @@ -113,7 +116,7 @@ function($, widget, manager, iview) {
'secondaryStructure': this.model.attributes.secondaryStructure,
'surface': this.model.attributes.surfaceRepresentation
};

return options
},

Expand All @@ -135,30 +138,30 @@ function($, widget, manager, iview) {
iv.renderer.setSize(minHW, minHW);
iv.render();
}
}
}
});

if ('webkitCancelFullScreen' in document) {
document.addEventListener("webkitfullscreenchange", function() {
document.addEventListener("webkitfullscreenchange", function() {
if (!document.webkitIsFullScreen) {
container.css({width: HEIGHT_PX, height: WIDTH_PX});
iv.renderer.setSize(WIDTH, HEIGHT);
iv.render();
container.css({width: HEIGHT_PX, height: WIDTH_PX});
iv.renderer.setSize(WIDTH, HEIGHT);
iv.render();
}
});
} else if ('mozCancelFullScreen' in document) {
document.addEventListener("mozfullscreenchange", function() {
});
} else if ('mozCancelFullScreen' in document) {
document.addEventListener("mozfullscreenchange", function() {
if (!document.mozIsFullScreen) {
iv.renderer.setSize(WIDTH, HEIGHT);
container.css({width: HEIGHT_PX, height: WIDTH_PX});
iv.render();

iv.renderer.setSize(WIDTH, HEIGHT);
container.css({width: HEIGHT_PX, height: WIDTH_PX});
iv.render();
}
});
}
});
}
},
});


manager.WidgetManager.register_widget_view('TrajectoryView', TrajectoryView);
return {
'TrajectoryView': TrajectoryView,
};
});
Loading

0 comments on commit af6628b

Please sign in to comment.