Skip to content

pandapipes import breaks matplotlib svg export #775

@jnettels

Description

@jnettels

Describe the bug
Importing pandapipes breaks the matplotlib svg export.

To Reproduce

"""pandapipes import breaks matplotlib svg export.

This script will cause matplotlib to fail with the error:

    File "***\matplotlib\backend_bases.py", line 746, in get_capstyle
    return self._capstyle.name
           ^^^^^^^^^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'name'
"""

import matplotlib
import matplotlib.pyplot as plt

# This script is fine if pandapipes is not imported.
# Importing pandapipes sets the default capstyle to the string 'round',
# while it has to be an object ``CapStyle('round')``
import pandapipes

print("matplotlib", matplotlib.__version__)  # Tested with 3.10.8
print("pandapipes", pandapipes.__version__)  # Tested with 0.12.0

fig, ax = plt.subplots()
ax.scatter([1, 2], [1, 2])

# Setting your own capstyle prevents the issue
# ax.scatter([1, 2], [1, 2], capstyle="butt")

# The error is only actually triggered when saving as 'svg'. 'png' is fine.
plt.savefig("./matplotlib_capstyle_error.svg")

Error message

Details
Traceback (most recent call last):
  File "***\matplotlib_capstyle_error.py", line 25, in <module>
    plt.savefig("./matplotlib_capstyle_error.svg")
    ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "***\matplotlib\pyplot.py", line 1250, in savefig
    res = fig.savefig(*args, **kwargs)  # type: ignore[func-returns-value]
  File "***matplotlib\figure.py", line 3490, in savefig
    self.canvas.print_figure(fname, **kwargs)
    ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "***\matplotlib\backends\backend_qtagg.py", line 75, in print_figure
    super().print_figure(*args, **kwargs)
    ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "***\matplotlib\backend_bases.py", line 2193, in print_figure
    result = print_method(
        filename,
    ...<3 lines>...
        bbox_inches_restore=_bbox_inches_restore,
        **kwargs)
  File "***matplotlib\backend_bases.py", line 2049, in <lambda>
    print_method = functools.wraps(meth)(lambda *args, **kwargs: meth(
                                                                 ~~~~^
        *args, **{k: v for k, v in kwargs.items() if k not in skip}))
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "***s\matplotlib\backends\backend_svg.py", line 1351, in print_svg
    self.figure.draw(renderer)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^
  File "***\matplotlib\artist.py", line 94, in draw_wrapper
    result = draw(artist, renderer, *args, **kwargs)
  File "***\matplotlib\artist.py", line 71, in draw_wrapper
    return draw(artist, renderer)
  File "***\matplotlib\figure.py", line 3257, in draw
    mimage._draw_list_compositing_images(
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        renderer, self, artists, self.suppressComposite)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "***\matplotlib\image.py", line 134, in _draw_list_compositing_images
    a.draw(renderer)
    ~~~~~~^^^^^^^^^^
  File "***\matplotlib\artist.py", line 71, in draw_wrapper
    return draw(artist, renderer)
  File "***s\matplotlib\axes\_base.py", line 3226, in draw
    mimage._draw_list_compositing_images(
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        renderer, self, artists, self.get_figure(root=True).suppressComposite)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "***\matplotlib\image.py", line 134, in _draw_list_compositing_images
    a.draw(renderer)
    ~~~~~~^^^^^^^^^^
  File "***\matplotlib\artist.py", line 71, in draw_wrapper
    return draw(artist, renderer)
  File "***\matplotlib\collections.py", line 1008, in draw
    super().draw(renderer)
    ~~~~~~~~~~~~^^^^^^^^^^
  File "***\matplotlib\artist.py", line 71, in draw_wrapper
    return draw(artist, renderer)
  File "***\matplotlib\collections.py", line 415, in draw
    renderer.draw_markers(
    ~~~~~~~~~~~~~~~~~~~~~^
        gc, paths[0], combined_transform.frozen(),
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        mpath.Path(offsets), offset_trf, tuple(facecolors[0]))
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "***\matplotlib\backends\backend_svg.py", line 705, in draw_markers
    style = self._get_style_dict(gc, rgbFace)
  File "***\matplotlib\backends\backend_svg.py", line 597, in _get_style_dict
    if gc.get_capstyle() != 'butt':
       ~~~~~~~~~~~~~~~^^
  File "***\matplotlib\backend_bases.py", line 746, in get_capstyle
    return self._capstyle.name
           ^^^^^^^^^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'name'

Expected behavior
The svg export should just work.

Python environment (please complete the following information):

  • OS: Windows 11
  • pandapipes version 0.12.0
  • matplotlib 3.10.8

Possible solution
Update pandapipes\plotting\__init__.py. Import the CapStyle class from matplotlib and set self._capstyle = CapStyle('round') instead of setting it to the string 'round', which causes the error in matplotlib.

import types

from matplotlib.backend_bases import GraphicsContextBase, RendererBase
from matplotlib._enums import CapStyle

from pandapipes.plotting.collections import *
from pandapipes.plotting.generic_geodata import *
from pandapipes.plotting.geo import *
from pandapipes.plotting.pipeflow_results import *
from pandapipes.plotting.simple_plot import *
from pandapower.plotting.collections import add_collections_to_axes, add_cmap_to_collection, \
    add_single_collection


class GC(GraphicsContextBase):
    """

    """

    def __init__(self):
        super().__init__()
        self._capstyle = CapStyle('round')


def custom_new_gc(self):
    """

    :param self:
    :type self:
    :return:
    :rtype:
    """
    return GC()


RendererBase.new_gc = types.MethodType(custom_new_gc, RendererBase)

I have created a PR with this simple solution. But I wonder if it is a good design decision that the import of pandapipes changes any default settings for external packages. (In this case from the capstyle 'butt' to 'round'.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions