Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFE: Rendet Dot results in Jupyter cells #220

Open
ankostis opened this issue Oct 7, 2019 · 5 comments
Open

RFE: Rendet Dot results in Jupyter cells #220

ankostis opened this issue Oct 7, 2019 · 5 comments

Comments

@ankostis
Copy link

ankostis commented Oct 7, 2019

Simply adding this method in pydot.Dot class makes it renderable in jupyter notebooks & lab:

diff --git a/pydot.py b/pydot.py
index 60a2bac..70829b6 100644
--- a/pydot.py
+++ b/pydot.py
@@ -1745,6 +1745,8 @@ class Dot(Graph):
 
         self.obj_dict = state
 
+    def _repr_svg_(self):
+        return self.create_svg().decode()
 
     def set_shape_files(self, file_paths):
         """Add the paths of the required image files.

There is an issue of which encoding to use, and how.
But it would be a great facility to data people who love notebooks.

@ankostis
Copy link
Author

Note that _repr_svg_() has some drawbacks on jupyterlab (but not plain Jupyter notebooks,, as described in jupyterlab/jupyterlab#7497.

@ankostis
Copy link
Author

(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

@ankostis
Copy link
Author

ankostis commented Dec 3, 2019

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

@ferdnyc
Copy link
Contributor

ferdnyc commented Feb 14, 2024

There is an issue of which encoding to use

Not really, as the encoding is... well,

  1. Probably always utf-8
  2. Most likely embedded in the SVG's <?xml tag

So 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.

@ferdnyc
Copy link
Contributor

ferdnyc commented Feb 14, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants