%matplotlib inline import matplotlib.pylab as plt import mpld3 mpld3.enable_notebook() class SliderView(mpld3.plugins.PluginBase): """ Add slider and JavaScript / Python interaction. """ JAVASCRIPT = """ mpld3.register_plugin("sliderview", SliderViewPlugin); SliderViewPlugin.prototype = Object.create(mpld3.Plugin.prototype); SliderViewPlugin.prototype.constructor = SliderViewPlugin; SliderViewPlugin.prototype.requiredProps = ["idline", "callback_func"]; SliderViewPlugin.prototype.defaultProps = {} function SliderViewPlugin(fig, props){ mpld3.Plugin.call(this, fig, props); }; SliderViewPlugin.prototype.draw = function(){ var line = mpld3.get_element(this.props.idline); var callback_func = this.props.callback_func; var div = d3.select("#" + this.fig.figid); // Create slider div.append("input").attr("type", "range").attr("min", 0).attr("max", 10).attr("step", 0.1).attr("value", 1) .on("change", function() { var command = callback_func + "(" + this.value + ")"; console.log("running "+command); var callbacks = { 'iopub' : {'output' : handle_output}}; var kernel = IPython.notebook.kernel; kernel.execute(command, callbacks, {silent:false}); }); function handle_output(out){ //console.log(out); var res = null; // if output is a print statement if (out.msg_type == "stream"){ res = out.content.data; } // if output is a python object else if(out.msg_type === "pyout"){ res = out.content.data["text/plain"]; } // if output is a python error else if(out.msg_type == "pyerr"){ res = out.content.ename + ": " + out.content.evalue; alert(res); } // if output is something we haven't thought of else{ res = "[out type not implemented]"; } // Update line data line.data = JSON.parse(res); line.elements() .attr("d", line.datafunc(line.data)) .style("stroke", "black"); } }; """ def __init__(self, line, callback_func): self.dict_ = {"type": "sliderview", "idline": mpld3.utils.get_id(line), "callback_func": callback_func} import numpy as np def updateSlider(val1): t = np.linspace(0, 10, 500) y = np.sin(val1*t) return map(list, list(zip(list(t), list(y)))) fig, ax = plt.subplots(figsize=(8, 4)) t = np.linspace(0, 10, 500) y = np.sin(t) ax.set_xlabel('Time') ax.set_ylabel('Amplitude') # create the line object line, = ax.plot(t, y, '-k', lw=3, alpha=0.5) ax.set_ylim(-1.2, 1.2) ax.set_title("Slider demo") mpld3.plugins.connect(fig, SliderView(line, callback_func="updateSlider")) from IPython import display display.HTML(""" """) class MyUserInterface(mpld3.plugins.PluginBase): """ Here we use Backbone to create more structured Javascript. """ JAVASCRIPT = """ var LineModel = Backbone.Model.extend({ initialize: function(options) { this.options = options || {}; this.on("change:sliderPosition", this.modelChanged); this.on("change:boundaryCondition", this.notImplemented); this.on("change:youngsModulus", this.notImplemented); this.on("change:useFEM", this.notImplemented); }, /** This example should be quite easy to extend to use more inputs. You just have to pass more model.get('...') things to kernel execute command below. */ notImplemented: function(model) { alert("This function is not implemented in the example on purpose."); }, /** Model changed, execute notebook kernel and update model data. */ modelChanged: function(model) { var command = this.options.callback_func + "(" + model.get('sliderPosition') + ")"; console.log("IPython kernel execute "+command); var callbacks = { 'iopub' : { 'output' : function(out) { //console.log(out); var res = null; // if output is a print statement if (out.msg_type == "stream"){ res = out.content.data; } // if output is a python object else if(out.msg_type === "pyout"){ res = out.content.data["text/plain"]; } // if output is a python error else if(out.msg_type == "pyerr"){ res = out.content.ename + ": " + out.content.evalue; alert(res); } // if output is something we haven't thought of else{ res = "[out type not implemented]"; alert(res); } model.set("line", JSON.parse(res)); } } }; IPython.notebook.kernel.execute(command, callbacks, {silent:false}); } }); var ToolsView = Backbone.View.extend({ /** This view renders toolbar with slider and other html elements. */ initialize: function(options) { this.options = options || {}; _.bindAll(this, 'render'); }, render: function() { var template = _.template($("#tools-template").html(), {}); $(this.el).append(template); return this; }, /** Listen event changes. */ events: { "change #slider1": "changeSlider1", "change #boundary_conditions": "changeBoundaryConditions", "change #young": "changeModulus", "change #useFEM": "changeUseFEM" }, changeSlider1: function(ev) { var sliderPosition = $(ev.currentTarget).val(); this.model.set('sliderPosition', sliderPosition); $(this.el).find("#slider1label").text(parseFloat(sliderPosition).toFixed(2)); }, changeBoundaryConditions: function(ev) { this.model.set('boundaryCondition', $(ev.currentTarget).val()); }, changeModulus: function(ev) { this.model.set('youngsModulus', $(ev.currentTarget).val()); }, changeUseFEM: function(ev) { var isChecked = $(ev.currentTarget).is(":checked"); this.model.set('useFEM', isChecked); } }); var CanvasView = Backbone.View.extend({ initialize: function(options) { this.options = options || {}; this.line = mpld3.get_element(this.options.props.idline); _.bindAll(this, 'render'); this.model.bind('change:line', this.render); }, /** Update line when model changes, f.e. new data is calculated inside notebook and updated to Backbone model. */ render: function() { this.line.elements().transition() .attr("d", this.line.datafunc(this.model.get('line'))) .style("stroke", "black"); } }); // PLUGIN START mpld3.register_plugin("myuserinterface", MyUserInterfacePlugin); MyUserInterfacePlugin.prototype = Object.create(mpld3.Plugin.prototype); MyUserInterfacePlugin.prototype.constructor = MyUserInterfacePlugin; MyUserInterfacePlugin.prototype.requiredProps = ["idline", "callback_func"]; MyUserInterfacePlugin.prototype.defaultProps = {} function MyUserInterfacePlugin(fig, props){ mpld3.Plugin.call(this, fig, props); }; MyUserInterfacePlugin.prototype.draw = function() { // Some hacking to get proper layout. var div = $("#" + this.fig.figid).attr("style", "border: 1px solid;"); var figdiv = div.find("div"); figdiv.attr("style", "display: inline;"); // Create LineModel var lineModel = new LineModel({ callback_func: this.props.callback_func }); // Create tools view var myel = $('
'); div.append(myel); var toolsView = new ToolsView({ el: myel, model: lineModel }); toolsView.render(); // Create canvas view which updates line visualization when the model is changed var canvasView = new CanvasView({ el: figdiv, model: lineModel, props: this.props }); }; """ def __init__(self, line, callback_func): self.dict_ = {"type": "myuserinterface", "idline": mpld3.utils.get_id(line), "callback_func": callback_func} import numpy as np L = 1.0 F = 3.0 E = 100.0 I = 0.1 def v_(x, a): b = L - a v = a*b/L**2*(L+b)*x/L - b*(x/L)**3 if x-a > 0.0: v += 1.0/L**2*(x-a)**3 v *= F*L**2/(6.0*E*I) return v v = np.vectorize(v_) def runCalculation(a): """ """ x = np.linspace(0, L, 500) y = -v(x, a)*1000.0 return map(list, list(zip(list(x), list(y)))) fig, ax = plt.subplots(figsize=(8, 4)) t = np.linspace(0, 1, 200) y = np.sin(t) ax.set_xlabel('x [m]') ax.set_ylabel('Deflection [mm]') ax.set_title('Euler-Bernoulli beam deflection line') # create the line object initial_data = np.array(runCalculation(0.5)) line, = ax.plot(initial_data[:, 0], initial_data[:, 1], '-k', lw=3, alpha=0.5) ax.plot([0.975, 1.025, 1.00, 0.975], [-1, -1, 0, -1], '-k', lw=1) ax.plot([-0.025, 0.025, 0.000, -0.025], [-1, -1, 0, -1], '-k', lw=1) ax.set_ylim(-10, 5) ax.grid(lw=0.1, alpha=0.2) mpld3.plugins.connect(fig, MyUserInterface(line, callback_func="runCalculation"))