.. _customwidgetrst: ============================ Custom widgets in a notebook ============================ .. only:: html **Links:** :download:`notebook `, :downloadlink:`html `, :download:`PDF `, :download:`python `, :downloadlink:`slides `, :githublink:`GitHub|_doc/notebooks/custom_widget.ipynb|*` The notebook explore a couple of ways to interact with the user and modifies the output based on these interactions. This is inspired from the examples from `ipwidgets `__. .. code:: ipython3 from jyquickhelper import add_notebook_menu add_notebook_menu() .. contents:: :local: List of widgets --------------- `Widget List `__ .. code:: ipython3 import ipywidgets import datetime .. code:: ipython3 obj = ipywidgets.DatePicker( description='Pick a Date', disabled=False, value=datetime.datetime.now(), ) obj .. parsed-literal:: DatePicker(value=datetime.datetime(2020, 1, 14, 14, 41, 54, 26999), description='Pick a Date') .. code:: ipython3 obj.value .. parsed-literal:: datetime.datetime(2020, 1, 14, 14, 41, 54, 26999) Events ------ .. code:: ipython3 from IPython.display import display button = ipywidgets.Button(description="Click Me!") display(button) def on_button_clicked(b): print("Button clicked.") button.on_click(on_button_clicked) .. parsed-literal:: Button(description='Click Me!', style=ButtonStyle()) .. code:: ipython3 int_range = ipywidgets.IntSlider() display(int_range) def on_value_change(change): print(change['new']) int_range.observe(on_value_change, names='value') .. parsed-literal:: IntSlider(value=0) .. parsed-literal:: 14 18 22 23 25 26 27 28 27 24 22 19 17 16 15 14 12 11 10 9 8 7 6 5 4 3 2 1 2 5 6 8 10 11 12 13 14 matplotlib ---------- .. code:: ipython3 %matplotlib inline import matplotlib.pyplot as plt .. code:: ipython3 import networkx as nx def random_lobster(n, m, k, p): return nx.random_lobster(n, p, p / m) def powerlaw_cluster(n, m, k, p): return nx.powerlaw_cluster_graph(n, m, p) def erdos_renyi(n, m, k, p): return nx.erdos_renyi_graph(n, p) def newman_watts_strogatz(n, m, k, p): return nx.newman_watts_strogatz_graph(n, k, p) def plot_random_graph(n, m, k, p, generator): g = generator(n, m, k, p) nx.draw(g) plt.show() .. code:: ipython3 ipywidgets.interact(plot_random_graph, n=(2,30), m=(1,10), k=(1,10), p=(0.0, 1.0, 0.001), generator={ 'lobster': random_lobster, 'power law': powerlaw_cluster, 'Newman-Watts-Strogatz': newman_watts_strogatz, 'Erdős-Rényi': erdos_renyi, }); .. parsed-literal:: interactive(children=(IntSlider(value=16, description='n', max=30, min=2), IntSlider(value=5, description='m',… Custom widget - text -------------------- `Building a Custom Widget - Hello World `__. .. code:: ipython3 import ipywidgets as widgets from traitlets import Unicode, validate class HelloWidget(widgets.DOMWidget): _view_name = Unicode('HelloView').tag(sync=True) _view_module = Unicode('hello').tag(sync=True) _view_module_version = Unicode('0.1.0').tag(sync=True) value = Unicode('Hello World! - ').tag(sync=True) .. code:: javascript %%javascript require.undef('hello'); define('hello', ["@jupyter-widgets/base"], function(widgets) { var HelloView = widgets.DOMWidgetView.extend({ render: function() { this.value_changed(); this.model.on('change:value', this.value_changed, this); }, value_changed: function() { this.el.textContent = this.model.get('value'); }, }); return { HelloView : HelloView }; }); .. parsed-literal:: .. code:: ipython3 w = HelloWidget() w .. parsed-literal:: HelloWidget() .. code:: ipython3 w.value = 'changed the value' Custom widget - html - svg - events ----------------------------------- See `Low Level Widget Tutorial `__, `CircleView `__. The following example links a custom widget and a sliding bar which defines the radius of circle to draw. See `Linking two similar widgets `__. The information (circles, radius) is declared in a python class *CircleWidget* and available in the javascript code in two places: the widget (``this.model``) and the view itself (used to connect event to it). Finally, a link is added between two values: value from the first widget (sliding bar) and radius from the second widget (*CircleWidget*). .. code:: javascript %%javascript require.config({ paths: { d3: '//cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.min' }, }); .. parsed-literal:: .. code:: ipython3 import ipywidgets from traitlets import Int, Unicode, Tuple, CInt, Dict, validate class CircleWidget(ipywidgets.DOMWidget): _view_name = Unicode('CircleView').tag(sync=True) _view_module = Unicode('circle').tag(sync=True) radius = Int(100).tag(sync=True) circles = Tuple().tag(sync=True) width = Int().tag(sync=True) height = Int().tag(sync=True) radius = Int().tag(sync=True) def __init__(self, **kwargs): super(ipywidgets.DOMWidget, self).__init__(**kwargs) self.width = kwargs.get('width', 500) self.height = kwargs.get('height', 100) self.radius = 1 def drawCircle(self, x, y, fillColor="white", borderColor="black"): newCircle = {"x": x, "y": y, "radius": self.radius * 10, "fillColor": fillColor, "borderColor": borderColor} self.circles = self.circles + (newCircle,) .. code:: javascript %%javascript "use strict"; require.undef('circle'); define('circle', ["@jupyter-widgets/base", "d3"], function(widgets, d3) { var CircleView = widgets.DOMWidgetView.extend({ initialize: function() { console.log("---- initialize, this:"); console.log(this); this.circles = []; this.radius = 1; }, createDiv: function(){ var width = this.model.get('width'); var height = this.model.get('height'); var divstyle = $("
"); return(divstyle); }, createCanvas: function(){ var width = this.model.get('width'); var height = this.model.get('height'); var radius = this.model.get('radius'); console.log("--SIZE--", width, 'x', height, " radius", radius); var svg = d3.select("#d3DemoDiv") .append("svg") .attr("id", "svg").attr("width", width).attr("height", height); this.svg = svg; var circleView = this; svg.on('click', function() { var coords = d3.mouse(this); //debugger; var radius = circleView.radius; console.log('--MOUSE--', coords, " radius:", radius); var newCircle = {x: coords[0], y: coords[1], radius: 10 * radius, borderColor: "black", fillColor: "beige"}; circleView.circles.push(newCircle); circleView.drawCircle(newCircle); //debugger; circleView.model.set("circles", JSON.stringify(circleView.circles)); circleView.touch(); }); }, drawCircle: function(obj){ this.svg.append("circle") .style("stroke", "gray") .style("fill", "white") .attr("r", obj.radius) .attr("cx", obj.x) .attr("cy", obj.y) .on("mouseover", function(){d3.select(this).style("fill", "aliceblue");}) .on("mouseout", function(){d3.select(this).style("fill", "white");}); }, render: function() { this.$el.append(this.createDiv()); this.listenTo(this.model, 'change:circles', this._circles_changed, this); this.listenTo(this.model, 'change:radius', this._radius_changed, this); var circleView = this; function myFunc(){ circleView.createCanvas() }; setTimeout(myFunc, 500); }, _circles_changed: function() { var circles = this.model.get("circles"); var newCircle = circles[circles.length-1]; console.log('--DRAW--', this.circles); this.circles.push(newCircle); console.log('--LENGTH--', circles.length, " == ", circles.length); this.drawCircle(newCircle); }, _radius_changed: function() { console.log('--RADIUS--', this.radius, this.model.get('radius')); this.radius = this.model.get('radius'); } }); return { CircleView : CircleView }; }); .. parsed-literal:: .. code:: ipython3 cw = CircleWidget(width=500, height=100) scale = ipywidgets.IntSlider(1, 0, 10) box = widgets.VBox([scale, cw]) mylink = ipywidgets.jslink((cw, 'radius'), (scale, 'value')) box .. parsed-literal:: VBox(children=(IntSlider(value=1, max=10), CircleWidget(height=100, radius=1, width=500))) .. code:: ipython3 cw.drawCircle(x=30, y=30) .. code:: ipython3 scale.value = 2 .. code:: ipython3 cw.drawCircle(x=60, y=30)