How to use FabricJS in Jupyter Notebook widget - jupyter-notebook

I'm trying to build a widget in Jupyter Notebook that uses Fabric.js (http://fabricjs.com/), however I'm getting an error that is a blocker for me. The most basic solution I need is just to make the widget output a canvas with an interactive red rectangle, like what you find on the Fabric.js homepage:
What I've tried so far:
I started from the basic "Hello World" tutorial (https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20Custom.html) which is the basis for the four cells below, and I tried to add a simple example from the fabric node webpage to create a red rectangle. Here are the cells I have in Jupyter notebook:
Cell 1:
%%HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.7.0/fabric.min.js" type="text/javascript"></script>
Cell 2:
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)
Cell 3:
%%javascript
require.undef('hello');
define('hello', ["#jupyter-widgets/base"], function(widgets) {
var HelloView = widgets.DOMWidgetView.extend({
render: function() {
var canvas = document.createElement('canvas');
canvas.id = 'canvas';
canvas.width = 1000;
canvas.height = 500;
var ctx = canvas.getContext("2d");
ctx.fillStyle = "blue";
ctx.fillRect(0, 0, canvas.width, canvas.height);
this.el.appendChild(canvas);
var fabricCanvas = new fabric.Canvas(canvas);
var rect = new fabric.Rect({
top : 100,
left : 100,
width : 60,
height : 70,
fill : 'red'
});
fabricCanvas.add(rect);
},
});
return {
HelloView : HelloView
};
});
Cell 4:
HelloWidget()
However, I unfortunately get the following error in the JS console and it doesn't make the red square:
Please help me fix the code to make it work!

My problem was I didn't understand how require.js works... :/
Here's how I fixed the problem:
%%javascript
require.undef('hello');
require.config({
//Define 3rd party plugins dependencies
paths: {
fabric: "https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.7.0/fabric.min"
}
});
define('hello', ["#jupyter-widgets/base", 'fabric'], function(widgets) {...

Related

html2canvas is working in IE, not working in google chrome

I'm trying to export the html part as a pdf file using html2canvas and jsPDF libraries. However, this functionality is working fine in IE and the contents that are available in the window scope is available in the exported pdf where the content inside the scroll bar is not available in chrome. The part has multiple rows where each row is iterated using angularjs ng-repeat and each row has customized css part. Each row should be exported with the applied css and the dynamic data that is available in the screen. Posting the codefor your reference,
Chrome Image
IE Image
Script Code:
$scope.exportFunctionViewData = function(){
html2canvas(document.getElementById('functionViewExport') , {
onrendered: function (canvas) {
var content = canvas.toDataURL('image/jpeg');
var imgWidth = 210;
var pageHeight = 295;
var imgHeight = canvas.height * imgWidth / canvas.width;
var heightLeft = imgHeight;
var doc = new jsPDF('p', 'mm');
var position = 0;
doc.addImage(content, 'JPEG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
while (heightLeft >= 0) {
position = heightLeft - imgHeight;
doc.addPage();
doc.addImage(content, 'JPEG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
doc.save($scope.title + '-FunctionView.pdf');
}
});
};

Testing HTML5 canvas in Grunt, qUnit and PhantomJS

I'm trying to create a simple qUnit test that should run in grunt and phantomJs using grunt-contrib-qunit.
The test runs fine in qUnit, but fails when using grunt and phantomJS.
The test code is:
test("testing the filter", function() {
var img = new Image(3, 2);
// this is a 3x2 image
img.src = "data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAIAAAASFvFNAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wIFDScubGmL8QAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAAG0lEQVQI1wXBAQ0AAAwCINz7hzKZhwzDLjTaPDVzBUhAyQZRAAAAAElFTkSuQmCC";
var canvas = document.createElement('canvas');
canvas.width = 3,
canvas.height = 2;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
ok(ctx.getImageData(0,0,1,1).data[0] === 255, 'Expected red-pixel to be 255, actually is ' + ctx.getImageData(0,0,1,1).data[0])
})
When I run this in a browser based qUnit, the test passes. When I run this in Grunt, using PhantomJS and qUnit, the test fails. As best I can tell, drawImage is failing silently, so it's not writing the pixels.
Everything else seems to be working correctly. All my other tests run fine, including DOM manipulation tests. It's just this one function that doesn't appear to work correctly.
It turns out that I was making this much more complex than it had to be. The image simply wasn't loaded at the time I was trying to draw the canvas. Fortunately, there is a way to do an asynchronous test in qUnit, so all I had to do was run the test after the image was loaded.
The weirdness was that it was running correctly in the browser based qUnit tests, but not phantomJs.
The working code is below:
test("testing the filter", function( assert ) {
var done = assert.async();
var img = document.createElement('img');
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width = 3,
canvas.height = 2;
img.addEventListener("load", function() {
ctx.drawImage(img, 0, 0);
ok(ctx.getImageData(0,0,1,1).data[0] === 255, 'Expected red-pixel to be 255, actually is ' + ctx.getImageData(0,0,1,1).data[0]);
done();
})
// this is a 3x2 image
img.src = "data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAIAAAASFvFNAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wIFDScubGmL8QAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAAG0lEQVQI1wXBAQ0AAAwCINz7hzKZhwzDLjTaPDVzBUhAyQZRAAAAAElFTkSuQmCC";
})

copy a div to a window popup with css

I'm trying to take a div and basically put it in a child window. The domains are going to be the same so I figure I could do the flowing ( where parm win is the div ).
popOut = function ( win )
{
var newWindow = window.open( "about:blank", "", "toolbar=yes, scrollbars=yes, resizable=yes, top=500, left=500, width=400, height=400", false );
newWindow.onload = function ()
{
this.focus
this.document.body.appendChild( win );
};
}
This seems to do the job but the css is clobbered. In thinking about this, the new window certainly knows nothing of the includes and css files. Can I copy or inject this info over somehow?
Would I be correct in assuming the new window knows nothing of its parent, thus no function can be ran from child to parent?
New window is definitely aware of its opener as well as opener aware of the popup. If the problem is new window not seeing opener styles, the following code should copy styles from opener to popup.
Place this after var newWindow = window.open(...)
var linkrels = document.getElementsByTagName('link');
for (var i = 0, max = linkrels.length; i < max; i++) {
if (linkrels[i].rel && linkrels[i].rel == 'stylesheet') {
var thestyle = document.createElement('link');
var attrib = linkrels[i].attributes;
for (var j = 0, attribmax = attrib.length; j < attribmax; j++) {
thestyle.setAttribute(attrib[j].nodeName, attrib[j].nodeValue);
}
newWindow.document.documentElement.appendChild(thestyle);
}
}
I haven't got a chance to test it, but something like this should work.

How do I make highlighted Google Map markers grow?

function highlightmarker(marker_id) {
//console.log("Highlight marker id: "+marker_id);
for (var i=0; i<markers.length; i++) {
if (markers[i].id == marker_id) {
map_markers[i].setAnimation(google.maps.Animation.BOUNCE);
}
}
}
This makes my map markers bounce up and down but I want them to grow instead.
I tried replacing the last line with this but it didn't work:
var img_height = 40;
var img_width = 40;
How do I make highlighted Google Map markers grow?
You could use a Icon (or a MarkerImage that's still supported but deprecated in favor of Icon) and use a the common marker image in it and set the appropriate size/scale you desire, for instance, assuming markers[i] points to a valid marker
var image = {
url: " http://www.google.com/mapfiles/marker.png",
size: new google.maps.Size(50, 50),
origin: null,
anchor: null,
scaledSize: new google.maps.Size(50, 50)
};
marker[i].setIcon(image);
for a more complete example I created a jsFiddle that sets the icon upon marker creation, http://jsfiddle.net/68gx9/
instead of
map_markers[i].setAnimation(google.maps.Animation.BOUNCE);
replace marker image with a new bigger one..
map_markers[i].setIcon(yourImageUrl);

Flot chart title option?

Does Flot have an option that can be set to give the chart a title? I'm not seeing one for the overall chart, just for the axes.
But I might have missed something.
I do not think this option exists. It is pretty easy, though, to title the plot using regular HTML. Just wrap a div around your "placeholder" div and add the title text to that.
after drawing flot chart (plot function) fill canvas with text (jsFiddle). Advantage of my solution is that you can save chart as image containing title on it.
example:
var c=document.getElementsByTagName("canvas")[0];
var canvas=c.getContext("2d");
var cx = c.width / 2;
var text="Flot chart title";
canvas.font="bold 20px sans-serif";
canvas.textAlign = 'center';
canvas.fillText(text,cx,35);
You can use hooks for that. For instance use the overlay hook, and implement your overlay functionality in a separate OverlayHandler
Here is an example, where the chartElement, chartDataand chartOptions are your HTML element and flot data and flot options, respectively.:
var plotOverlayHandler = function(plot, cvs) {
if(!plot) { return; }
var cvsWidth = plot.width() / 2;
var text = 'Flot chart-title!';
cvs.font = "bold 16px Open Sans";
cvs.fillStyle = "#666666";
cvs.textAlign = 'center';
cvs.fillText(text, cvsWidth, 30);
return cvs;
};
var plot = $.plot(chartElement, chartData, chartOptions);
plot.hooks.drawOverlay.push( plotOverlayHandler );
When exporting the canvas via the native toDataURL method, simply apply the OverlayHandler first. This allows for greater flexibility.
var elCanvas = $('canvas.flot-base').first();
var canvas = plotCanvasOverlay(elCanvas, elCanvas.get(0).getContext('2d'))
var dataUrl = canvas.toDataURL('image/png');
Pioja's answer is indeed a great one. His jsFiddle shows the full details. It is important to have the following included in your options:
canvas: true,
grid: {
margin: { top:50 }
}
This will then insert a nice chart title which can be included in the image if you export it.
Building on pioja's answer, the title can be set directly after the plot has been made with:
var plot = $.plot($("#"+PlotPlaceholder), data, options);
By using the getCanvas function:
var c = plot.getCanvas();
Now, just follow pioja's code to get:
var canvas=c.getContext("2d");
var cx = c.width / 2;
var text="Flot chart title";
canvas.font="bold 20px sans-serif";
canvas.textAlign = 'center';
canvas.fillText(text,cx,35);

Resources