import sys, time from IPython.utils.traitlets import HasTraits, Integer, Unicode, Bool, Instance, Float from IPython.display import display from IPython.kernel.comm import Comm class RangeWidget(HasTraits): """A Widget for a value on a Range""" min = Float(0) max = Float(100) value = Float(50) step = Float(1) _keys = ['min', 'max', 'value', 'step'] _handling = Bool(False) comm = Instance(Comm) def _comm_default(self): return Comm(target='range', _open_data=self.data) def _validate_and_update(self, name, old, new): if self.value > self.max: raise ValueError("value %d exceeds max %d" % (self.value, self.max)) if new < self.min: raise ValueError("value %d below min %d" % (self.value, self.min)) if not self._handling: self.update() def __init__(self, **kwargs): super(RangeWidget, self).__init__(**kwargs) # register on_trait_change after init # otherwise default values can't be specified self.on_trait_change(self._validate_and_update, self._keys) # register handler on message self.comm.on_msg(self.handle_msg) @property def data(self): return dict((key, getattr(self, key)) for key in self._keys) def update(self): """send my state to my js peer""" self.comm.send(self.data) def __repr__(self): return "" % (self.value, self.min, self.max, self.step) def handle_msg(self, data): self._handling = True try: for key in self._keys: if key in data: setattr(self, key, data[key]) finally: self._handling = False sys.stdout.write('\r%s' % self) sys.stdout.flush() def display(self): display(self) self.update() def _repr_html_(self): return """
""" % self.comm.comm_id %%javascript var RangeWidget = function (comm) { this.comm = comm; this.keys = ['min', 'max', 'step']; $([this.comm]).on("comm_open", $.proxy(this.handle_open, this)); $([this.comm]).on("comm_msg", $.proxy(this.handler, this)); }; RangeWidget.prototype.get_div = function () { var div = $("div." + this.comm.comm_id); if (div.length === 0) { return null; } if (div.find("input").length === 0) { this.create_range(div); } return div; }; RangeWidget.prototype.create_range = function (thediv) { var range = $('').attr('type', 'range'); var data = this.data; for (var i=0; i < this.keys.length; i++) { var key = this.keys[i]; range.attr(key, data[key]); } range.val(data.value); thediv.append( $("").addClass("min").text(data.min) ).append( range ).append( $("").addClass("max").text(data.max) ).append( $("
") ).append( $("").addClass("value").text(data.value) ); range.on("change", $.proxy(this.value_changed, this)); } RangeWidget.prototype.handle_open = function (evt, data) { console.log('range open', this, evt, data); this.data = data; this.get_div(); }; RangeWidget.prototype.value_changed = function (evt) { var now = new Date(); if (now - this.last_changed < 100) { // only update every 0.1s, max // return; } var div = this.get_div(); var range = div.find("input"); var data = {}; for (var i=0; i < this.keys.length; i++) { var key = this.keys[i]; data[key] = parseFloat(range.attr(key)); } data.value = parseFloat(range.val()); div.find("span.min").text(data.min); div.find("span.max").text(data.max); div.find("span.value").text(data.value); this.comm.send(data); this.last_changed = now; }; RangeWidget.prototype.handler = function (evt, data) { console.log('range update', evt, data); this.data = data; var div = this.get_div(); if (!div) { return; } var range = div.find("input"); for (var i=0; i < this.keys.length; i++) { var key = this.keys[i]; range.attr(key, data[key]); } div.find("span.min").text(data.min); div.find("span.max").text(data.max); div.find("span.value").text(data.value); range.val(data.value); }; var new_range = function (comm) { var r = new RangeWidget(comm); }; IPython.comm_manager.register_target('range', function (comm) { var r = new RangeWidget(comm);}); r = RangeWidget() r.display() r.value = 5 print r import math, time import numpy as np x = np.linspace(0,10,1000) y = 50 + (np.sin(x) * 50) for n in y: r.value = (n) time.sleep(0.005) from IPython.utils.traitlets import List class FlotWidget(HasTraits): lines = List() comm = Instance(Comm, kw=dict(target='flotplot')) def show(self): self.comm.send(dict(lines=self.lines)) def plot(self, x, y): self.lines.append(list(zip(x,y))) self.show() def clear(self): self.lines = [] self.show() def _repr_html_(self): return '
' % self.comm.comm_id %%javascript $.getScript('//cdnjs.cloudflare.com/ajax/libs/flot/0.8.1/jquery.flot.min.js'); $("").appendTo("head"); var FlotWidget = function (comm) { this.comm = comm; $([this.comm]).on("comm_msg", $.proxy(this.handler, this)); }; FlotWidget.prototype.handler = function (evt, data) { // console.log('flot', data); $.plot($('#' + this.comm.comm_id), data.lines); } IPython.comm_manager.register_target('flotplot', function (comm) { var f = new FlotWidget(comm); console.log(f)}); flot = FlotWidget() flot flot.clear() x = np.linspace(0,5,200) y = np.sin(x) flot.plot(x,np.sin(x)) flot.plot(x,np.cos(x)) flot.plot(x, np.sin(2*x)) from IPython.utils.traitlets import HasTraits, Instance, Any class SineOmegaT(HasTraits): omega = Instance(RangeWidget, kw=dict(min=0, max=10, step=0.1, value=1), allow_none=False) flot = Instance(FlotWidget, args=()) # can't use traitlets with numpy arrays t = None y = None _in_omega_changed = False def _omega_value_changed(self, name, old, new): self.y = np.sin(new * self.t) self.show() def __init__(self): super(SineOmegaT, self).__init__() self.omega.on_trait_change(self._omega_value_changed, 'value') self.t = np.linspace(0,10,200) self.y = np.sin(self.omega.value * self.t) # add HTML to the dom display(self) # ensure omega slider is drawn self.omega.update() self.last_draw = 0 self.show() def show(self): now = time.time() if False and (now - self.last_draw) < 0.1: return self.last_draw = now self.flot.lines = [] self.flot.plot(self.t, self.y) def _repr_html_(self): return u"$$sin(\omega t)$$
%s
$\omega$: %s" % (self.flot._repr_html_(), self.omega._repr_html_()) sino = SineOmegaT() sino.omega.max = 30 sino.omega.step = 1 sino.t = np.linspace(0,100,10) sino.show() from IPython.kernel.comm import Comm from IPython.core.getipython import get_ipython class RPC(object): """An object whose methods are exposed via RPC""" def __init__(self, comm): self.comm = comm self.comm.on_msg(self.handler) def add(self, a, b): return a + b def mul(self, x, y): return x * y def get_execution_count(self): return get_ipython().execution_count def handler(self, data): """this implements RPC""" method = getattr(self, data['method']) args = data.get('args', ()) kwargs = data.get('kwargs', {}) result = method(*args, **kwargs) self.comm.send(dict(result=result, method=data['method'])) get_ipython().comm_manager.register_target('rpc', RPC) %%javascript var RPCProxy = function () { console.log(this); this.comm = new IPython.Comm(IPython.utils.uuid()); this.comm.target = 'rpc'; $([this.comm]).on("comm_msg", $.proxy(this.handler, this)); IPython.comm_manager.register_comm(this.comm); this.comm.open(); try { this.notification = IPython.notification_area.get_widget('rpc'); } catch (e) { this.notification = IPython.notification_area.new_notification_widget('rpc'); } }; RPCProxy.prototype.handler = function (evt, data) { console.log('rpc', data); this.notification.set_message(JSON.stringify(data), 5000); } RPCProxy.prototype.add = function (a,b) { this.comm.send({method : 'add', args : [a,b]}); } RPCProxy.prototype.mul = function (a,b) { this.comm.send({method : 'mul', args : [a,b]}); } RPCProxy.prototype.get_ec = function () { this.comm.send({method : 'get_execution_count'}); } var rpc = new RPCProxy(); rpc.add(5,3); setTimeout(function () { rpc.get_ec(); }, 2000); setTimeout(function () { rpc.mul(6,6); }, 4000);