Open ankostis opened 4 years ago
Note that _repr_svg_()
has some drawbacks on jupyterlab (but not plain Jupyter notebooks,, as described in jupyterlab/jupyterlab#7497.
(continuing my last comment)
I reverted to implement the _repr_html()
method instead, which works in both jupyter environments, but the SVGs are not re-scalable. Therefore i used the svg-pan-zoom
JS library to make them "zoomable" with user controls, as shown in this hackish commit: pygraphkit/graphtik@e350afa78.
For the web, i would try also the all client-JS solution https://visjs.github.io/vis-network/docs/network
For future reference, this the code for zoomable SVGs in Jupyter:
def _repr_html_monkeypatches(dot):
"""
Monkey-patching for ``pydot.Dot._repr_html_()` for rendering in jupyter cells.
:param dot:
a `pydot.Dot()` instance
.. Note::
Had to use ``_repr_html_()`` and not simply ``_repr_svg_()`` because
(due to https://github.com/jupyterlab/jupyterlab/issues/7497)
"""
pan_zoom_json" = "{controlIconsEnabled: true, zoomScaleSensitivity: 0.4, fit: true}",
element_styles = "width: 100%; height: 300px;",
container_styles = ""
svg_txt = dot.create_svg().decode()
html = f"""
<div class="svg_container">
<style>
.svg_container {{
{container_styles}
}}
.svg_container SVG {{
{element_styles}
}}
</style>
<script src="http://ariutta.github.io/svg-pan-zoom/dist/svg-pan-zoom.min.js"></script>
<script type="text/javascript">
var scriptTag = document.scripts[document.scripts.length - 1];
var parentTag = scriptTag.parentNode;
svg_el = parentTag.querySelector(".svg_container svg");
svgPanZoom(svg_el, {pan_zoom_json});
</script>
{svg_txt}
</</>
"""
return html
There is an issue of which encoding to use
Not really, as the encoding is... well,
<?xml
tagSo if one wanted to be really pedantic, they could use:
import re
import pydot
dot = pydot.Dot()
svg = dot.create_svg()
encoding = 'utf-8'
if b'encoding=' in svg:
match = re.search(b'encoding="([^"]*)"', svg)
encoding = match.groups()[0].decode(encoding="ascii")
svg_txt = svg.decode(encoding=encoding)
Heh! In fact, interestingly, my point 1 turns out to be more correct. You wouldn't actually want to use that code, as it probably lies:
>>> import pydot
>>> dot = pydot.Dot()
>>> dot.create_svg(encoding="iso-8859-15")
b'<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"\n "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n<!-- Generated by graphviz version 8.1.0 (20230707.0739)\n -->\n<!-- Title: G Pages: 1 -->\n<svg width="8pt" height="8pt"\n viewBox="0.00 0.00 8.00 8.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">\n<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 4)">\n<title>G</title>\n<polygon fill="white" stroke="none" points="-4,4 -4,-4 4,-4 4,4 -4,4"/>\n</g>\n</svg>\n'
Note the encoding="UTF-8"
in the resulting bytestring.
The question of whether an SVG created with create_svg(encoding="iso-8859-15")
is actually encoded in ISO Western European script, as we requested, or in UTF-8, as it claims, remains an interesting one.
But to finish my thought, the safest option is probably to always explicitly specify "utf-8" both coming and going:
import pydot
def _repr_svg(self):
return self.create_svg(encoding="utf-8").decode(
encoding="utf-8")
pydot.Dot._repr_svg = _repr_svg
Ditto for a _repr_html()
implementation.
Simply adding this method in
pydot.Dot
class makes it renderable in jupyter notebooks & lab:There is an issue of which encoding to use, and how. But it would be a great facility to data people who love notebooks.