Fix broken display of SVG using ipywidgets DOMWidget in Jupyter Notebook

Viewed 365

Summary

Please see this example notebook, which isn't working as expected. The third cell in the notebook should show an SVG window containing a simple green box. Example output is shown in cell 4*.

Details

After running the notebook, a tooltip popup is displayed when I hover over the element in the console. Because of this popup I believe the SVG container and box (which is a PATH element) are definitely being created, they just aren't being displayed. The popup says they are being rendered at size 0x0.

How can I get this to work as expected, so that the contents of the SVG window show up in the output cell (cell #3)?

Code

Here is the cell by cell code for convenience.

Cell #1

import ipywidgets.widgets as widgets
from traitlets import Unicode


class Test(widgets.DOMWidget):
    _view_name = Unicode('TestView').tag(sync=True)
    _view_module = Unicode('test').tag(sync=True)
    _view_module_version = Unicode('0.1.0').tag(sync=True)

Cell #2

%%javascript

require.undef('test');

define('test', ["@jupyter-widgets/base"], function(widgets) {
    var TestView = widgets.DOMWidgetView.extend({

        render: function() {
            TestView.__super__.render.apply(this, arguments);
            var svg = document.createElement('svg');
            svg.innerHTML = '<path fill-rule="evenodd" fill="#66cc99" stroke="#555555" stroke-width="2.0" opacity="0.6" d="M 0.0,0.0 L 50.0,0.0 L 50.0,50.0 L 0.0,50.0 L 0.0,0.0 z" />';
            this.el.appendChild(svg);
            console.log(svg);  // when you hover over this line in the console, you can see the SVG has been created...
        },
    });

    return {
        TestView : TestView,
    };

});

Cell #3

Test()  # this cell should output a green box

Cell #4: Example Output

%%html
<svg>
<path fill-rule="evenodd" fill="#66cc99" stroke="#555555" stroke-width="2.0" opacity="0.6" d="M 0.0,0.0 L 50.0,0.0 L 50.0,50.0 L 0.0,50.0 L 0.0,0.0 z" />
</svg>

* NOTE: clone and run notebook to show the expected output at the bottom.

1 Answers

Figured this out: for reasons I don't understand, you have to utilize document.createElementNS() function to add SVG, not document.createElement(). You also have to use svg.setAttributeNS() rather than svg.setAttribute().

Here's a fixed version of the notebook. Code:

%%javascript

require.undef('test');

define('test', ["@jupyter-widgets/base"], function(widgets) {
    var TestView = widgets.DOMWidgetView.extend({

        render: function() {
            TestView.__super__.render.apply(this, arguments);
            var xmlns = "http://www.w3.org/2000/svg";
            var svg = document.createElementNS(xmlns, "svg");
            svg.setAttributeNS(null, "viewBox", "0 0 100 100");
            svg.innerHTML = '<path fill-rule="evenodd" fill="#66cc99" stroke="#555555" stroke-width="2.0" opacity="0.6" d="M 0.0,0.0 L 50.0,0.0 L 50.0,50.0 L 0.0,50.0 L 0.0,0.0 z" />';
            this.el.appendChild(svg);
            console.log(svg);  // when you hover over this line in the console, you can see the SVG has been created...
        },
    });

    return {
        TestView : TestView,
    };

});
Related