Ejemplo n.º 1
0
    def __init__(self, figure, fh, dummy=False):
        """
        Creates a new PGF renderer that translates any drawing instruction
        into text commands to be interpreted in a latex pgfpicture environment.

        Attributes
        ----------
        figure : `matplotlib.figure.Figure`
            Matplotlib figure to initialize height, width and dpi from.
        fh : file-like
            File handle for the output of the drawing commands.
        """

        RendererBase.__init__(self)
        self.dpi = figure.dpi
        self.fh = fh
        self.figure = figure
        self.image_counter = 0

        # get LatexManager instance
        self.latexManager = LatexManager._get_cached_or_new()

        if dummy:
            # dummy==True deactivate all methods
            for m in RendererPgf.__dict__:
                if m.startswith("draw_"):
                    self.__dict__[m] = lambda *args, **kwargs: None
        else:
            # if fh does not belong to a filename, deactivate draw_image
            if not hasattr(fh, 'name') or not os.path.exists(fh.name):
                cbook._warn_external("streamed pgf-code does not support "
                                     "raster graphics, consider using the "
                                     "pgf-to-pdf option", UserWarning)
                self.__dict__["draw_image"] = lambda *args, **kwargs: None
Ejemplo n.º 2
0
 def __init__(self, toolmanager, name):
     cbook._warn_external(
         'The new Tool classes introduced in v1.5 are experimental; their '
         'API (including names) will likely change in future versions.')
     self._name = name
     self._toolmanager = toolmanager
     self._figure = None
Ejemplo n.º 3
0
def test_warn_external_frame_embedded_python():
    with patch.object(cbook, "sys") as mock_sys:
        mock_sys._getframe = Mock(return_value=None)
        with warnings.catch_warnings(record=True) as w:
            cbook._warn_external("dummy")
    assert len(w) == 1
    assert str(w[0].message) == "dummy"
Ejemplo n.º 4
0
    def tight_layout(self, figure, renderer=None,
                     pad=1.08, h_pad=None, w_pad=None, rect=None):
        """
        Adjust subplot parameters to give specified padding.

        Parameters
        ----------

        pad : float
            Padding between the figure edge and the edges of subplots, as a
            fraction of the font-size.
        h_pad, w_pad : float, optional
            Padding (height/width) between edges of adjacent subplots.
            Defaults to ``pad_inches``.
        rect : tuple of 4 floats, optional
            (left, bottom, right, top) rectangle in normalized figure
            coordinates that the whole subplots area (including labels) will
            fit into.  Default is (0, 0, 1, 1).
        """

        subplotspec_list = tight_layout.get_subplotspec_list(
            figure.axes, grid_spec=self)
        if None in subplotspec_list:
            cbook._warn_external("This figure includes Axes that are not "
                                 "compatible with tight_layout, so results "
                                 "might be incorrect.")

        if renderer is None:
            renderer = tight_layout.get_renderer(figure)

        kwargs = tight_layout.get_tight_layout_figure(
            figure, figure.axes, subplotspec_list, renderer,
            pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect)
        if kwargs:
            self.update(**kwargs)
Ejemplo n.º 5
0
def to_qcolor(color):
    """Create a QColor from a matplotlib color"""
    qcolor = QtGui.QColor()
    try:
        rgba = mcolors.to_rgba(color)
    except ValueError:
        cbook._warn_external('Ignoring invalid color %r' % color)
        return qcolor  # return invalid QColor
    qcolor.setRgbF(*rgba)
    return qcolor
Ejemplo n.º 6
0
    def add_tool(self, name, tool, *args, **kwargs):
        """
        Add *tool* to `ToolManager`.

        If successful, adds a new event ``tool_trigger_{name}`` where
        ``{name}`` is the *name* of the tool; the event is fired everytime the
        tool is triggered.

        Parameters
        ----------
        name : str
            Name of the tool, treated as the ID, has to be unique.
        tool : class_like, i.e. str or type
            Reference to find the class of the Tool to added.

        Notes
        -----
        args and kwargs get passed directly to the tools constructor.

        See Also
        --------
        matplotlib.backend_tools.ToolBase : The base class for tools.
        """

        tool_cls = self._get_cls_to_instantiate(tool)
        if not tool_cls:
            raise ValueError('Impossible to find class for %s' % str(tool))

        if name in self._tools:
            cbook._warn_external('A "Tool class" with the same name already '
                                 'exists, not added')
            return self._tools[name]

        tool_obj = tool_cls(self, name, *args, **kwargs)
        self._tools[name] = tool_obj

        if tool_cls.default_keymap is not None:
            self.update_keymap(name, tool_cls.default_keymap)

        # For toggle tools init the radio_group in self._toggled
        if isinstance(tool_obj, tools.ToolToggleBase):
            # None group is not mutually exclusive, a set is used to keep track
            # of all toggled tools in this group
            if tool_obj.radio_group is None:
                self._toggled.setdefault(None, set())
            else:
                self._toggled.setdefault(tool_obj.radio_group, None)

            # If initially toggled
            if tool_obj.toggled:
                self._handle_toggle(tool_obj, None, None, None)
        tool_obj.set_figure(self.figure)

        self._tool_added_event(tool_obj)
        return tool_obj
Ejemplo n.º 7
0
    def add_tool(self, name, tool, *args, **kwargs):
        """
        Add *tool* to `ToolManager`.

        If successful, adds a new event ``tool_trigger_{name}`` where
        ``{name}`` is the *name* of the tool; the event is fired everytime the
        tool is triggered.

        Parameters
        ----------
        name : str
            Name of the tool, treated as the ID, has to be unique.
        tool : class_like, i.e. str or type
            Reference to find the class of the Tool to added.

        Notes
        -----
        args and kwargs get passed directly to the tools constructor.

        See Also
        --------
        matplotlib.backend_tools.ToolBase : The base class for tools.
        """

        tool_cls = self._get_cls_to_instantiate(tool)
        if not tool_cls:
            raise ValueError('Impossible to find class for %s' % str(tool))

        if name in self._tools:
            cbook._warn_external('A "Tool class" with the same name already '
                                 'exists, not added')
            return self._tools[name]

        tool_obj = tool_cls(self, name, *args, **kwargs)
        self._tools[name] = tool_obj

        if tool_cls.default_keymap is not None:
            self.update_keymap(name, tool_cls.default_keymap)

        # For toggle tools init the radio_group in self._toggled
        if isinstance(tool_obj, tools.ToolToggleBase):
            # None group is not mutually exclusive, a set is used to keep track
            # of all toggled tools in this group
            if tool_obj.radio_group is None:
                self._toggled.setdefault(None, set())
            else:
                self._toggled.setdefault(tool_obj.radio_group, None)

            # If initially toggled
            if tool_obj.toggled:
                self._handle_toggle(tool_obj, None, None, None)
        tool_obj.set_figure(self.figure)

        self._tool_added_event(tool_obj)
        return tool_obj
Ejemplo n.º 8
0
def _parse_legend_args(axs, *args, handles=None, labels=None, **kwargs):
    """
    Get the handles and labels from the calls to either ``figure.legend``
    or ``axes.legend``.

    ``axs`` is a list of axes (to get legend artists from)
    """
    log = logging.getLogger(__name__)

    handlers = kwargs.get('handler_map', {}) or {}
    extra_args = ()

    if (handles is not None or labels is not None) and args:
        cbook._warn_external("You have mixed positional and keyword "
                             "arguments, some input may be discarded.")

    # if got both handles and labels as kwargs, make same length
    if handles and labels:
        handles, labels = zip(*zip(handles, labels))

    elif handles is not None and labels is None:
        labels = [handle.get_label() for handle in handles]

    elif labels is not None and handles is None:
        # Get as many handles as there are labels.
        handles = [
            handle for handle, label in zip(_get_legend_handles(axs, handlers),
                                            labels)
        ]

    # No arguments - automatically detect labels and handles.
    elif len(args) == 0:
        handles, labels = _get_legend_handles_labels(axs, handlers)
        if not handles:
            log.warning('No handles with labels found to put in legend.')

    # One argument. User defined labels - automatic handle detection.
    elif len(args) == 1:
        labels, = args
        # Get as many handles as there are labels.
        handles = [
            handle for handle, label in zip(_get_legend_handles(axs, handlers),
                                            labels)
        ]

    # Two arguments:
    #   * user defined handles and labels
    elif len(args) >= 2:
        handles, labels = args[:2]
        extra_args = args[2:]

    else:
        raise TypeError('Invalid arguments to legend.')

    return handles, labels, extra_args, kwargs
Ejemplo n.º 9
0
def _remove_blacklisted_style_params(d, warn=True):
    o = {}
    for key, val in d.items():
        if key in STYLE_BLACKLIST:
            if warn:
                cbook._warn_external(
                    "Style includes a parameter, '{0}', that is not related "
                    "to style.  Ignoring".format(key))
        else:
            o[key] = val
    return o
Ejemplo n.º 10
0
def _remove_blacklisted_style_params(d, warn=True):
    o = {}
    for key, val in d.items():
        if key in STYLE_BLACKLIST:
            if warn:
                cbook._warn_external(
                    "Style includes a parameter, '{0}', that is not related "
                    "to style.  Ignoring".format(key))
        else:
            o[key] = val
    return o
Ejemplo n.º 11
0
def _remove_blacklisted_style_params(d, warn=True):
    o = {}
    for key in d:  # prevent triggering RcParams.__getitem__('backend')
        if key in STYLE_BLACKLIST:
            if warn:
                cbook._warn_external(
                    "Style includes a parameter, '{0}', that is not related "
                    "to style.  Ignoring".format(key))
        else:
            o[key] = d[key]
    return o
Ejemplo n.º 12
0
def latex2png(latex, filename, fontset='cm'):
    latex = "$%s$" % latex
    with mpl.rc_context({'mathtext.fontset': fontset}):
        try:
            depth = mathtext.math_to_image(latex,
                                           filename,
                                           dpi=100,
                                           format="png")
        except Exception:
            cbook._warn_external(f"Could not render math expression {latex}")
            depth = 0
    return depth
Ejemplo n.º 13
0
def latex2png(latex, filename, fontset='cm'):
    latex = "$%s$" % latex
    with mpl.rc_context({'mathtext.fontset': fontset}):
        if os.path.exists(filename):
            depth = mathtext_parser.get_depth(latex, dpi=100)
        else:
            try:
                depth = mathtext_parser.to_png(filename, latex, dpi=100)
            except Exception:
                cbook._warn_external(
                    f"Could not render math expression {latex}")
                depth = 0
    return depth
Ejemplo n.º 14
0
def _parse_legend_args(axs, *args, handles=None, labels=None, **kwargs):
    """
    Get the handles and labels from the calls to either ``figure.legend``
    or ``axes.legend``.

    ``axs`` is a list of axes (to get legend artists from)
    """
    log = logging.getLogger(__name__)

    handlers = kwargs.get('handler_map', {}) or {}
    extra_args = ()

    if (handles is not None or labels is not None) and args:
        cbook._warn_external("You have mixed positional and keyword "
                             "arguments, some input may be discarded.")

    # if got both handles and labels as kwargs, make same length
    if handles and labels:
        handles, labels = zip(*zip(handles, labels))

    elif handles is not None and labels is None:
        labels = [handle.get_label() for handle in handles]

    elif labels is not None and handles is None:
        # Get as many handles as there are labels.
        handles = [handle for handle, label
                   in zip(_get_legend_handles(axs, handlers), labels)]

    # No arguments - automatically detect labels and handles.
    elif len(args) == 0:
        handles, labels = _get_legend_handles_labels(axs, handlers)
        if not handles:
            log.warning('No handles with labels found to put in legend.')

    # One argument. User defined labels - automatic handle detection.
    elif len(args) == 1:
        labels, = args
        # Get as many handles as there are labels.
        handles = [handle for handle, label
                   in zip(_get_legend_handles(axs, handlers), labels)]

    # Two arguments:
    #   * user defined handles and labels
    elif len(args) >= 2:
        handles, labels = args[:2]
        extra_args = args[2:]

    else:
        raise TypeError('Invalid arguments to legend.')

    return handles, labels, extra_args, kwargs
Ejemplo n.º 15
0
def get_renderer(fig):
    if fig._cachedRenderer:
        renderer = fig._cachedRenderer
    else:
        canvas = fig.canvas

        if canvas and hasattr(canvas, "get_renderer"):
            renderer = canvas.get_renderer()
        else:  # Some noninteractive backends have no renderer until draw time.
            cbook._warn_external("tight_layout: falling back to Agg renderer")
            from matplotlib.backends.backend_agg import FigureCanvasAgg
            canvas = FigureCanvasAgg(fig)
            renderer = canvas.get_renderer()

    return renderer
Ejemplo n.º 16
0
    def __init__(self, figure, fh, dummy=False):
        """
        Creates a new PGF renderer that translates any drawing instruction
        into text commands to be interpreted in a latex pgfpicture environment.

        Attributes
        ----------
        figure : `matplotlib.figure.Figure`
            Matplotlib figure to initialize height, width and dpi from.
        fh : file-like
            File handle for the output of the drawing commands.
        """

        RendererBase.__init__(self)
        self.dpi = figure.dpi
        self.fh = fh
        self.figure = figure
        self.image_counter = 0

        self._latexManager = LatexManager._get_cached_or_new()  # deprecated

        if dummy:
            # dummy==True deactivate all methods
            for m in RendererPgf.__dict__:
                if m.startswith("draw_"):
                    self.__dict__[m] = lambda *args, **kwargs: None
        else:
            # if fh does not belong to a filename, deactivate draw_image
            if not hasattr(fh, 'name') or not os.path.exists(fh.name):
                self.__dict__["draw_image"] = \
                    lambda *args, **kwargs: cbook._warn_external(
                        "streamed pgf-code does not support raster graphics, "
                        "consider using the pgf-to-pdf option")
Ejemplo n.º 17
0
def get_renderer(fig):
    if fig._cachedRenderer:
        renderer = fig._cachedRenderer
    else:
        canvas = fig.canvas

        if canvas and hasattr(canvas, "get_renderer"):
            renderer = canvas.get_renderer()
        else:
            # not sure if this can happen
            cbook._warn_external("tight_layout : falling back to Agg renderer")
            from matplotlib.backends.backend_agg import FigureCanvasAgg
            canvas = FigureCanvasAgg(fig)
            renderer = canvas.get_renderer()

    return renderer
Ejemplo n.º 18
0
def get_renderer(fig):
    if fig._cachedRenderer:
        renderer = fig._cachedRenderer
    else:
        canvas = fig.canvas

        if canvas and hasattr(canvas, "get_renderer"):
            renderer = canvas.get_renderer()
        else:
            # not sure if this can happen
            cbook._warn_external("tight_layout : falling back to Agg renderer")
            from matplotlib.backends.backend_agg import FigureCanvasAgg
            canvas = FigureCanvasAgg(fig)
            renderer = canvas.get_renderer()

    return renderer
Ejemplo n.º 19
0
    def _find_best_position(self, width, height, renderer, consider=None):
        """
        Determine the best location to place the legend.

        *consider* is a list of ``(x, y)`` pairs to consider as a potential
        lower-left corner of the legend. All are display coords.
        """
        # should always hold because function is only called internally
        assert self.isaxes

        start_time = time.perf_counter()

        verts, bboxes, lines, offsets = self._auto_legend_data()

        bbox = Bbox.from_bounds(0, 0, width, height)
        if consider is None:
            consider = [
                self._get_anchored_bbox(x, bbox, self.get_bbox_to_anchor(),
                                        renderer)
                for x in range(1, len(self.codes))
            ]

        candidates = []
        for idx, (l, b) in enumerate(consider):
            legendBox = Bbox.from_bounds(l, b, width, height)
            badness = 0
            # XXX TODO: If markers are present, it would be good to
            # take them into account when checking vertex overlaps in
            # the next line.
            badness = (legendBox.count_contains(verts) +
                       legendBox.count_contains(offsets) +
                       legendBox.count_overlaps(bboxes) + sum(
                           line.intersects_bbox(legendBox, filled=False)
                           for line in lines))
            if badness == 0:
                return l, b
            # Include the index to favor lower codes in case of a tie.
            candidates.append((badness, idx, (l, b)))

        _, _, (l, b) = min(candidates)

        if self._loc_used_default and time.perf_counter() - start_time > 1:
            cbook._warn_external(
                'Creating legend with loc="best" can be slow with large '
                'amounts of data.')

        return l, b
Ejemplo n.º 20
0
def latex2png(latex, filename, fontset='cm'):
    latex = "$%s$" % latex
    orig_fontset = rcParams['mathtext.fontset']
    rcParams['mathtext.fontset'] = fontset
    if os.path.exists(filename):
        depth = mathtext_parser.get_depth(latex, dpi=100)
    else:
        try:
            depth = mathtext_parser.to_png(filename, latex, dpi=100)
        except Exception:
            cbook._warn_external("Could not render math expression %s" % latex,
                                 Warning)
            depth = 0
    rcParams['mathtext.fontset'] = orig_fontset
    sys.stdout.write("#")
    sys.stdout.flush()
    return depth
Ejemplo n.º 21
0
def latex2png(latex, filename, fontset='cm'):
    latex = "$%s$" % latex
    orig_fontset = rcParams['mathtext.fontset']
    rcParams['mathtext.fontset'] = fontset
    if os.path.exists(filename):
        depth = mathtext_parser.get_depth(latex, dpi=100)
    else:
        try:
            depth = mathtext_parser.to_png(filename, latex, dpi=100)
        except Exception:
            cbook._warn_external("Could not render math expression %s" % latex,
                                 Warning)
            depth = 0
    rcParams['mathtext.fontset'] = orig_fontset
    sys.stdout.write("#")
    sys.stdout.flush()
    return depth
Ejemplo n.º 22
0
    def tight_layout(self,
                     figure,
                     renderer=None,
                     pad=1.08,
                     h_pad=None,
                     w_pad=None,
                     rect=None):
        """
        Adjust subplot parameters to give specified padding.

        Parameters
        ----------

        pad : float
            Padding between the figure edge and the edges of subplots, as a
            fraction of the font-size.
        h_pad, w_pad : float, optional
            Padding (height/width) between edges of adjacent subplots.
            Defaults to *pad*.
        rect : tuple of 4 floats, optional
            (left, bottom, right, top) rectangle in normalized figure
            coordinates that the whole subplots area (including labels) will
            fit into.  Default is (0, 0, 1, 1).
        """

        subplotspec_list = tight_layout.get_subplotspec_list(figure.axes,
                                                             grid_spec=self)
        if None in subplotspec_list:
            cbook._warn_external("This figure includes Axes that are not "
                                 "compatible with tight_layout, so results "
                                 "might be incorrect.")

        if renderer is None:
            renderer = tight_layout.get_renderer(figure)

        kwargs = tight_layout.get_tight_layout_figure(figure,
                                                      figure.axes,
                                                      subplotspec_list,
                                                      renderer,
                                                      pad=pad,
                                                      h_pad=h_pad,
                                                      w_pad=w_pad,
                                                      rect=rect)
        if kwargs:
            self.update(**kwargs)
Ejemplo n.º 23
0
    def new_floating_axis(self, nth_coord, value,
                          axis_direction="bottom",
                          axes=None,
                          ):

        if axes is None:
            cbook._warn_external(
                "'new_floating_axis' explicitly requires the axes keyword.")
            axes = self.axes

        _helper = AxisArtistHelperRectlinear.Floating(
            axes, nth_coord, value, axis_direction)

        axisline = AxisArtist(axes, _helper)

        axisline.line.set_clip_on(True)
        axisline.line.set_clip_box(axisline.axes.bbox)
        return axisline
Ejemplo n.º 24
0
    def new_floating_axis(self, nth_coord, value,
                          axis_direction="bottom",
                          axes=None,
                          ):

        if axes is None:
            cbook._warn_external(
                "'new_floating_axis' explicitly requires the axes keyword.")
            axes = self.axes

        _helper = AxisArtistHelperRectlinear.Floating(
            axes, nth_coord, value, axis_direction)

        axisline = AxisArtist(axes, _helper)

        axisline.line.set_clip_on(True)
        axisline.line.set_clip_box(axisline.axes.bbox)
        return axisline
Ejemplo n.º 25
0
    def _find_best_position(self, width, height, renderer, consider=None):
        """
        Determine the best location to place the legend.

        *consider* is a list of ``(x, y)`` pairs to consider as a potential
        lower-left corner of the legend. All are display coords.
        """
        # should always hold because function is only called internally
        assert self.isaxes

        verts, bboxes, lines, offsets = self._auto_legend_data()
        if self._loc_used_default and verts.shape[0] > 200000:
            # this size results in a 3+ second render time on a good machine
            cbook._warn_external(
                'Creating legend with loc="best" can be slow with large '
                'amounts of data.'
            )

        bbox = Bbox.from_bounds(0, 0, width, height)
        if consider is None:
            consider = [self._get_anchored_bbox(x, bbox,
                                                self.get_bbox_to_anchor(),
                                                renderer)
                        for x in range(1, len(self.codes))]

        candidates = []
        for idx, (l, b) in enumerate(consider):
            legendBox = Bbox.from_bounds(l, b, width, height)
            badness = 0
            # XXX TODO: If markers are present, it would be good to
            # take them into account when checking vertex overlaps in
            # the next line.
            badness = (legendBox.count_contains(verts)
                       + legendBox.count_contains(offsets)
                       + legendBox.count_overlaps(bboxes)
                       + sum(line.intersects_bbox(legendBox, filled=False)
                             for line in lines))
            if badness == 0:
                return l, b
            # Include the index to favor lower codes in case of a tie.
            candidates.append((badness, idx, (l, b)))

        _, _, (l, b) = min(candidates)
        return l, b
Ejemplo n.º 26
0
    def get_tool(self, name, warn=True):
        """
        Return the tool object, also accepts the actual tool for convenience.

        Parameters
        ----------
        name : str, ToolBase
            Name of the tool, or the tool itself
        warn : bool, optional
            If this method should give warnings.
        """
        if isinstance(name, tools.ToolBase) and name.name in self._tools:
            return name
        if name not in self._tools:
            if warn:
                cbook._warn_external("ToolManager does not control tool "
                                     "%s" % name)
            return None
        return self._tools[name]
Ejemplo n.º 27
0
    def get_tool(self, name, warn=True):
        """
        Return the tool object, also accepts the actual tool for convenience.

        Parameters
        ----------
        name : str, ToolBase
            Name of the tool, or the tool itself
        warn : bool, optional
            If this method should give warnings.
        """
        if isinstance(name, tools.ToolBase) and name.name in self._tools:
            return name
        if name not in self._tools:
            if warn:
                cbook._warn_external("ToolManager does not control tool "
                                     "%s" % name)
            return None
        return self._tools[name]
Ejemplo n.º 28
0
    def new_fixed_axis(self, loc,
                       nth_coord=None,
                       axis_direction=None,
                       offset=None,
                       axes=None,
                       ):

        if axes is None:
            cbook._warn_external(
                "'new_fixed_axis' explicitly requires the axes keyword.")
            axes = self.axes

        _helper = AxisArtistHelperRectlinear.Fixed(axes, loc, nth_coord)

        if axis_direction is None:
            axis_direction = loc
        axisline = AxisArtist(axes, _helper, offset=offset,
                              axis_direction=axis_direction,
                              )

        return axisline
Ejemplo n.º 29
0
    def new_fixed_axis(self, loc,
                       nth_coord=None,
                       axis_direction=None,
                       offset=None,
                       axes=None,
                       ):

        if axes is None:
            cbook._warn_external(
                "'new_fixed_axis' explicitly requires the axes keyword.")
            axes = self.axes

        _helper = AxisArtistHelperRectlinear.Fixed(axes, loc, nth_coord)

        if axis_direction is None:
            axis_direction = loc
        axisline = AxisArtist(axes, _helper, offset=offset,
                              axis_direction=axis_direction,
                              )

        return axisline
Ejemplo n.º 30
0
    def draw_image(self, gc, x, y, im, transform=None):
        # docstring inherited

        h, w = im.shape[:2]
        if w == 0 or h == 0:
            return

        if not os.path.exists(getattr(self.fh, "name", "")):
            cbook._warn_external(
                "streamed pgf-code does not support raster graphics, consider "
                "using the pgf-to-pdf option.")

        # save the images to png files
        path = pathlib.Path(self.fh.name)
        fname_img = "%s-img%d.png" % (path.stem, self.image_counter)
        Image.fromarray(im[::-1]).save(path.parent / fname_img)
        self.image_counter += 1

        # reference the image in the pgf picture
        writeln(self.fh, r"\begin{pgfscope}")
        self._print_pgf_clip(gc)
        f = 1. / self.dpi  # from display coords to inch
        if transform is None:
            writeln(self.fh,
                    r"\pgfsys@transformshift{%fin}{%fin}" % (x * f, y * f))
            w, h = w * f, h * f
        else:
            tr1, tr2, tr3, tr4, tr5, tr6 = transform.frozen().to_values()
            writeln(self.fh,
                    r"\pgfsys@transformcm{%f}{%f}{%f}{%f}{%fin}{%fin}" %
                    (tr1 * f, tr2 * f, tr3 * f, tr4 * f,
                     (tr5 + x) * f, (tr6 + y) * f))
            w = h = 1  # scale is already included in the transform
        interp = str(transform is None).lower()  # interpolation in PDF reader
        writeln(self.fh,
                r"\pgftext[left,bottom]"
                r"{%s[interpolate=%s,width=%fin,height=%fin]{%s}}" %
                (_get_image_inclusion_command(),
                 interp, w, h, fname_img))
        writeln(self.fh, r"\end{pgfscope}")
Ejemplo n.º 31
0
    def update_keymap(self, name, *keys):
        """
        Set the keymap to associate with the specified tool.

        Parameters
        ----------
        name : string
            Name of the Tool
        keys : keys to associate with the Tool
        """

        if name not in self._tools:
            raise KeyError('%s not in Tools' % name)

        self._remove_keys(name)

        for key in keys:
            for k in validate_stringlist(key):
                if k in self._keys:
                    cbook._warn_external('Key %s changed from %s to %s' %
                                         (k, self._keys[k], name))
                self._keys[k] = name
Ejemplo n.º 32
0
    def update_keymap(self, name, *keys):
        """
        Set the keymap to associate with the specified tool.

        Parameters
        ----------
        name : string
            Name of the Tool
        keys : keys to associate with the Tool
        """

        if name not in self._tools:
            raise KeyError('%s not in Tools' % name)

        self._remove_keys(name)

        for key in keys:
            for k in validate_stringlist(key):
                if k in self._keys:
                    cbook._warn_external('Key %s changed from %s to %s' %
                                         (k, self._keys[k], name))
                self._keys[k] = name
Ejemplo n.º 33
0
def do_constrained_layout(fig,
                          renderer,
                          h_pad,
                          w_pad,
                          hspace=None,
                          wspace=None):
    """
    Do the constrained_layout.  Called at draw time in
     ``figure.constrained_layout()``

    Parameters
    ----------
    fig : Figure
        ``Figure`` instance to do the layout in.

    renderer : Renderer
        Renderer to use.

     h_pad, w_pad : float
       Padding around the axes elements in figure-normalized units.

     hspace, wspace : float
        Fraction of the figure to dedicate to space between the
        axes.  These are evenly spread between the gaps between the axes.
        A value of 0.2 for a three-column layout would have a space
        of 0.1 of the figure width between each column.
        If h/wspace < h/w_pad, then the pads are used instead.
    """

    # list of unique gridspecs that contain child axes:
    gss = set()
    for ax in fig.axes:
        if hasattr(ax, 'get_subplotspec'):
            gs = ax.get_subplotspec().get_gridspec()
            if gs._layoutgrid is not None:
                gss.add(gs)
    gss = list(gss)
    if len(gss) == 0:
        cbook._warn_external('There are no gridspecs with layoutgrids. '
                             'Possibly did not call parent GridSpec with the'
                             ' "figure" keyword')

    for _ in range(2):
        # do the algorithm twice.  This has to be done because decorations
        # change size after the first re-position (i.e. x/yticklabels get
        # larger/smaller).  This second reposition tends to be much milder,
        # so doing twice makes things work OK.

        # make margins for all the axes and subfigures in the
        # figure.  Add margins for colorbars...
        _make_layout_margins(fig,
                             renderer,
                             h_pad=h_pad,
                             w_pad=w_pad,
                             hspace=hspace,
                             wspace=wspace)
        _make_margin_suptitles(fig, renderer, h_pad=h_pad, w_pad=w_pad)

        # if a layout is such that a columns (or rows) margin has no
        # constraints, we need to make all such instances in the grid
        # match in margin size.
        _match_submerged_margins(fig)

        # update all the variables in the layout.
        fig._layoutgrid.update_variables()

        if _check_no_collapsed_axes(fig):
            _reposition_axes(fig,
                             renderer,
                             h_pad=h_pad,
                             w_pad=w_pad,
                             hspace=hspace,
                             wspace=wspace)
        else:
            cbook._warn_external('constrained_layout not applied because '
                                 'axes sizes collapsed to zero.  Try making '
                                 'figure larger or axes decorations smaller.')
        _reset_margins(fig)
Ejemplo n.º 34
0
def specgram(x, NFFT=None, Fs=None, detrend=None, window=None,
             noverlap=None, pad_to=None, sides=None, scale_by_freq=None,
             mode=None):
    """
    Compute a spectrogram.

    Compute and plot a spectrogram of data in x.  Data are split into
    NFFT length segments and the spectrum of each section is
    computed.  The windowing function window is applied to each
    segment, and the amount of overlap of each segment is
    specified with noverlap.

    Parameters
    ----------
    x : array-like
        1-D array or sequence.

    %(Spectral)s

    %(PSD)s

    noverlap : int, optional
        The number of points of overlap between blocks.  The default
        value is 128.
    mode : str, optional, default: 'psd'
        What sort of spectrum to use:
            'psd'
                Returns the power spectral density.
            'complex'
                Returns the complex-valued frequency spectrum.
            'magnitude'
                Returns the magnitude spectrum.
            'angle'
                Returns the phase spectrum without unwrapping.
            'phase'
                Returns the phase spectrum with unwrapping.

    Returns
    -------
    spectrum : array-like
        2-D array, columns are the periodograms of successive segments.

    freqs : array-like
        1-D array, frequencies corresponding to the rows in *spectrum*.

    t : array-like
        1-D array, the times corresponding to midpoints of segments
        (i.e the columns in *spectrum*).

    See Also
    --------
    psd : differs in the overlap and in the return values.
    complex_spectrum : similar, but with complex valued frequencies.
    magnitude_spectrum : similar single segment when mode is 'magnitude'.
    angle_spectrum : similar to single segment when mode is 'angle'.
    phase_spectrum : similar to single segment when mode is 'phase'.

    Notes
    -----
    detrend and scale_by_freq only apply when *mode* is set to 'psd'.

    """
    if noverlap is None:
        noverlap = 128  # default in _spectral_helper() is noverlap = 0
    if NFFT is None:
        NFFT = 256  # same default as in _spectral_helper()
    if len(x) <= NFFT:
        cbook._warn_external("Only one segment is calculated since parameter "
                             "NFFT (=%d) >= signal length (=%d)." %
                             (NFFT, len(x)))

    spec, freqs, t = _spectral_helper(x=x, y=None, NFFT=NFFT, Fs=Fs,
                                      detrend_func=detrend, window=window,
                                      noverlap=noverlap, pad_to=pad_to,
                                      sides=sides,
                                      scale_by_freq=scale_by_freq,
                                      mode=mode)

    if mode != 'complex':
        spec = spec.real  # Needed since helper implements generically

    return spec, freqs, t
Ejemplo n.º 35
0
 def _calc_offset_transform(self):
     """calculate the offset transform performed by the spine"""
     self._ensure_position_is_set()
     position = self._position
     if isinstance(position, str):
         if position == 'center':
             position = ('axes', 0.5)
         elif position == 'zero':
             position = ('data', 0)
     assert len(position) == 2, "position should be 2-tuple"
     position_type, amount = position
     assert position_type in ('axes', 'outward', 'data')
     if position_type == 'outward':
         if amount == 0:
             # short circuit commonest case
             self._spine_transform = ('identity',
                                      mtransforms.IdentityTransform())
         elif self.spine_type in ['left', 'right', 'top', 'bottom']:
             offset_vec = {'left': (-1, 0),
                           'right': (1, 0),
                           'bottom': (0, -1),
                           'top': (0, 1),
                           }[self.spine_type]
             # calculate x and y offset in dots
             offset_x = amount * offset_vec[0] / 72.0
             offset_y = amount * offset_vec[1] / 72.0
             self._spine_transform = ('post',
                                      mtransforms.ScaledTranslation(
                                          offset_x,
                                          offset_y,
                                          self.figure.dpi_scale_trans))
         else:
             cbook._warn_external('unknown spine type "%s": no spine '
                                  'offset performed' % self.spine_type)
             self._spine_transform = ('identity',
                                      mtransforms.IdentityTransform())
     elif position_type == 'axes':
         if self.spine_type in ('left', 'right'):
             self._spine_transform = ('pre',
                                      mtransforms.Affine2D.from_values(
                                          # keep y unchanged, fix x at
                                          # amount
                                          0, 0, 0, 1, amount, 0))
         elif self.spine_type in ('bottom', 'top'):
             self._spine_transform = ('pre',
                                      mtransforms.Affine2D.from_values(
                                          # keep x unchanged, fix y at
                                          # amount
                                          1, 0, 0, 0, 0, amount))
         else:
             cbook._warn_external('unknown spine type "%s": no spine '
                                  'offset performed' % self.spine_type)
             self._spine_transform = ('identity',
                                      mtransforms.IdentityTransform())
     elif position_type == 'data':
         if self.spine_type in ('right', 'top'):
             # The right and top spines have a default position of 1 in
             # axes coordinates.  When specifying the position in data
             # coordinates, we need to calculate the position relative to 0.
             amount -= 1
         if self.spine_type in ('left', 'right'):
             self._spine_transform = ('data',
                                      mtransforms.Affine2D().translate(
                                          amount, 0))
         elif self.spine_type in ('bottom', 'top'):
             self._spine_transform = ('data',
                                      mtransforms.Affine2D().translate(
                                          0, amount))
         else:
             cbook._warn_external('unknown spine type "%s": no spine '
                                  'offset performed' % self.spine_type)
             self._spine_transform = ('identity',
                                      mtransforms.IdentityTransform())
Ejemplo n.º 36
0
def inset_axes(parent_axes, width, height, loc='upper right',
               bbox_to_anchor=None, bbox_transform=None,
               axes_class=None,
               axes_kwargs=None,
               borderpad=0.5):
    """
    Create an inset axes with a given width and height.

    Both sizes used can be specified either in inches or percentage.
    For example,::

        inset_axes(parent_axes, width='40%%', height='30%%', loc=3)

    creates in inset axes in the lower left corner of *parent_axes* which spans
    over 30%% in height and 40%% in width of the *parent_axes*. Since the usage
    of `.inset_axes` may become slightly tricky when exceeding such standard
    cases, it is recommended to read :doc:`the examples
    </gallery/axes_grid1/inset_locator_demo>`.

    Notes
    -----
    The meaning of *bbox_to_anchor* and *bbox_to_transform* is interpreted
    differently from that of legend. The value of bbox_to_anchor
    (or the return value of its get_points method; the default is
    *parent_axes.bbox*) is transformed by the bbox_transform (the default
    is Identity transform) and then interpreted as points in the pixel
    coordinate (which is dpi dependent).

    Thus, following three calls are identical and creates an inset axes
    with respect to the *parent_axes*::

       axins = inset_axes(parent_axes, "30%%", "40%%")
       axins = inset_axes(parent_axes, "30%%", "40%%",
                          bbox_to_anchor=parent_axes.bbox)
       axins = inset_axes(parent_axes, "30%%", "40%%",
                          bbox_to_anchor=(0, 0, 1, 1),
                          bbox_transform=parent_axes.transAxes)

    Parameters
    ----------
    parent_axes : `matplotlib.axes.Axes`
        Axes to place the inset axes.

    width, height : float or str
        Size of the inset axes to create. If a float is provided, it is
        the size in inches, e.g. *width=1.3*. If a string is provided, it is
        the size in relative units, e.g. *width='40%%'*. By default, i.e. if
        neither *bbox_to_anchor* nor *bbox_transform* are specified, those
        are relative to the parent_axes. Otherwise they are to be understood
        relative to the bounding box provided via *bbox_to_anchor*.

    loc : int or string, optional, default to 1
        Location to place the inset axes. The valid locations are::

            'upper right'  : 1,
            'upper left'   : 2,
            'lower left'   : 3,
            'lower right'  : 4,
            'right'        : 5,
            'center left'  : 6,
            'center right' : 7,
            'lower center' : 8,
            'upper center' : 9,
            'center'       : 10

    bbox_to_anchor : tuple or `matplotlib.transforms.BboxBase`, optional
        Bbox that the inset axes will be anchored to. If None,
        a tuple of (0, 0, 1, 1) is used if *bbox_transform* is set
        to *parent_axes.transAxes* or *parent_axes.figure.transFigure*.
        Otherwise, *parent_axes.bbox* is used. If a tuple, can be either
        [left, bottom, width, height], or [left, bottom].
        If the kwargs *width* and/or *height* are specified in relative units,
        the 2-tuple [left, bottom] cannot be used. Note that,
        unless *bbox_transform* is set, the units of the bounding box
        are interpreted in the pixel coordinate. When using *bbox_to_anchor*
        with tuple, it almost always makes sense to also specify
        a *bbox_transform*. This might often be the axes transform
        *parent_axes.transAxes*.

    bbox_transform : `matplotlib.transforms.Transform`, optional
        Transformation for the bbox that contains the inset axes.
        If None, a `.transforms.IdentityTransform` is used. The value
        of *bbox_to_anchor* (or the return value of its get_points method)
        is transformed by the *bbox_transform* and then interpreted
        as points in the pixel coordinate (which is dpi dependent).
        You may provide *bbox_to_anchor* in some normalized coordinate,
        and give an appropriate transform (e.g., *parent_axes.transAxes*).

    axes_class : `matplotlib.axes.Axes` type, optional
        If specified, the inset axes created will be created with this class's
        constructor.

    axes_kwargs : dict, optional
        Keyworded arguments to pass to the constructor of the inset axes.
        Valid arguments include:
        %(Axes)s

    borderpad : float, optional
        Padding between inset axes and the bbox_to_anchor. Defaults to 0.5.
        The units are axes font size, i.e. for a default font size of 10 points
        *borderpad = 0.5* is equivalent to a padding of 5 points.

    Returns
    -------
    inset_axes : `axes_class`
        Inset axes object created.
    """

    if axes_class is None:
        axes_class = HostAxes

    if axes_kwargs is None:
        inset_axes = axes_class(parent_axes.figure, parent_axes.get_position())
    else:
        inset_axes = axes_class(parent_axes.figure, parent_axes.get_position(),
                                **axes_kwargs)

    if bbox_transform in [parent_axes.transAxes,
                          parent_axes.figure.transFigure]:
        if bbox_to_anchor is None:
            cbook._warn_external("Using the axes or figure transform "
                                 "requires a bounding box in the respective "
                                 "coordinates. "
                                 "Using bbox_to_anchor=(0,0,1,1) now.")
            bbox_to_anchor = (0, 0, 1, 1)

    if bbox_to_anchor is None:
        bbox_to_anchor = parent_axes.bbox

    if isinstance(bbox_to_anchor, tuple) and \
        (isinstance(width, str) or isinstance(height, str)):
        if len(bbox_to_anchor) != 4:
            raise ValueError("Using relative units for width or height "
                             "requires to provide a 4-tuple or a "
                             "`BBox` instance to `bbox_to_anchor.")

    axes_locator = AnchoredSizeLocator(bbox_to_anchor,
                                       width, height,
                                       loc=loc,
                                       bbox_transform=bbox_transform,
                                       borderpad=borderpad)

    inset_axes.set_axes_locator(axes_locator)

    _add_inset_axes(parent_axes, inset_axes)

    return inset_axes
Ejemplo n.º 37
0
def get_tight_layout_figure(fig, axes_list, subplotspec_list, renderer,
                            pad=1.08, h_pad=None, w_pad=None, rect=None):
    """
    Return subplot parameters for tight-layouted-figure with specified padding.

    Parameters
    ----------
    fig : Figure
    axes_list : list of Axes
    subplotspec_list : list of `.SubplotSpec`
        The subplotspecs of each axes.
    renderer : renderer
    pad : float
        Padding between the figure edge and the edges of subplots, as a
        fraction of the font size.
    h_pad, w_pad : float
        Padding (height/width) between edges of adjacent subplots.  Defaults to
        *pad_inches*.
    rect : Tuple[float, float, float, float], optional
        (left, bottom, right, top) rectangle in normalized figure coordinates
        that the whole subplots area (including labels) will fit into.
        Defaults to using the entire figure.

    Returns
    -------
    subplotspec or None
        subplotspec kwargs to be passed to `.Figure.subplots_adjust` or
        None if tight_layout could not be accomplished.

    """

    subplot_list = []
    nrows_list = []
    ncols_list = []
    ax_bbox_list = []

    # Multiple axes can share same subplot_interface (e.g., axes_grid1); thus
    # we need to join them together.
    subplot_dict = {}

    subplotspec_list2 = []

    for ax, subplotspec in zip(axes_list, subplotspec_list):
        if subplotspec is None:
            continue

        subplots = subplot_dict.setdefault(subplotspec, [])

        if not subplots:
            myrows, mycols, _, _ = subplotspec.get_geometry()
            nrows_list.append(myrows)
            ncols_list.append(mycols)
            subplotspec_list2.append(subplotspec)
            subplot_list.append(subplots)
            ax_bbox_list.append(subplotspec.get_position(fig))

        subplots.append(ax)

    if len(nrows_list) == 0 or len(ncols_list) == 0:
        return {}

    max_nrows = max(nrows_list)
    max_ncols = max(ncols_list)

    num1num2_list = []
    for subplotspec in subplotspec_list2:
        rows, cols, num1, num2 = subplotspec.get_geometry()
        div_row, mod_row = divmod(max_nrows, rows)
        div_col, mod_col = divmod(max_ncols, cols)
        if mod_row != 0:
            cbook._warn_external('tight_layout not applied: number of rows '
                                 'in subplot specifications must be '
                                 'multiples of one another.')
            return {}
        if mod_col != 0:
            cbook._warn_external('tight_layout not applied: number of '
                                 'columns in subplot specifications must be '
                                 'multiples of one another.')
            return {}

        rowNum1, colNum1 = divmod(num1, cols)
        if num2 is None:
            rowNum2, colNum2 = rowNum1, colNum1
        else:
            rowNum2, colNum2 = divmod(num2, cols)

        num1num2_list.append((rowNum1 * div_row * max_ncols +
                              colNum1 * div_col,
                              ((rowNum2 + 1) * div_row - 1) * max_ncols +
                              (colNum2 + 1) * div_col - 1))

    kwargs = auto_adjust_subplotpars(fig, renderer,
                                     nrows_ncols=(max_nrows, max_ncols),
                                     num1num2_list=num1num2_list,
                                     subplot_list=subplot_list,
                                     ax_bbox_list=ax_bbox_list,
                                     pad=pad, h_pad=h_pad, w_pad=w_pad)

    # kwargs can be none if tight_layout fails...
    if rect is not None and kwargs is not None:
        # if rect is given, the whole subplots area (including
        # labels) will fit into the rect instead of the
        # figure. Note that the rect argument of
        # *auto_adjust_subplotpars* specify the area that will be
        # covered by the total area of axes.bbox. Thus we call
        # auto_adjust_subplotpars twice, where the second run
        # with adjusted rect parameters.

        left, bottom, right, top = rect
        if left is not None:
            left += kwargs["left"]
        if bottom is not None:
            bottom += kwargs["bottom"]
        if right is not None:
            right -= (1 - kwargs["right"])
        if top is not None:
            top -= (1 - kwargs["top"])

        kwargs = auto_adjust_subplotpars(fig, renderer,
                                         nrows_ncols=(max_nrows, max_ncols),
                                         num1num2_list=num1num2_list,
                                         subplot_list=subplot_list,
                                         ax_bbox_list=ax_bbox_list,
                                         pad=pad, h_pad=h_pad, w_pad=w_pad,
                                         rect=(left, bottom, right, top))

    return kwargs
Ejemplo n.º 38
0
 def set_aspect(self, *args, **kwargs):
     """
     Secondary axes cannot set the aspect ratio, so calling this just
     sets a warning.
     """
     cbook._warn_external("Secondary axes can't set the aspect ratio")
Ejemplo n.º 39
0
    def __init__(self, parent, handles, labels,
                 loc=None,
                 numpoints=None,    # the number of points in the legend line
                 markerscale=None,  # the relative size of legend markers
                                    # vs. original
                 markerfirst=True,  # controls ordering (left-to-right) of
                                    # legend marker and label
                 scatterpoints=None,    # number of scatter points
                 scatteryoffsets=None,
                 prop=None,          # properties for the legend texts
                 fontsize=None,        # keyword to set font size directly

                 # spacing & pad defined as a fraction of the font-size
                 borderpad=None,      # the whitespace inside the legend border
                 labelspacing=None,   # the vertical space between the legend
                                      # entries
                 handlelength=None,   # the length of the legend handles
                 handleheight=None,   # the height of the legend handles
                 handletextpad=None,  # the pad between the legend handle
                                      # and text
                 borderaxespad=None,  # the pad between the axes and legend
                                      # border
                 columnspacing=None,  # spacing between columns

                 ncol=1,     # number of columns
                 mode=None,  # mode for horizontal distribution of columns.
                             # None, "expand"

                 fancybox=None,  # True use a fancy box, false use a rounded
                                 # box, none use rc
                 shadow=None,
                 title=None,  # set a title for the legend
                 title_fontsize=None,  # set to ax.fontsize if None
                 framealpha=None,  # set frame alpha
                 edgecolor=None,  # frame patch edgecolor
                 facecolor=None,  # frame patch facecolor

                 bbox_to_anchor=None,  # bbox that the legend will be anchored.
                 bbox_transform=None,  # transform for the bbox
                 frameon=None,  # draw frame
                 handler_map=None,
                 ):
        """
        Parameters
        ----------
        parent : `~matplotlib.axes.Axes` or `.Figure`
            The artist that contains the legend.

        handles : sequence of `.Artist`
            A list of Artists (lines, patches) to be added to the legend.

        labels : sequence of strings
            A list of labels to show next to the artists. The length of handles
            and labels should be the same. If they are not, they are truncated
            to the smaller of both lengths.

        Other Parameters
        ----------------

        %(_legend_kw_doc)s

        Notes
        -----

        Users can specify any arbitrary location for the legend using the
        *bbox_to_anchor* keyword argument. bbox_to_anchor can be an instance
        of BboxBase(or its derivatives) or a tuple of 2 or 4 floats.
        See :meth:`set_bbox_to_anchor` for more detail.

        The legend location can be specified by setting *loc* with a tuple of
        2 floats, which is interpreted as the lower-left corner of the legend
        in the normalized axes coordinate.
        """
        # local import only to avoid circularity
        from matplotlib.axes import Axes
        from matplotlib.figure import Figure

        Artist.__init__(self)

        if prop is None:
            if fontsize is not None:
                self.prop = FontProperties(size=fontsize)
            else:
                self.prop = FontProperties(size=rcParams["legend.fontsize"])
        elif isinstance(prop, dict):
            self.prop = FontProperties(**prop)
            if "size" not in prop:
                self.prop.set_size(rcParams["legend.fontsize"])
        else:
            self.prop = prop

        self._fontsize = self.prop.get_size_in_points()

        self.texts = []
        self.legendHandles = []
        self._legend_title_box = None

        #: A dictionary with the extra handler mappings for this Legend
        #: instance.
        self._custom_handler_map = handler_map

        locals_view = locals()
        for name in ["numpoints", "markerscale", "shadow", "columnspacing",
                     "scatterpoints", "handleheight", 'borderpad',
                     'labelspacing', 'handlelength', 'handletextpad',
                     'borderaxespad']:
            if locals_view[name] is None:
                value = rcParams["legend." + name]
            else:
                value = locals_view[name]
            setattr(self, name, value)
        del locals_view
        # trim handles and labels if illegal label...
        _lab, _hand = [], []
        for label, handle in zip(labels, handles):
            if isinstance(label, str) and label.startswith('_'):
                cbook._warn_external('The handle {!r} has a label of {!r} '
                                     'which cannot be automatically added to'
                                     ' the legend.'.format(handle, label))
            else:
                _lab.append(label)
                _hand.append(handle)
        labels, handles = _lab, _hand

        handles = list(handles)
        if len(handles) < 2:
            ncol = 1
        self._ncol = ncol

        if self.numpoints <= 0:
            raise ValueError("numpoints must be > 0; it was %d" % numpoints)

        # introduce y-offset for handles of the scatter plot
        if scatteryoffsets is None:
            self._scatteryoffsets = np.array([3. / 8., 4. / 8., 2.5 / 8.])
        else:
            self._scatteryoffsets = np.asarray(scatteryoffsets)
        reps = self.scatterpoints // len(self._scatteryoffsets) + 1
        self._scatteryoffsets = np.tile(self._scatteryoffsets,
                                        reps)[:self.scatterpoints]

        # _legend_box is an OffsetBox instance that contains all
        # legend items and will be initialized from _init_legend_box()
        # method.
        self._legend_box = None

        if isinstance(parent, Axes):
            self.isaxes = True
            self.axes = parent
            self.set_figure(parent.figure)
        elif isinstance(parent, Figure):
            self.isaxes = False
            self.set_figure(parent)
        else:
            raise TypeError("Legend needs either Axes or Figure as parent")
        self.parent = parent

        self._loc_used_default = loc is None
        if loc is None:
            loc = rcParams["legend.loc"]
            if not self.isaxes and loc in [0, 'best']:
                loc = 'upper right'
        if isinstance(loc, str):
            if loc not in self.codes:
                if self.isaxes:
                    cbook.warn_deprecated(
                        "3.1", message="Unrecognized location {!r}. Falling "
                        "back on 'best'; valid locations are\n\t{}\n"
                        "This will raise an exception %(removal)s."
                        .format(loc, '\n\t'.join(self.codes)))
                    loc = 0
                else:
                    cbook.warn_deprecated(
                        "3.1", message="Unrecognized location {!r}. Falling "
                        "back on 'upper right'; valid locations are\n\t{}\n'"
                        "This will raise an exception %(removal)s."
                        .format(loc, '\n\t'.join(self.codes)))
                    loc = 1
            else:
                loc = self.codes[loc]
        if not self.isaxes and loc == 0:
            cbook.warn_deprecated(
                "3.1", message="Automatic legend placement (loc='best') not "
                "implemented for figure legend. Falling back on 'upper "
                "right'. This will raise an exception %(removal)s.")
            loc = 1

        self._mode = mode
        self.set_bbox_to_anchor(bbox_to_anchor, bbox_transform)

        # We use FancyBboxPatch to draw a legend frame. The location
        # and size of the box will be updated during the drawing time.

        if facecolor is None:
            facecolor = rcParams["legend.facecolor"]
        if facecolor == 'inherit':
            facecolor = rcParams["axes.facecolor"]

        if edgecolor is None:
            edgecolor = rcParams["legend.edgecolor"]
        if edgecolor == 'inherit':
            edgecolor = rcParams["axes.edgecolor"]

        self.legendPatch = FancyBboxPatch(
            xy=(0.0, 0.0), width=1., height=1.,
            facecolor=facecolor,
            edgecolor=edgecolor,
            mutation_scale=self._fontsize,
            snap=True
            )

        # The width and height of the legendPatch will be set (in the
        # draw()) to the length that includes the padding. Thus we set
        # pad=0 here.
        if fancybox is None:
            fancybox = rcParams["legend.fancybox"]

        if fancybox:
            self.legendPatch.set_boxstyle("round", pad=0,
                                          rounding_size=0.2)
        else:
            self.legendPatch.set_boxstyle("square", pad=0)

        self._set_artist_props(self.legendPatch)

        self._drawFrame = frameon
        if frameon is None:
            self._drawFrame = rcParams["legend.frameon"]

        # init with null renderer
        self._init_legend_box(handles, labels, markerfirst)

        # If shadow is activated use framealpha if not
        # explicitly passed. See Issue 8943
        if framealpha is None:
            if shadow:
                self.get_frame().set_alpha(1)
            else:
                self.get_frame().set_alpha(rcParams["legend.framealpha"])
        else:
            self.get_frame().set_alpha(framealpha)

        tmp = self._loc_used_default
        self._set_loc(loc)
        self._loc_used_default = tmp  # ignore changes done by _set_loc

        # figure out title fontsize:
        if title_fontsize is None:
            title_fontsize = rcParams['legend.title_fontsize']
        tprop = FontProperties(size=title_fontsize)
        self.set_title(title, prop=tprop)
        self._last_fontsize_points = self._fontsize
        self._draggable = None
Ejemplo n.º 40
0
def get_tight_layout_figure(fig,
                            axes_list,
                            subplotspec_list,
                            renderer,
                            pad=1.08,
                            h_pad=None,
                            w_pad=None,
                            rect=None):
    """
    Return subplot parameters for tight-layouted-figure with specified padding.

    Parameters
    ----------
    fig : Figure
    axes_list : list of Axes
    subplotspec_list : list of `.SubplotSpec`
        The subplotspecs of each axes.
    renderer : renderer
    pad : float
        Padding between the figure edge and the edges of subplots, as a
        fraction of the font size.
    h_pad, w_pad : float
        Padding (height/width) between edges of adjacent subplots.  Defaults to
        *pad*.
    rect : Tuple[float, float, float, float], optional
        (left, bottom, right, top) rectangle in normalized figure coordinates
        that the whole subplots area (including labels) will fit into.
        Defaults to using the entire figure.

    Returns
    -------
    subplotspec or None
        subplotspec kwargs to be passed to `.Figure.subplots_adjust` or
        None if tight_layout could not be accomplished.

    """

    subplot_list = []
    nrows_list = []
    ncols_list = []
    ax_bbox_list = []

    # Multiple axes can share same subplot_interface (e.g., axes_grid1); thus
    # we need to join them together.
    subplot_dict = {}

    subplotspec_list2 = []

    for ax, subplotspec in zip(axes_list, subplotspec_list):
        if subplotspec is None:
            continue

        subplots = subplot_dict.setdefault(subplotspec, [])

        if not subplots:
            myrows, mycols, _, _ = subplotspec.get_geometry()
            nrows_list.append(myrows)
            ncols_list.append(mycols)
            subplotspec_list2.append(subplotspec)
            subplot_list.append(subplots)
            ax_bbox_list.append(subplotspec.get_position(fig))

        subplots.append(ax)

    if len(nrows_list) == 0 or len(ncols_list) == 0:
        return {}

    max_nrows = max(nrows_list)
    max_ncols = max(ncols_list)

    num1num2_list = []
    for subplotspec in subplotspec_list2:
        rows, cols, num1, num2 = subplotspec.get_geometry()
        div_row, mod_row = divmod(max_nrows, rows)
        div_col, mod_col = divmod(max_ncols, cols)
        if mod_row != 0:
            cbook._warn_external('tight_layout not applied: number of rows '
                                 'in subplot specifications must be '
                                 'multiples of one another.')
            return {}
        if mod_col != 0:
            cbook._warn_external('tight_layout not applied: number of '
                                 'columns in subplot specifications must be '
                                 'multiples of one another.')
            return {}

        rowNum1, colNum1 = divmod(num1, cols)
        if num2 is None:
            rowNum2, colNum2 = rowNum1, colNum1
        else:
            rowNum2, colNum2 = divmod(num2, cols)

        num1num2_list.append(
            (rowNum1 * div_row * max_ncols + colNum1 * div_col,
             ((rowNum2 + 1) * div_row - 1) * max_ncols +
             (colNum2 + 1) * div_col - 1))

    kwargs = auto_adjust_subplotpars(fig,
                                     renderer,
                                     nrows_ncols=(max_nrows, max_ncols),
                                     num1num2_list=num1num2_list,
                                     subplot_list=subplot_list,
                                     ax_bbox_list=ax_bbox_list,
                                     pad=pad,
                                     h_pad=h_pad,
                                     w_pad=w_pad)

    # kwargs can be none if tight_layout fails...
    if rect is not None and kwargs is not None:
        # if rect is given, the whole subplots area (including
        # labels) will fit into the rect instead of the
        # figure. Note that the rect argument of
        # *auto_adjust_subplotpars* specify the area that will be
        # covered by the total area of axes.bbox. Thus we call
        # auto_adjust_subplotpars twice, where the second run
        # with adjusted rect parameters.

        left, bottom, right, top = rect
        if left is not None:
            left += kwargs["left"]
        if bottom is not None:
            bottom += kwargs["bottom"]
        if right is not None:
            right -= (1 - kwargs["right"])
        if top is not None:
            top -= (1 - kwargs["top"])

        kwargs = auto_adjust_subplotpars(fig,
                                         renderer,
                                         nrows_ncols=(max_nrows, max_ncols),
                                         num1num2_list=num1num2_list,
                                         subplot_list=subplot_list,
                                         ax_bbox_list=ax_bbox_list,
                                         pad=pad,
                                         h_pad=h_pad,
                                         w_pad=w_pad,
                                         rect=(left, bottom, right, top))

    return kwargs
Ejemplo n.º 41
0
    def _init_legend_box(self, handles, labels, markerfirst=True):
        """
        Initialize the legend_box. The legend_box is an instance of
        the OffsetBox, which is packed with legend handles and
        texts. Once packed, their location is calculated during the
        drawing time.
        """

        fontsize = self._fontsize

        # legend_box is a HPacker, horizontally packed with
        # columns. Each column is a VPacker, vertically packed with
        # legend items. Each legend item is HPacker packed with
        # legend handleBox and labelBox. handleBox is an instance of
        # offsetbox.DrawingArea which contains legend handle. labelBox
        # is an instance of offsetbox.TextArea which contains legend
        # text.

        text_list = []  # the list of text instances
        handle_list = []  # the list of text instances
        handles_and_labels = []

        label_prop = dict(verticalalignment='baseline',
                          horizontalalignment='left',
                          fontproperties=self.prop,
                          )

        # The approximate height and descent of text. These values are
        # only used for plotting the legend handle.
        descent = 0.35 * self._approx_text_height() * (self.handleheight - 0.7)
        # 0.35 and 0.7 are just heuristic numbers and may need to be improved.
        height = self._approx_text_height() * self.handleheight - descent
        # each handle needs to be drawn inside a box of (x, y, w, h) =
        # (0, -descent, width, height).  And their coordinates should
        # be given in the display coordinates.

        # The transformation of each handle will be automatically set
        # to self.get_transform(). If the artist does not use its
        # default transform (e.g., Collections), you need to
        # manually set their transform to the self.get_transform().
        legend_handler_map = self.get_legend_handler_map()

        for orig_handle, lab in zip(handles, labels):
            handler = self.get_legend_handler(legend_handler_map, orig_handle)
            if handler is None:
                cbook._warn_external(
                    "Legend does not support {!r} instances.\nA proxy artist "
                    "may be used instead.\nSee: "
                    "http://matplotlib.org/users/legend_guide.html"
                    "#creating-artists-specifically-for-adding-to-the-legend-"
                    "aka-proxy-artists".format(orig_handle))
                # We don't have a handle for this artist, so we just defer
                # to None.
                handle_list.append(None)
            else:
                textbox = TextArea(lab, textprops=label_prop,
                                   multilinebaseline=True,
                                   minimumdescent=True)
                handlebox = DrawingArea(width=self.handlelength * fontsize,
                                        height=height,
                                        xdescent=0., ydescent=descent)

                text_list.append(textbox._text)
                # Create the artist for the legend which represents the
                # original artist/handle.
                handle_list.append(handler.legend_artist(self, orig_handle,
                                                         fontsize, handlebox))
                handles_and_labels.append((handlebox, textbox))

        if handles_and_labels:
            # We calculate number of rows in each column. The first
            # (num_largecol) columns will have (nrows+1) rows, and remaining
            # (num_smallcol) columns will have (nrows) rows.
            ncol = min(self._ncol, len(handles_and_labels))
            nrows, num_largecol = divmod(len(handles_and_labels), ncol)
            num_smallcol = ncol - num_largecol
            # starting index of each column and number of rows in it.
            rows_per_col = [nrows + 1] * num_largecol + [nrows] * num_smallcol
            start_idxs = np.concatenate([[0], np.cumsum(rows_per_col)[:-1]])
            cols = zip(start_idxs, rows_per_col)
        else:
            cols = []

        columnbox = []
        for i0, di in cols:
            # pack handleBox and labelBox into itemBox
            itemBoxes = [HPacker(pad=0,
                                 sep=self.handletextpad * fontsize,
                                 children=[h, t] if markerfirst else [t, h],
                                 align="baseline")
                         for h, t in handles_and_labels[i0:i0 + di]]
            # minimumdescent=False for the text of the last row of the column
            if markerfirst:
                itemBoxes[-1].get_children()[1].set_minimumdescent(False)
            else:
                itemBoxes[-1].get_children()[0].set_minimumdescent(False)

            # pack columnBox
            alignment = "baseline" if markerfirst else "right"
            columnbox.append(VPacker(pad=0,
                                     sep=self.labelspacing * fontsize,
                                     align=alignment,
                                     children=itemBoxes))

        mode = "expand" if self._mode == "expand" else "fixed"
        sep = self.columnspacing * fontsize
        self._legend_handle_box = HPacker(pad=0,
                                          sep=sep, align="baseline",
                                          mode=mode,
                                          children=columnbox)
        self._legend_title_box = TextArea("")
        self._legend_box = VPacker(pad=self.borderpad * fontsize,
                                   sep=self.labelspacing * fontsize,
                                   align="center",
                                   children=[self._legend_title_box,
                                             self._legend_handle_box])
        self._legend_box.set_figure(self.figure)
        self.texts = text_list
        self.legendHandles = handle_list
Ejemplo n.º 42
0
def test_warn_external_frame_embedded_python():
    with patch.object(cbook, "sys") as mock_sys:
        mock_sys._getframe = Mock(return_value=None)
        with pytest.warns(UserWarning, match=r"\Adummy\Z"):
            cbook._warn_external("dummy")
Ejemplo n.º 43
0
def test_warn_external(recwarn):
    cbook._warn_external("oops")
    assert len(recwarn) == 1
    assert recwarn[0].filename == __file__
Ejemplo n.º 44
0
def auto_adjust_subplotpars(fig,
                            renderer,
                            nrows_ncols,
                            num1num2_list,
                            subplot_list,
                            ax_bbox_list=None,
                            pad=1.08,
                            h_pad=None,
                            w_pad=None,
                            rect=None):
    """
    Return a dict of subplot parameters to adjust spacing between subplots
    or ``None`` if resulting axes would have zero height or width.

    Note that this function ignores geometry information of subplot
    itself, but uses what is given by the *nrows_ncols* and *num1num2_list*
    parameters.  Also, the results could be incorrect if some subplots have
    ``adjustable=datalim``.

    Parameters
    ----------
    nrows_ncols : Tuple[int, int]
        Number of rows and number of columns of the grid.
    num1num2_list : List[int]
        List of numbers specifying the area occupied by the subplot
    subplot_list : list of subplots
        List of subplots that will be used to calculate optimal subplot_params.
    pad : float
        Padding between the figure edge and the edges of subplots, as a
        fraction of the font size.
    h_pad, w_pad : float
        Padding (height/width) between edges of adjacent subplots, as a
        fraction of the font size.  Defaults to *pad*.
    rect : Tuple[float, float, float, float]
        [left, bottom, right, top] in normalized (0, 1) figure coordinates.
    """
    rows, cols = nrows_ncols

    font_size_inches = (
        FontProperties(size=rcParams["font.size"]).get_size_in_points() / 72)
    pad_inches = pad * font_size_inches
    if h_pad is not None:
        vpad_inches = h_pad * font_size_inches
    else:
        vpad_inches = pad_inches

    if w_pad is not None:
        hpad_inches = w_pad * font_size_inches
    else:
        hpad_inches = pad_inches

    if len(num1num2_list) != len(subplot_list) or len(subplot_list) == 0:
        raise ValueError

    if rect is None:
        margin_left = margin_bottom = margin_right = margin_top = None
    else:
        margin_left, margin_bottom, _right, _top = rect
        if _right:
            margin_right = 1 - _right
        else:
            margin_right = None
        if _top:
            margin_top = 1 - _top
        else:
            margin_top = None

    vspaces = [[] for i in range((rows + 1) * cols)]
    hspaces = [[] for i in range(rows * (cols + 1))]

    union = Bbox.union

    if ax_bbox_list is None:
        ax_bbox_list = []
        for subplots in subplot_list:
            ax_bbox = union(
                [ax.get_position(original=True) for ax in subplots])
            ax_bbox_list.append(ax_bbox)

    for subplots, ax_bbox, (num1, num2) in zip(subplot_list, ax_bbox_list,
                                               num1num2_list):
        if all(not ax.get_visible() for ax in subplots):
            continue

        tight_bbox_raw = union([
            ax.get_tightbbox(renderer) for ax in subplots if ax.get_visible()
        ])
        tight_bbox = TransformedBbox(tight_bbox_raw,
                                     fig.transFigure.inverted())

        row1, col1 = divmod(num1, cols)

        if num2 is None:
            # left
            hspaces[row1 * (cols + 1) + col1].append(
                _get_left(tight_bbox, ax_bbox))
            # right
            hspaces[row1 * (cols + 1) + (col1 + 1)].append(
                _get_right(tight_bbox, ax_bbox))
            # top
            vspaces[row1 * cols + col1].append(_get_top(tight_bbox, ax_bbox))
            # bottom
            vspaces[(row1 + 1) * cols + col1].append(
                _get_bottom(tight_bbox, ax_bbox))

        else:
            row2, col2 = divmod(num2, cols)

            for row_i in range(row1, row2 + 1):
                # left
                hspaces[row_i * (cols + 1) + col1].append(
                    _get_left(tight_bbox, ax_bbox))
                # right
                hspaces[row_i * (cols + 1) + (col2 + 1)].append(
                    _get_right(tight_bbox, ax_bbox))
            for col_i in range(col1, col2 + 1):
                # top
                vspaces[row1 * cols + col_i].append(
                    _get_top(tight_bbox, ax_bbox))
                # bottom
                vspaces[(row2 + 1) * cols + col_i].append(
                    _get_bottom(tight_bbox, ax_bbox))

    fig_width_inch, fig_height_inch = fig.get_size_inches()

    # margins can be negative for axes with aspect applied. And we
    # append + [0] to make minimum margins 0

    if not margin_left:
        margin_left = max([sum(s) for s in hspaces[::cols + 1]] + [0])
        margin_left += pad_inches / fig_width_inch

    if not margin_right:
        margin_right = max([sum(s) for s in hspaces[cols::cols + 1]] + [0])
        margin_right += pad_inches / fig_width_inch

    if not margin_top:
        margin_top = max([sum(s) for s in vspaces[:cols]] + [0])
        margin_top += pad_inches / fig_height_inch

    if not margin_bottom:
        margin_bottom = max([sum(s) for s in vspaces[-cols:]] + [0])
        margin_bottom += pad_inches / fig_height_inch

    if margin_left + margin_right >= 1:
        cbook._warn_external('Tight layout not applied. The left and right '
                             'margins cannot be made large enough to '
                             'accommodate all axes decorations. ')
        return None
    if margin_bottom + margin_top >= 1:
        cbook._warn_external('Tight layout not applied. The bottom and top '
                             'margins cannot be made large enough to '
                             'accommodate all axes decorations. ')
        return None

    kwargs = dict(left=margin_left,
                  right=1 - margin_right,
                  bottom=margin_bottom,
                  top=1 - margin_top)
    if cols > 1:
        hspace = (max(
            sum(s) for i in range(rows)
            for s in hspaces[i * (cols + 1) + 1:(i + 1) * (cols + 1) - 1]) +
                  hpad_inches / fig_width_inch)
        # axes widths:
        h_axes = (1 - margin_right - margin_left - hspace * (cols - 1)) / cols
        if h_axes < 0:
            cbook._warn_external('Tight layout not applied. tight_layout '
                                 'cannot make axes width small enough to '
                                 'accommodate all axes decorations')
            return None
        else:
            kwargs["wspace"] = hspace / h_axes

    if rows > 1:
        vspace = (max(sum(s) for s in vspaces[cols:-cols]) +
                  vpad_inches / fig_height_inch)
        v_axes = (1 - margin_top - margin_bottom - vspace * (rows - 1)) / rows
        if v_axes < 0:
            cbook._warn_external('Tight layout not applied. tight_layout '
                                 'cannot make axes height small enough to '
                                 'accommodate all axes decorations')
            return None
        else:
            kwargs["hspace"] = vspace / v_axes

    return kwargs
Ejemplo n.º 45
0
def auto_adjust_subplotpars(fig,
                            renderer,
                            nrows_ncols,
                            num1num2_list,
                            subplot_list,
                            ax_bbox_list=None,
                            pad=1.08,
                            h_pad=None,
                            w_pad=None,
                            rect=None):
    """
    Return a dict of subplot parameters to adjust spacing between subplots
    or ``None`` if resulting axes would have zero height or width.

    Note that this function ignores geometry information of subplot
    itself, but uses what is given by the *nrows_ncols* and *num1num2_list*
    parameters.  Also, the results could be incorrect if some subplots have
    ``adjustable=datalim``.

    Parameters
    ----------
    nrows_ncols : Tuple[int, int]
        Number of rows and number of columns of the grid.
    num1num2_list : List[int]
        List of numbers specifying the area occupied by the subplot
    subplot_list : list of subplots
        List of subplots that will be used to calculate optimal subplot_params.
    pad : float
        Padding between the figure edge and the edges of subplots, as a
        fraction of the font size.
    h_pad, w_pad : float
        Padding (height/width) between edges of adjacent subplots, as a
        fraction of the font size.  Defaults to *pad*.
    rect : Tuple[float, float, float, float]
        [left, bottom, right, top] in normalized (0, 1) figure coordinates.
    """
    rows, cols = nrows_ncols

    font_size_inches = (
        FontProperties(size=rcParams["font.size"]).get_size_in_points() / 72)
    pad_inches = pad * font_size_inches
    vpad_inches = h_pad * font_size_inches if h_pad is not None else pad_inches
    hpad_inches = w_pad * font_size_inches if w_pad is not None else pad_inches

    if len(num1num2_list) != len(subplot_list) or len(subplot_list) == 0:
        raise ValueError

    if rect is None:
        margin_left = margin_bottom = margin_right = margin_top = None
    else:
        margin_left, margin_bottom, _right, _top = rect
        margin_right = 1 - _right if _right else None
        margin_top = 1 - _top if _top else None

    vspaces = np.zeros((rows + 1, cols))
    hspaces = np.zeros((rows, cols + 1))

    if ax_bbox_list is None:
        ax_bbox_list = [
            Bbox.union([ax.get_position(original=True) for ax in subplots])
            for subplots in subplot_list
        ]

    for subplots, ax_bbox, (num1, num2) in zip(subplot_list, ax_bbox_list,
                                               num1num2_list):
        if all(not ax.get_visible() for ax in subplots):
            continue

        bb = []
        for ax in subplots:
            if ax.get_visible():
                try:
                    bb += [ax.get_tightbbox(renderer, for_layout_only=True)]
                except TypeError:
                    bb += [ax.get_tightbbox(renderer)]

        tight_bbox_raw = Bbox.union(bb)
        tight_bbox = TransformedBbox(tight_bbox_raw,
                                     fig.transFigure.inverted())

        row1, col1 = divmod(num1, cols)
        if num2 is None:
            num2 = num1
        row2, col2 = divmod(num2, cols)

        for row_i in range(row1, row2 + 1):
            hspaces[row_i, col1] += ax_bbox.xmin - tight_bbox.xmin  # left
            hspaces[row_i, col2 + 1] += tight_bbox.xmax - ax_bbox.xmax  # right
        for col_i in range(col1, col2 + 1):
            vspaces[row1, col_i] += tight_bbox.ymax - ax_bbox.ymax  # top
            vspaces[row2 + 1, col_i] += ax_bbox.ymin - tight_bbox.ymin  # bot.

    fig_width_inch, fig_height_inch = fig.get_size_inches()

    # margins can be negative for axes with aspect applied, so use max(, 0) to
    # make them nonnegative.
    if not margin_left:
        margin_left = (max(hspaces[:, 0].max(), 0) +
                       pad_inches / fig_width_inch)
    if not margin_right:
        margin_right = (max(hspaces[:, -1].max(), 0) +
                        pad_inches / fig_width_inch)
    if not margin_top:
        margin_top = (max(vspaces[0, :].max(), 0) +
                      pad_inches / fig_height_inch)
        suptitle = fig._suptitle
        if suptitle and suptitle.get_in_layout():
            rel_suptitle_height = fig.transFigure.inverted().transform_bbox(
                suptitle.get_window_extent(renderer)).height
            margin_top += rel_suptitle_height + pad_inches / fig_height_inch
    if not margin_bottom:
        margin_bottom = (max(vspaces[-1, :].max(), 0) +
                         pad_inches / fig_height_inch)

    if margin_left + margin_right >= 1:
        cbook._warn_external('Tight layout not applied. The left and right '
                             'margins cannot be made large enough to '
                             'accommodate all axes decorations. ')
        return None
    if margin_bottom + margin_top >= 1:
        cbook._warn_external('Tight layout not applied. The bottom and top '
                             'margins cannot be made large enough to '
                             'accommodate all axes decorations. ')
        return None

    kwargs = dict(left=margin_left,
                  right=1 - margin_right,
                  bottom=margin_bottom,
                  top=1 - margin_top)

    if cols > 1:
        hspace = hspaces[:, 1:-1].max() + hpad_inches / fig_width_inch
        # axes widths:
        h_axes = (1 - margin_right - margin_left - hspace * (cols - 1)) / cols
        if h_axes < 0:
            cbook._warn_external('Tight layout not applied. tight_layout '
                                 'cannot make axes width small enough to '
                                 'accommodate all axes decorations')
            return None
        else:
            kwargs["wspace"] = hspace / h_axes
    if rows > 1:
        vspace = vspaces[1:-1, :].max() + vpad_inches / fig_height_inch
        v_axes = (1 - margin_top - margin_bottom - vspace * (rows - 1)) / rows
        if v_axes < 0:
            cbook._warn_external('Tight layout not applied. tight_layout '
                                 'cannot make axes height small enough to '
                                 'accommodate all axes decorations')
            return None
        else:
            kwargs["hspace"] = vspace / v_axes

    return kwargs
Ejemplo n.º 46
0
 def _calc_offset_transform(self):
     """Calculate the offset transform performed by the spine."""
     self._ensure_position_is_set()
     position = self._position
     if isinstance(position, str):
         if position == 'center':
             position = ('axes', 0.5)
         elif position == 'zero':
             position = ('data', 0)
     assert len(position) == 2, "position should be 2-tuple"
     position_type, amount = position
     assert position_type in ('axes', 'outward', 'data')
     if position_type == 'outward':
         if amount == 0:
             # short circuit commonest case
             self._spine_transform = ('identity',
                                      mtransforms.IdentityTransform())
         elif self.spine_type in ['left', 'right', 'top', 'bottom']:
             offset_vec = {'left': (-1, 0),
                           'right': (1, 0),
                           'bottom': (0, -1),
                           'top': (0, 1),
                           }[self.spine_type]
             # calculate x and y offset in dots
             offset_x = amount * offset_vec[0] / 72.0
             offset_y = amount * offset_vec[1] / 72.0
             self._spine_transform = ('post',
                                      mtransforms.ScaledTranslation(
                                          offset_x,
                                          offset_y,
                                          self.figure.dpi_scale_trans))
         else:
             cbook._warn_external('unknown spine type "%s": no spine '
                                  'offset performed' % self.spine_type)
             self._spine_transform = ('identity',
                                      mtransforms.IdentityTransform())
     elif position_type == 'axes':
         if self.spine_type in ('left', 'right'):
             self._spine_transform = ('pre',
                                      mtransforms.Affine2D.from_values(
                                          # keep y unchanged, fix x at
                                          # amount
                                          0, 0, 0, 1, amount, 0))
         elif self.spine_type in ('bottom', 'top'):
             self._spine_transform = ('pre',
                                      mtransforms.Affine2D.from_values(
                                          # keep x unchanged, fix y at
                                          # amount
                                          1, 0, 0, 0, 0, amount))
         else:
             cbook._warn_external('unknown spine type "%s": no spine '
                                  'offset performed' % self.spine_type)
             self._spine_transform = ('identity',
                                      mtransforms.IdentityTransform())
     elif position_type == 'data':
         if self.spine_type in ('right', 'top'):
             # The right and top spines have a default position of 1 in
             # axes coordinates.  When specifying the position in data
             # coordinates, we need to calculate the position relative to 0.
             amount -= 1
         if self.spine_type in ('left', 'right'):
             self._spine_transform = ('data',
                                      mtransforms.Affine2D().translate(
                                          amount, 0))
         elif self.spine_type in ('bottom', 'top'):
             self._spine_transform = ('data',
                                      mtransforms.Affine2D().translate(
                                          0, amount))
         else:
             cbook._warn_external('unknown spine type "%s": no spine '
                                  'offset performed' % self.spine_type)
             self._spine_transform = ('identity',
                                      mtransforms.IdentityTransform())
    def print_png(self,
                  filename_or_obj,
                  *args,
                  metadata=None,
                  pil_kwargs=None,
                  **kwargs):
        """
        Write the figure to a PNG file.

        Parameters
        ----------
        filename_or_obj : str or PathLike or file-like object
            The file to write to.

        metadata : dict, optional
            Metadata in the PNG file as key-value pairs of bytes or latin-1
            encodable strings.
            According to the PNG specification, keys must be shorter than 79
            chars.

            The `PNG specification`_ defines some common keywords that may be
            used as appropriate:

            - Title: Short (one line) title or caption for image.
            - Author: Name of image's creator.
            - Description: Description of image (possibly long).
            - Copyright: Copyright notice.
            - Creation Time: Time of original image creation
              (usually RFC 1123 format).
            - Software: Software used to create the image.
            - Disclaimer: Legal disclaimer.
            - Warning: Warning of nature of content.
            - Source: Device used to create the image.
            - Comment: Miscellaneous comment;
              conversion from other image format.

            Other keywords may be invented for other purposes.

            If 'Software' is not given, an autogenerated value for matplotlib
            will be used.

            For more details see the `PNG specification`_.

            .. _PNG specification: \
                https://www.w3.org/TR/2003/REC-PNG-20031110/#11keywords

        pil_kwargs : dict, optional
            If set to a non-None value, use Pillow to save the figure instead
            of Matplotlib's builtin PNG support, and pass these keyword
            arguments to `PIL.Image.save`.

            If the 'pnginfo' key is present, it completely overrides
            *metadata*, including the default 'Software' key.
        """
        from matplotlib import _png

        if metadata is None:
            metadata = {}
        default_metadata = {
            "Software":
            f"matplotlib version{__version__}, http://matplotlib.org/",
        }

        FigureCanvasAgg.draw(self)
        if pil_kwargs is not None:
            from PIL import Image
            from PIL.PngImagePlugin import PngInfo
            # Only use the metadata kwarg if pnginfo is not set, because the
            # semantics of duplicate keys in pnginfo is unclear.
            if "pnginfo" in pil_kwargs:
                if metadata:
                    cbook._warn_external("'metadata' is overridden by the "
                                         "'pnginfo' entry in 'pil_kwargs'.")
            else:
                pnginfo = PngInfo()
                for k, v in {**default_metadata, **metadata}.items():
                    pnginfo.add_text(k, v)
                pil_kwargs["pnginfo"] = pnginfo
            pil_kwargs.setdefault("dpi", (self.figure.dpi, self.figure.dpi))
            (Image.fromarray(np.asarray(self.buffer_rgba())).save(
                filename_or_obj, format="png", **pil_kwargs))

        else:
            renderer = self.get_renderer()
            with cbook.open_file_cm(filename_or_obj, "wb") as fh:
                _png.write_png(renderer._renderer,
                               fh,
                               self.figure.dpi,
                               metadata={
                                   **default_metadata,
                                   **metadata
                               })
Ejemplo n.º 48
0
 def set_aspect(self, *args, **kwargs):
     """
     Secondary axes cannot set the aspect ratio, so calling this just
     sets a warning.
     """
     cbook._warn_external("Secondary axes can't set the aspect ratio")
Ejemplo n.º 49
0
def get_parallels(bezier2, width):
    """
    Given the quadratic bezier control points *bezier2*, returns
    control points of quadratic bezier lines roughly parallel to given
    one separated by *width*.
    """

    # The parallel bezier lines are constructed by following ways.
    #  c1 and c2 are control points representing the begin and end of the
    #  bezier line.
    #  cm is the middle point

    c1x, c1y = bezier2[0]
    cmx, cmy = bezier2[1]
    c2x, c2y = bezier2[2]

    parallel_test = check_if_parallel(c1x - cmx, c1y - cmy,
                                      cmx - c2x, cmy - c2y)

    if parallel_test == -1:
        cbook._warn_external(
            "Lines do not intersect. A straight line is used instead.")
        cos_t1, sin_t1 = get_cos_sin(c1x, c1y, c2x, c2y)
        cos_t2, sin_t2 = cos_t1, sin_t1
    else:
        # t1 and t2 is the angle between c1 and cm, cm, c2.  They are
        # also a angle of the tangential line of the path at c1 and c2
        cos_t1, sin_t1 = get_cos_sin(c1x, c1y, cmx, cmy)
        cos_t2, sin_t2 = get_cos_sin(cmx, cmy, c2x, c2y)

    # find c1_left, c1_right which are located along the lines
    # through c1 and perpendicular to the tangential lines of the
    # bezier path at a distance of width. Same thing for c2_left and
    # c2_right with respect to c2.
    c1x_left, c1y_left, c1x_right, c1y_right = (
        get_normal_points(c1x, c1y, cos_t1, sin_t1, width)
    )
    c2x_left, c2y_left, c2x_right, c2y_right = (
        get_normal_points(c2x, c2y, cos_t2, sin_t2, width)
    )

    # find cm_left which is the intersecting point of a line through
    # c1_left with angle t1 and a line through c2_left with angle
    # t2. Same with cm_right.
    if parallel_test != 0:
        # a special case for a straight line, i.e., angle between two
        # lines are smaller than some (arbitrary) value.
        cmx_left, cmy_left = (
            0.5 * (c1x_left + c2x_left), 0.5 * (c1y_left + c2y_left)
        )
        cmx_right, cmy_right = (
            0.5 * (c1x_right + c2x_right), 0.5 * (c1y_right + c2y_right)
        )
    else:
        cmx_left, cmy_left = get_intersection(c1x_left, c1y_left, cos_t1,
                                              sin_t1, c2x_left, c2y_left,
                                              cos_t2, sin_t2)

        cmx_right, cmy_right = get_intersection(c1x_right, c1y_right, cos_t1,
                                                sin_t1, c2x_right, c2y_right,
                                                cos_t2, sin_t2)

    # the parallel bezier lines are created with control points of
    # [c1_left, cm_left, c2_left] and [c1_right, cm_right, c2_right]
    path_left = [(c1x_left, c1y_left),
                 (cmx_left, cmy_left),
                 (c2x_left, c2y_left)]
    path_right = [(c1x_right, c1y_right),
                  (cmx_right, cmy_right),
                  (c2x_right, c2y_right)]

    return path_left, path_right
Ejemplo n.º 50
0
    def __init__(self, parent, handles, labels,
                 loc=None,
                 numpoints=None,    # the number of points in the legend line
                 markerscale=None,  # the relative size of legend markers
                                    # vs. original
                 markerfirst=True,  # controls ordering (left-to-right) of
                                    # legend marker and label
                 scatterpoints=None,    # number of scatter points
                 scatteryoffsets=None,
                 prop=None,          # properties for the legend texts
                 fontsize=None,      # keyword to set font size directly
                 labelcolor=None,    # keyword to set the text color

                 # spacing & pad defined as a fraction of the font-size
                 borderpad=None,      # the whitespace inside the legend border
                 labelspacing=None,   # the vertical space between the legend
                                      # entries
                 handlelength=None,   # the length of the legend handles
                 handleheight=None,   # the height of the legend handles
                 handletextpad=None,  # the pad between the legend handle
                                      # and text
                 borderaxespad=None,  # the pad between the axes and legend
                                      # border
                 columnspacing=None,  # spacing between columns

                 ncol=1,     # number of columns
                 mode=None,  # mode for horizontal distribution of columns.
                             # None, "expand"

                 fancybox=None,  # True use a fancy box, false use a rounded
                                 # box, none use rc
                 shadow=None,
                 title=None,  # set a title for the legend
                 title_fontsize=None,  # the font size for the title
                 framealpha=None,  # set frame alpha
                 edgecolor=None,  # frame patch edgecolor
                 facecolor=None,  # frame patch facecolor

                 bbox_to_anchor=None,  # bbox that the legend will be anchored.
                 bbox_transform=None,  # transform for the bbox
                 frameon=None,  # draw frame
                 handler_map=None,
                 ):
        """
        Parameters
        ----------
        parent : `~matplotlib.axes.Axes` or `.Figure`
            The artist that contains the legend.

        handles : list of `.Artist`
            A list of Artists (lines, patches) to be added to the legend.

        labels : list of str
            A list of labels to show next to the artists. The length of handles
            and labels should be the same. If they are not, they are truncated
            to the smaller of both lengths.

        Other Parameters
        ----------------
        %(_legend_kw_doc)s

        Notes
        -----
        Users can specify any arbitrary location for the legend using the
        *bbox_to_anchor* keyword argument. *bbox_to_anchor* can be a
        `.BboxBase` (or derived therefrom) or a tuple of 2 or 4 floats.
        See `set_bbox_to_anchor` for more detail.

        The legend location can be specified by setting *loc* with a tuple of
        2 floats, which is interpreted as the lower-left corner of the legend
        in the normalized axes coordinate.
        """
        # local import only to avoid circularity
        from matplotlib.axes import Axes
        from matplotlib.figure import Figure

        Artist.__init__(self)

        if prop is None:
            if fontsize is not None:
                self.prop = FontProperties(size=fontsize)
            else:
                self.prop = FontProperties(
                    size=mpl.rcParams["legend.fontsize"])
        else:
            self.prop = FontProperties._from_any(prop)
            if isinstance(prop, dict) and "size" not in prop:
                self.prop.set_size(mpl.rcParams["legend.fontsize"])

        self._fontsize = self.prop.get_size_in_points()

        self.texts = []
        self.legendHandles = []
        self._legend_title_box = None

        #: A dictionary with the extra handler mappings for this Legend
        #: instance.
        self._custom_handler_map = handler_map

        locals_view = locals()
        for name in ["numpoints", "markerscale", "shadow", "columnspacing",
                     "scatterpoints", "handleheight", 'borderpad',
                     'labelspacing', 'handlelength', 'handletextpad',
                     'borderaxespad']:
            if locals_view[name] is None:
                value = mpl.rcParams["legend." + name]
            else:
                value = locals_view[name]
            setattr(self, name, value)
        del locals_view
        # trim handles and labels if illegal label...
        _lab, _hand = [], []
        for label, handle in zip(labels, handles):
            if isinstance(label, str) and label.startswith('_'):
                cbook._warn_external('The handle {!r} has a label of {!r} '
                                     'which cannot be automatically added to'
                                     ' the legend.'.format(handle, label))
            else:
                _lab.append(label)
                _hand.append(handle)
        labels, handles = _lab, _hand

        handles = list(handles)
        if len(handles) < 2:
            ncol = 1
        self._ncol = ncol

        if self.numpoints <= 0:
            raise ValueError("numpoints must be > 0; it was %d" % numpoints)

        # introduce y-offset for handles of the scatter plot
        if scatteryoffsets is None:
            self._scatteryoffsets = np.array([3. / 8., 4. / 8., 2.5 / 8.])
        else:
            self._scatteryoffsets = np.asarray(scatteryoffsets)
        reps = self.scatterpoints // len(self._scatteryoffsets) + 1
        self._scatteryoffsets = np.tile(self._scatteryoffsets,
                                        reps)[:self.scatterpoints]

        # _legend_box is a VPacker instance that contains all
        # legend items and will be initialized from _init_legend_box()
        # method.
        self._legend_box = None

        if isinstance(parent, Axes):
            self.isaxes = True
            self.axes = parent
            self.set_figure(parent.figure)
        elif isinstance(parent, Figure):
            self.isaxes = False
            self.set_figure(parent)
        else:
            raise TypeError("Legend needs either Axes or Figure as parent")
        self.parent = parent

        self._loc_used_default = loc is None
        if loc is None:
            loc = mpl.rcParams["legend.loc"]
            if not self.isaxes and loc in [0, 'best']:
                loc = 'upper right'
        if isinstance(loc, str):
            if loc not in self.codes:
                raise ValueError(
                    "Unrecognized location {!r}. Valid locations are\n\t{}\n"
                    .format(loc, '\n\t'.join(self.codes)))
            else:
                loc = self.codes[loc]
        if not self.isaxes and loc == 0:
            raise ValueError(
                "Automatic legend placement (loc='best') not implemented for "
                "figure legend.")

        self._mode = mode
        self.set_bbox_to_anchor(bbox_to_anchor, bbox_transform)

        # We use FancyBboxPatch to draw a legend frame. The location
        # and size of the box will be updated during the drawing time.

        if facecolor is None:
            facecolor = mpl.rcParams["legend.facecolor"]
        if facecolor == 'inherit':
            facecolor = mpl.rcParams["axes.facecolor"]

        if edgecolor is None:
            edgecolor = mpl.rcParams["legend.edgecolor"]
        if edgecolor == 'inherit':
            edgecolor = mpl.rcParams["axes.edgecolor"]

        if fancybox is None:
            fancybox = mpl.rcParams["legend.fancybox"]

        self.legendPatch = FancyBboxPatch(
            xy=(0, 0), width=1, height=1,
            facecolor=facecolor, edgecolor=edgecolor,
            # If shadow is used, default to alpha=1 (#8943).
            alpha=(framealpha if framealpha is not None
                   else 1 if shadow
                   else mpl.rcParams["legend.framealpha"]),
            # The width and height of the legendPatch will be set (in draw())
            # to the length that includes the padding. Thus we set pad=0 here.
            boxstyle=("round,pad=0,rounding_size=0.2" if fancybox
                      else "square,pad=0"),
            mutation_scale=self._fontsize,
            snap=True,
            visible=(frameon if frameon is not None
                     else mpl.rcParams["legend.frameon"])
        )
        self._set_artist_props(self.legendPatch)

        # init with null renderer
        self._init_legend_box(handles, labels, markerfirst)

        tmp = self._loc_used_default
        self._set_loc(loc)
        self._loc_used_default = tmp  # ignore changes done by _set_loc

        # figure out title fontsize:
        if title_fontsize is None:
            title_fontsize = mpl.rcParams['legend.title_fontsize']
        tprop = FontProperties(size=title_fontsize)
        self.set_title(title, prop=tprop)
        self._draggable = None

        # set the text color

        color_getters = {  # getter function depends on line or patch
            'linecolor':       ['get_color',           'get_facecolor'],
            'markerfacecolor': ['get_markerfacecolor', 'get_facecolor'],
            'mfc':             ['get_markerfacecolor', 'get_facecolor'],
            'markeredgecolor': ['get_markeredgecolor', 'get_edgecolor'],
            'mec':             ['get_markeredgecolor', 'get_edgecolor'],
        }
        if labelcolor is None:
            pass
        elif isinstance(labelcolor, str) and labelcolor in color_getters:
            getter_names = color_getters[labelcolor]
            for handle, text in zip(self.legendHandles, self.texts):
                for getter_name in getter_names:
                    try:
                        color = getattr(handle, getter_name)()
                        text.set_color(color)
                        break
                    except AttributeError:
                        pass
        elif np.iterable(labelcolor):
            for text, color in zip(self.texts,
                                   itertools.cycle(
                                       colors.to_rgba_array(labelcolor))):
                text.set_color(color)
        else:
            raise ValueError("Invalid argument for labelcolor : %s" %
                             str(labelcolor))
Ejemplo n.º 51
0
def auto_adjust_subplotpars(
        fig, renderer, nrows_ncols, num1num2_list, subplot_list,
        ax_bbox_list=None, pad=1.08, h_pad=None, w_pad=None, rect=None):
    """
    Return a dict of subplot parameters to adjust spacing between subplots
    or ``None`` if resulting axes would have zero height or width.

    Note that this function ignores geometry information of subplot
    itself, but uses what is given by the *nrows_ncols* and *num1num2_list*
    parameters.  Also, the results could be incorrect if some subplots have
    ``adjustable=datalim``.

    Parameters
    ----------
    nrows_ncols : Tuple[int, int]
        Number of rows and number of columns of the grid.
    num1num2_list : List[int]
        List of numbers specifying the area occupied by the subplot
    subplot_list : list of subplots
        List of subplots that will be used to calculate optimal subplot_params.
    pad : float
        Padding between the figure edge and the edges of subplots, as a
        fraction of the font size.
    h_pad, w_pad : float
        Padding (height/width) between edges of adjacent subplots, as a
        fraction of the font size.  Defaults to *pad*.
    rect : Tuple[float, float, float, float]
        [left, bottom, right, top] in normalized (0, 1) figure coordinates.
    """
    rows, cols = nrows_ncols

    font_size_inches = (
        FontProperties(size=rcParams["font.size"]).get_size_in_points() / 72)
    pad_inches = pad * font_size_inches
    if h_pad is not None:
        vpad_inches = h_pad * font_size_inches
    else:
        vpad_inches = pad_inches

    if w_pad is not None:
        hpad_inches = w_pad * font_size_inches
    else:
        hpad_inches = pad_inches

    if len(num1num2_list) != len(subplot_list) or len(subplot_list) == 0:
        raise ValueError

    if rect is None:
        margin_left = margin_bottom = margin_right = margin_top = None
    else:
        margin_left, margin_bottom, _right, _top = rect
        if _right:
            margin_right = 1 - _right
        else:
            margin_right = None
        if _top:
            margin_top = 1 - _top
        else:
            margin_top = None

    vspaces = [[] for i in range((rows + 1) * cols)]
    hspaces = [[] for i in range(rows * (cols + 1))]

    union = Bbox.union

    if ax_bbox_list is None:
        ax_bbox_list = []
        for subplots in subplot_list:
            ax_bbox = union([ax.get_position(original=True)
                             for ax in subplots])
            ax_bbox_list.append(ax_bbox)

    for subplots, ax_bbox, (num1, num2) in zip(subplot_list,
                                               ax_bbox_list,
                                               num1num2_list):
        if all(not ax.get_visible() for ax in subplots):
            continue

        tight_bbox_raw = union([ax.get_tightbbox(renderer) for ax in subplots
                                if ax.get_visible()])
        tight_bbox = TransformedBbox(tight_bbox_raw,
                                     fig.transFigure.inverted())

        row1, col1 = divmod(num1, cols)

        if num2 is None:
            # left
            hspaces[row1 * (cols + 1) + col1].append(
                                        _get_left(tight_bbox, ax_bbox))
            # right
            hspaces[row1 * (cols + 1) + (col1 + 1)].append(
                                        _get_right(tight_bbox, ax_bbox))
            # top
            vspaces[row1 * cols + col1].append(
                                        _get_top(tight_bbox, ax_bbox))
            # bottom
            vspaces[(row1 + 1) * cols + col1].append(
                                        _get_bottom(tight_bbox, ax_bbox))

        else:
            row2, col2 = divmod(num2, cols)

            for row_i in range(row1, row2 + 1):
                # left
                hspaces[row_i * (cols + 1) + col1].append(
                                    _get_left(tight_bbox, ax_bbox))
                # right
                hspaces[row_i * (cols + 1) + (col2 + 1)].append(
                                    _get_right(tight_bbox, ax_bbox))
            for col_i in range(col1, col2 + 1):
                # top
                vspaces[row1 * cols + col_i].append(
                                    _get_top(tight_bbox, ax_bbox))
                # bottom
                vspaces[(row2 + 1) * cols + col_i].append(
                                    _get_bottom(tight_bbox, ax_bbox))

    fig_width_inch, fig_height_inch = fig.get_size_inches()

    # margins can be negative for axes with aspect applied. And we
    # append + [0] to make minimum margins 0

    if not margin_left:
        margin_left = max([sum(s) for s in hspaces[::cols + 1]] + [0])
        margin_left += pad_inches / fig_width_inch

    if not margin_right:
        margin_right = max([sum(s) for s in hspaces[cols::cols + 1]] + [0])
        margin_right += pad_inches / fig_width_inch

    if not margin_top:
        margin_top = max([sum(s) for s in vspaces[:cols]] + [0])
        margin_top += pad_inches / fig_height_inch

    if not margin_bottom:
        margin_bottom = max([sum(s) for s in vspaces[-cols:]] + [0])
        margin_bottom += pad_inches / fig_height_inch

    if margin_left + margin_right >= 1:
        cbook._warn_external('Tight layout not applied. The left and right '
                             'margins cannot be made large enough to '
                             'accommodate all axes decorations. ')
        return None
    if margin_bottom + margin_top >= 1:
        cbook._warn_external('Tight layout not applied. The bottom and top '
                             'margins cannot be made large enough to '
                             'accommodate all axes decorations. ')
        return None

    kwargs = dict(left=margin_left,
                  right=1 - margin_right,
                  bottom=margin_bottom,
                  top=1 - margin_top)
    if cols > 1:
        hspace = (
            max(sum(s)
                for i in range(rows)
                for s in hspaces[i * (cols + 1) + 1:(i + 1) * (cols + 1) - 1])
            + hpad_inches / fig_width_inch)
        # axes widths:
        h_axes = (1 - margin_right - margin_left - hspace * (cols - 1)) / cols
        if h_axes < 0:
            cbook._warn_external('Tight layout not applied. tight_layout '
                                 'cannot make axes width small enough to '
                                 'accommodate all axes decorations')
            return None
        else:
            kwargs["wspace"] = hspace / h_axes

    if rows > 1:
        vspace = (max(sum(s) for s in vspaces[cols:-cols])
                  + vpad_inches / fig_height_inch)
        v_axes = (1 - margin_top - margin_bottom - vspace * (rows - 1)) / rows
        if v_axes < 0:
            cbook._warn_external('Tight layout not applied. tight_layout '
                                 'cannot make axes height small enough to '
                                 'accommodate all axes decorations')
            return None
        else:
            kwargs["hspace"] = vspace / v_axes

    return kwargs
Ejemplo n.º 52
0
    def _init_legend_box(self, handles, labels, markerfirst=True):
        """
        Initialize the legend_box. The legend_box is an instance of
        the OffsetBox, which is packed with legend handles and
        texts. Once packed, their location is calculated during the
        drawing time.
        """

        fontsize = self._fontsize

        # legend_box is a HPacker, horizontally packed with
        # columns. Each column is a VPacker, vertically packed with
        # legend items. Each legend item is HPacker packed with
        # legend handleBox and labelBox. handleBox is an instance of
        # offsetbox.DrawingArea which contains legend handle. labelBox
        # is an instance of offsetbox.TextArea which contains legend
        # text.

        text_list = []  # the list of text instances
        handle_list = []  # the list of text instances
        handles_and_labels = []

        label_prop = dict(verticalalignment='baseline',
                          horizontalalignment='left',
                          fontproperties=self.prop,
                          )

        # The approximate height and descent of text. These values are
        # only used for plotting the legend handle.
        descent = 0.35 * fontsize * (self.handleheight - 0.7)
        # 0.35 and 0.7 are just heuristic numbers and may need to be improved.
        height = fontsize * self.handleheight - descent
        # each handle needs to be drawn inside a box of (x, y, w, h) =
        # (0, -descent, width, height).  And their coordinates should
        # be given in the display coordinates.

        # The transformation of each handle will be automatically set
        # to self.get_transform(). If the artist does not use its
        # default transform (e.g., Collections), you need to
        # manually set their transform to the self.get_transform().
        legend_handler_map = self.get_legend_handler_map()

        for orig_handle, lab in zip(handles, labels):
            handler = self.get_legend_handler(legend_handler_map, orig_handle)
            if handler is None:
                cbook._warn_external(
                    "Legend does not support {!r} instances.\nA proxy artist "
                    "may be used instead.\nSee: "
                    "https://matplotlib.org/users/legend_guide.html"
                    "#creating-artists-specifically-for-adding-to-the-legend-"
                    "aka-proxy-artists".format(orig_handle))
                # We don't have a handle for this artist, so we just defer
                # to None.
                handle_list.append(None)
            else:
                textbox = TextArea(lab, textprops=label_prop,
                                   multilinebaseline=True,
                                   minimumdescent=True)
                handlebox = DrawingArea(width=self.handlelength * fontsize,
                                        height=height,
                                        xdescent=0., ydescent=descent)

                text_list.append(textbox._text)
                # Create the artist for the legend which represents the
                # original artist/handle.
                handle_list.append(handler.legend_artist(self, orig_handle,
                                                         fontsize, handlebox))
                handles_and_labels.append((handlebox, textbox))

        if handles_and_labels:
            # We calculate number of rows in each column. The first
            # (num_largecol) columns will have (nrows+1) rows, and remaining
            # (num_smallcol) columns will have (nrows) rows.
            ncol = min(self._ncol, len(handles_and_labels))
            nrows, num_largecol = divmod(len(handles_and_labels), ncol)
            num_smallcol = ncol - num_largecol
            # starting index of each column and number of rows in it.
            rows_per_col = [nrows + 1] * num_largecol + [nrows] * num_smallcol
            start_idxs = np.concatenate([[0], np.cumsum(rows_per_col)[:-1]])
            cols = zip(start_idxs, rows_per_col)
        else:
            cols = []

        columnbox = []
        for i0, di in cols:
            # pack handleBox and labelBox into itemBox
            itemBoxes = [HPacker(pad=0,
                                 sep=self.handletextpad * fontsize,
                                 children=[h, t] if markerfirst else [t, h],
                                 align="baseline")
                         for h, t in handles_and_labels[i0:i0 + di]]
            # minimumdescent=False for the text of the last row of the column
            if markerfirst:
                itemBoxes[-1].get_children()[1].set_minimumdescent(False)
            else:
                itemBoxes[-1].get_children()[0].set_minimumdescent(False)

            # pack columnBox
            alignment = "baseline" if markerfirst else "right"
            columnbox.append(VPacker(pad=0,
                                     sep=self.labelspacing * fontsize,
                                     align=alignment,
                                     children=itemBoxes))

        mode = "expand" if self._mode == "expand" else "fixed"
        sep = self.columnspacing * fontsize
        self._legend_handle_box = HPacker(pad=0,
                                          sep=sep, align="baseline",
                                          mode=mode,
                                          children=columnbox)
        self._legend_title_box = TextArea("")
        self._legend_box = VPacker(pad=self.borderpad * fontsize,
                                   sep=self.labelspacing * fontsize,
                                   align="center",
                                   children=[self._legend_title_box,
                                             self._legend_handle_box])
        self._legend_box.set_figure(self.figure)
        self.texts = text_list
        self.legendHandles = handle_list
Ejemplo n.º 53
0
def test_warn_external_frame_embedded_python():
    with patch.object(cbook, "sys") as mock_sys:
        mock_sys._getframe = Mock(return_value=None)
        with pytest.warns(UserWarning, match=r"\Adummy\Z"):
            cbook._warn_external("dummy")
Ejemplo n.º 54
0
def test_warn_external(recwarn):
    cbook._warn_external("oops")
    assert len(recwarn) == 1
    assert recwarn[0].filename == __file__
Ejemplo n.º 55
0
def specgram(x, NFFT=None, Fs=None, detrend=None, window=None,
             noverlap=None, pad_to=None, sides=None, scale_by_freq=None,
             mode=None):
    """
    Compute a spectrogram.

    Compute and plot a spectrogram of data in x.  Data are split into
    NFFT length segments and the spectrum of each section is
    computed.  The windowing function window is applied to each
    segment, and the amount of overlap of each segment is
    specified with noverlap.

    Parameters
    ----------
    x : array_like
        1-D array or sequence.

    %(Spectral)s

    %(PSD)s

    noverlap : int, optional
        The number of points of overlap between blocks.  The default
        value is 128.
    mode : str, optional
        What sort of spectrum to use, default is 'psd'.
            'psd'
                Returns the power spectral density.

            'complex'
                Returns the complex-valued frequency spectrum.

            'magnitude'
                Returns the magnitude spectrum.

            'angle'
                Returns the phase spectrum without unwrapping.

            'phase'
                Returns the phase spectrum with unwrapping.

    Returns
    -------
    spectrum : array_like
        2-D array, columns are the periodograms of successive segments.

    freqs : array_like
        1-D array, frequencies corresponding to the rows in *spectrum*.

    t : array_like
        1-D array, the times corresponding to midpoints of segments
        (i.e the columns in *spectrum*).

    See Also
    --------
    psd : differs in the overlap and in the return values.
    complex_spectrum : similar, but with complex valued frequencies.
    magnitude_spectrum : similar single segment when mode is 'magnitude'.
    angle_spectrum : similar to single segment when mode is 'angle'.
    phase_spectrum : similar to single segment when mode is 'phase'.

    Notes
    -----
    detrend and scale_by_freq only apply when *mode* is set to 'psd'.

    """
    if noverlap is None:
        noverlap = 128  # default in _spectral_helper() is noverlap = 0
    if NFFT is None:
        NFFT = 256  # same default as in _spectral_helper()
    if len(x) <= NFFT:
        cbook._warn_external("Only one segment is calculated since parameter "
                             "NFFT (=%d) >= signal length (=%d)." %
                             (NFFT, len(x)))

    spec, freqs, t = _spectral_helper(x=x, y=None, NFFT=NFFT, Fs=Fs,
                                      detrend_func=detrend, window=window,
                                      noverlap=noverlap, pad_to=pad_to,
                                      sides=sides,
                                      scale_by_freq=scale_by_freq,
                                      mode=mode)

    if mode != 'complex':
        spec = spec.real  # Needed since helper implements generically

    return spec, freqs, t
Ejemplo n.º 56
0
def do_constrained_layout(fig, renderer, h_pad, w_pad,
        hspace=None, wspace=None):

    """
    Do the constrained_layout.  Called at draw time in
     ``figure.constrained_layout()``

    Parameters
    ----------


    fig : Figure
      is the ``figure`` instance to do the layout in.

    renderer : Renderer
      the renderer to use.

     h_pad, w_pad : float
       are in figure-normalized units, and are a padding around the axes
       elements.

     hspace, wspace : float
        are in fractions of the subplot sizes.

    """

    '''  Steps:

    1. get a list of unique gridspecs in this figure.  Each gridspec will be
    constrained separately.
    2. Check for gaps in the gridspecs.  i.e. if not every axes slot in the
    gridspec has been filled.  If empty, add a ghost axis that is made so
    that it cannot be seen (though visible=True).  This is needed to make
    a blank spot in the layout.
    3. Compare the tight_bbox of each axes to its `position`, and assume that
    the difference is the space needed by the elements around the edge of
    the axes (decorations) like the title, ticklabels, x-labels, etc.  This
    can include legends who overspill the axes boundaries.
    4. Constrain gridspec elements to line up:
        a) if colnum0 != colnumC, the two subplotspecs are stacked next to
        each other, with the appropriate order.
        b) if colnum0 == colnumC, line up the left or right side of the
        _poslayoutbox (depending if it is the min or max num that is equal).
        c) do the same for rows...
    5. The above doesn't constrain relative sizes of the _poslayoutboxes at
    all, and indeed zero-size is a solution that the solver often finds more
    convenient than expanding the sizes.  Right now the solution is to compare
    subplotspec sizes (i.e. drowsC and drows0) and constrain the larger
    _poslayoutbox to be larger than the ratio of the sizes.  i.e. if drows0 >
    drowsC,  then ax._poslayoutbox > axc._poslayoutbox * drowsC / drows0. This
    works fine *if* the decorations are similar between the axes.  If the
    larger subplotspec has much larger axes decorations, then the constraint
    above is incorrect.

    We need the greater than in the above, in general, rather than an equals
    sign.  Consider the case of the left column having 2 rows, and the right
    column having 1 row.  We want the top and bottom of the _poslayoutboxes to
    line up. So that means if there are decorations on the left column axes
    they will be smaller than half as large as the right hand axis.

    This can break down if the decoration size for the right hand axis (the
    margins) is very large.  There must be a math way to check for this case.

    '''

    invTransFig = fig.transFigure.inverted().transform_bbox

    # list of unique gridspecs that contain child axes:
    gss = set()
    for ax in fig.axes:
        if hasattr(ax, 'get_subplotspec'):
            gs = ax.get_subplotspec().get_gridspec()
            if gs._layoutbox is not None:
                gss.add(gs)
    if len(gss) == 0:
        cbook._warn_external('There are no gridspecs with layoutboxes. '
                             'Possibly did not call parent GridSpec with the'
                             ' figure= keyword')

    if fig._layoutbox.constrained_layout_called < 1:
        for gs in gss:
            # fill in any empty gridspec slots w/ ghost axes...
            _make_ghost_gridspec_slots(fig, gs)

    for nnn in range(2):
        # do the algorithm twice.  This has to be done because decorators
        # change size after the first re-position (i.e. x/yticklabels get
        # larger/smaller).  This second reposition tends to be much milder,
        # so doing twice makes things work OK.
        for ax in fig.axes:
            _log.debug(ax._layoutbox)
            if ax._layoutbox is not None:
                # make margins for each layout box based on the size of
                # the decorators.
                _make_layout_margins(ax, renderer, h_pad, w_pad)

        # do layout for suptitle.
        if fig._suptitle is not None and fig._suptitle._layoutbox is not None:
            sup = fig._suptitle
            bbox = invTransFig(sup.get_window_extent(renderer=renderer))
            height = bbox.y1 - bbox.y0
            sup._layoutbox.edit_height(height+h_pad)

        # OK, the above lines up ax._poslayoutbox with ax._layoutbox
        # now we need to
        #   1) arrange the subplotspecs.  We do it at this level because
        #      the subplotspecs are meant to contain other dependent axes
        #      like colorbars or legends.
        #   2) line up the right and left side of the ax._poslayoutbox
        #      that have the same subplotspec maxes.

        if fig._layoutbox.constrained_layout_called < 1:
            # arrange the subplotspecs...  This is all done relative to each
            # other.  Some subplotspecs contain axes, and others contain
            # gridspecs the ones that contain gridspecs are a set proportion
            # of their parent gridspec.  The ones that contain axes are
            # not so constrained.
            figlb = fig._layoutbox
            for child in figlb.children:
                if child._is_gridspec_layoutbox():
                    # This routine makes all the subplot spec containers
                    # have the correct arrangement.  It just stacks the
                    # subplot layoutboxes in the correct order...
                    _arrange_subplotspecs(child, hspace=hspace, wspace=wspace)

            for gs in gss:
                _align_spines(fig, gs)

        fig._layoutbox.constrained_layout_called += 1
        fig._layoutbox.update_variables()

        # check if any axes collapsed to zero.  If not, don't change positions:
        if _axes_all_finite_sized(fig):
            # Now set the position of the axes...
            for ax in fig.axes:
                if ax._layoutbox is not None:
                    newpos = ax._poslayoutbox.get_rect()
                    # Now set the new position.
                    # ax.set_position will zero out the layout for
                    # this axis, allowing users to hard-code the position,
                    # so this does the same w/o zeroing layout.
                    ax._set_position(newpos, which='original')
        else:
            cbook._warn_external('constrained_layout not applied.  At least '
                                 'one axes collapsed to zero width or height.')
def do_constrained_layout(fig,
                          renderer,
                          h_pad,
                          w_pad,
                          hspace=None,
                          wspace=None):
    """
    Do the constrained_layout.  Called at draw time in
     ``figure.constrained_layout()``

    Parameters
    ----------


    fig : Figure
      is the ``figure`` instance to do the layout in.

    renderer : Renderer
      the renderer to use.

     h_pad, w_pad : float
       are in figure-normalized units, and are a padding around the axes
       elements.

     hspace, wspace : float
        are in fractions of the subplot sizes.

    """
    '''  Steps:

    1. get a list of unique gridspecs in this figure.  Each gridspec will be
    constrained separately.
    2. Check for gaps in the gridspecs.  i.e. if not every axes slot in the
    gridspec has been filled.  If empty, add a ghost axis that is made so
    that it cannot be seen (though visible=True).  This is needed to make
    a blank spot in the layout.
    3. Compare the tight_bbox of each axes to its `position`, and assume that
    the difference is the space needed by the elements around the edge of
    the axes (decorations) like the title, ticklabels, x-labels, etc.  This
    can include legends who overspill the axes boundaries.
    4. Constrain gridspec elements to line up:
        a) if colnum0 != colnumC, the two subplotspecs are stacked next to
        each other, with the appropriate order.
        b) if colnum0 == colnumC, line up the left or right side of the
        _poslayoutbox (depending if it is the min or max num that is equal).
        c) do the same for rows...
    5. The above doesn't constrain relative sizes of the _poslayoutboxes at
    all, and indeed zero-size is a solution that the solver often finds more
    convenient than expanding the sizes.  Right now the solution is to compare
    subplotspec sizes (i.e. drowsC and drows0) and constrain the larger
    _poslayoutbox to be larger than the ratio of the sizes.  i.e. if drows0 >
    drowsC,  then ax._poslayoutbox > axc._poslayoutbox * drowsC / drows0. This
    works fine *if* the decorations are similar between the axes.  If the
    larger subplotspec has much larger axes decorations, then the constraint
    above is incorrect.

    We need the greater than in the above, in general, rather than an equals
    sign.  Consider the case of the left column having 2 rows, and the right
    column having 1 row.  We want the top and bottom of the _poslayoutboxes to
    line up. So that means if there are decorations on the left column axes
    they will be smaller than half as large as the right hand axis.

    This can break down if the decoration size for the right hand axis (the
    margins) is very large.  There must be a math way to check for this case.

    '''

    invTransFig = fig.transFigure.inverted().transform_bbox

    # list of unique gridspecs that contain child axes:
    gss = set()
    for ax in fig.axes:
        if hasattr(ax, 'get_subplotspec'):
            gs = ax.get_subplotspec().get_gridspec()
            if gs._layoutbox is not None:
                gss.add(gs)
    if len(gss) == 0:
        cbook._warn_external('There are no gridspecs with layoutboxes. '
                             'Possibly did not call parent GridSpec with the'
                             ' figure= keyword')

    if fig._layoutbox.constrained_layout_called < 1:
        for gs in gss:
            # fill in any empty gridspec slots w/ ghost axes...
            _make_ghost_gridspec_slots(fig, gs)

    for nnn in range(2):
        # do the algorithm twice.  This has to be done because decorators
        # change size after the first re-position (i.e. x/yticklabels get
        # larger/smaller).  This second reposition tends to be much milder,
        # so doing twice makes things work OK.
        for ax in fig.axes:
            _log.debug(ax._layoutbox)
            if ax._layoutbox is not None:
                # make margins for each layout box based on the size of
                # the decorators.
                _make_layout_margins(ax, renderer, h_pad, w_pad)

        # do layout for suptitle.
        suptitle = fig._suptitle
        do_suptitle = (suptitle is not None and suptitle._layoutbox is not None
                       and suptitle.get_in_layout())
        if do_suptitle:
            bbox = invTransFig(suptitle.get_window_extent(renderer=renderer))
            height = bbox.y1 - bbox.y0
            if np.isfinite(height):
                # reserve at top of figure include an h_pad above and below
                suptitle._layoutbox.edit_height(height + h_pad * 2)

        # OK, the above lines up ax._poslayoutbox with ax._layoutbox
        # now we need to
        #   1) arrange the subplotspecs.  We do it at this level because
        #      the subplotspecs are meant to contain other dependent axes
        #      like colorbars or legends.
        #   2) line up the right and left side of the ax._poslayoutbox
        #      that have the same subplotspec maxes.

        if fig._layoutbox.constrained_layout_called < 1:
            # arrange the subplotspecs...  This is all done relative to each
            # other.  Some subplotspecs contain axes, and others contain
            # gridspecs the ones that contain gridspecs are a set proportion
            # of their parent gridspec.  The ones that contain axes are
            # not so constrained.
            figlb = fig._layoutbox
            for child in figlb.children:
                if child._is_gridspec_layoutbox():
                    # This routine makes all the subplot spec containers
                    # have the correct arrangement.  It just stacks the
                    # subplot layoutboxes in the correct order...
                    _arrange_subplotspecs(child, hspace=hspace, wspace=wspace)

            for gs in gss:
                _align_spines(fig, gs)

        fig._layoutbox.constrained_layout_called += 1
        fig._layoutbox.update_variables()

        # check if any axes collapsed to zero.  If not, don't change positions:
        if _axes_all_finite_sized(fig):
            # Now set the position of the axes...
            for ax in fig.axes:
                if ax._layoutbox is not None:
                    newpos = ax._poslayoutbox.get_rect()
                    # Now set the new position.
                    # ax.set_position will zero out the layout for
                    # this axis, allowing users to hard-code the position,
                    # so this does the same w/o zeroing layout.
                    ax._set_position(newpos, which='original')
            if do_suptitle:
                newpos = suptitle._layoutbox.get_rect()
                suptitle.set_y(1.0 - h_pad)
            else:
                if suptitle is not None and suptitle._layoutbox is not None:
                    suptitle._layoutbox.edit_height(0)
        else:
            cbook._warn_external('constrained_layout not applied.  At least '
                                 'one axes collapsed to zero width or height.')
Ejemplo n.º 58
0
def atan2(dy, dx):
    if dx == 0 and dy == 0:
        cbook._warn_external("dx and dy are 0")
        return 0
    else:
        return math.atan2(dy, dx)