This simple notebook demonstrates:
In [1]:
import ipywidgets as widgets
from traitlets import Int, Unicode, List
class CircleWidget(widgets.DOMWidget):
_view_name = Unicode('CircleView').tag(sync=True)
_view_module = Unicode('circle').tag(sync=True)
newCircleRequest = List().tag(sync=True)
circleCount = Int(0).tag(sync=True);
def drawCircle(self, x, y, radius):
newCircle = {"x": x, "y": y, "radius": radius};
self.newCircleRequest = [newCircle];
def getCount(self):
return(self.circleCount);
In [5]:
%%javascript
"use strict";
require.config({
paths: {
d3: '//cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.min'
},
});
require.undef('circle');
define('circle', ["jupyter-js-widgets", "d3"], function(widgets, d3) {
var CircleView = widgets.DOMWidgetView.extend({
initialize: function() {
this.options = {}; // if this is missing: "Error setting state: view.options is undefined"
this.circles = [];
this.circleCount = 0;
},
createDiv: function(){
var toolbarDiv = $("<div id='toolbarDiv' style='border:1px solid gray; height: 30px; width: 600px'></div>");
var div = $("<div id='d3DemoDiv' style='border:1px solid red; height: 300px; width: 600px'></div>");
div.append(toolbarDiv);
this.circleCountReadout = $("<input type='text' id='circleCountReadout' value='0'/>");
toolbarDiv.append(this.circleCountReadout);
var circleView = this;
function clearCircles() {
circleView.circles = [];
circleView.circleCount = 0;
$("#circleCountReadout").val(0);
$("#svg").children().remove();
circleView.model.set("circleCount", 0);
circleView.touch();
};
var clearButton = $('<button>Clear Circles</button>').click(clearCircles);
toolbarDiv.append(clearButton);
return(div);
},
createCanvas: function(){
debugger;
var svg = d3.select("#d3DemoDiv")
.append("svg")
.attr("id", "svg").attr("width", 600).attr("height", 300);
this.svg = svg;
var circleView = this;
svg.on('click', function() {
var coords = d3.mouse(this);
var newCircle = {x: coords[0], y: coords[1], radius: 20,
borderColor: "black", fillColor: "beige"};
circleView.circles.push(newCircle);
circleView.drawCircle(newCircle, "blue");
});
},
drawCircle: function(obj, color){
this.svg.append("circle")
.style("stroke", color)
.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");});
this.circleCount += 1;
$("#circleCountReadout").val(this.circleCount);
this.model.set("circleCount", this.circleCount);
this.touch();
},
render: function() {
this.$el.append(this.createDiv());
this.listenTo(this.model, 'change:newCircleRequest', this.newCircleRequested, this);
var circleView = this;
function delayCanvasCreationUntilDivExists(){
// would be better to trigger on DOM div creation. not knowing how to do that,
// this setTimeout hack will have to suffice.
circleView.createCanvas()
}
setTimeout(delayCanvasCreationUntilDivExists, 0);
},
newCircleRequested: function() {
var newCircle = this.model.get("newCircleRequest")[0];
this.circles.push(newCircle);
this.drawCircle(newCircle, "red");
}
});
return {
CircleView : CircleView
};
});
In [7]:
cw = CircleWidget(width=500, height=300)
cw
In [4]:
cw.drawCircle(x=30, y=30, radius=10)
In [5]:
cw.drawCircle(x=400, y=200, radius=30)
In [7]:
cw.getCount()
Out[7]:
In [ ]: