Esempio n. 1
0
    def waterfall_reverse_line_order(self):
        ax = self.canvas.figure.get_axes()[0]
        x, y = ax.waterfall_x_offset, ax.waterfall_y_offset
        fills = datafunctions.get_waterfall_fills(ax)
        ax.update_waterfall(0, 0)

        errorbar_cap_lines = datafunctions.remove_and_return_errorbar_cap_lines(
            ax)

        ax.lines.reverse()
        for cap in errorbar_cap_lines:
            ax.add_line(cap)
        if LooseVersion("3.7") > LooseVersion(
                matplotlib.__version__) >= LooseVersion("3.2"):
            for line_fill in fills:
                if line_fill not in ax.collections:
                    ax.add_collection(line_fill)
        elif LooseVersion(matplotlib.__version__) < LooseVersion("3.2"):
            ax.collections += fills
        else:
            raise NotImplementedError(
                "ArtistList will become an immutable tuple in matplotlib 3.7 and thus, "
                "this code doesn't work anymore.")
        ax.collections.reverse()
        ax.update_waterfall(x, y)

        if ax.get_legend():
            ax.make_legend()

        self.canvas.draw()
Esempio n. 2
0
def errorbar_caps_removed(ax):
    # Error bar caps are considered lines so they are removed before checking the number of lines on the axes so
    # they aren't confused for "actual" lines.
    error_bar_caps = datafunctions.remove_and_return_errorbar_cap_lines(ax)
    yield
    # Re-add error bar caps
    ax.lines += error_bar_caps
Esempio n. 3
0
    def _add_plot_type_option_menu(self, menu, ax):
        # Error bar caps are considered lines so they are removed before checking the number of lines on the axes so
        # they aren't confused for "actual" lines.
        error_bar_caps = datafunctions.remove_and_return_errorbar_cap_lines(ax)

        # Able to change the plot type to waterfall if there is only one axes, it is a MantidAxes, and there is more
        # than one line on the axes.
        if len(ax.get_figure().get_axes()) > 1 or not isinstance(
                ax, MantidAxes) or len(ax.get_lines()) <= 1:
            return

        # Re-add error bar caps
        ax.lines += error_bar_caps

        plot_type_menu = QMenu("Plot Type", menu)
        plot_type_action_group = QActionGroup(plot_type_menu)
        standard = plot_type_menu.addAction(
            "1D", lambda: self._change_plot_type(
                ax, plot_type_action_group.checkedAction()))
        waterfall = plot_type_menu.addAction(
            "Waterfall", lambda: self._change_plot_type(
                ax, plot_type_action_group.checkedAction()))

        for action in [waterfall, standard]:
            plot_type_action_group.addAction(action)
            action.setCheckable(True)

        if ax.is_waterfall():
            waterfall.setChecked(True)
        else:
            standard.setChecked(True)

        menu.addMenu(plot_type_menu)
Esempio n. 4
0
    def _replot_selected_curve(self, plot_kwargs):
        """Replot the selected curve with the given plot kwargs"""
        ax = self.get_selected_ax()
        curve = self.get_selected_curve()

        waterfall = False
        if isinstance(ax, MantidAxes):
            waterfall = ax.is_waterfall()
        check_line_colour = False
        # If the plot is a waterfall plot and the user has set it so the area under each line is filled, and the fill
        # colour for each line is set as the line colour, after the curve is updated we need to check if its colour has
        # changed so the fill can be updated accordingly.
        if waterfall and ax.waterfall_has_fill(
        ) and datafunctions.waterfall_fill_is_line_colour(ax):
            check_line_colour = True

        if isinstance(curve, Line2D):
            curve_index = ax.get_lines().index(curve)
            errorbar = False
        else:
            curve_index = ax.get_lines().index(curve[0])
            errorbar = True

        new_curve = FigureErrorsManager.replot_curve(ax, curve, plot_kwargs)
        self.curve_names_dict[self.view.get_selected_curve_name()] = new_curve

        if isinstance(ax, MantidAxes):
            errorbar_cap_lines = datafunctions.remove_and_return_errorbar_cap_lines(
                ax)
        else:
            errorbar_cap_lines = []

        # When a curve is redrawn it is moved to the back of the list of curves so here it is moved back to its previous
        # position. This is so that the correct offset is applied to the curve if the plot is a waterfall plot, but it
        # also just makes sense for the curve order to remain unchanged.
        ax.lines.insert(curve_index, ax.lines.pop())

        if waterfall:
            if check_line_colour:
                # curve can be either a Line2D or an ErrorContainer and the colour is accessed differently for each.
                if not errorbar:
                    # if the line colour hasn't changed then the fill colour doesn't need to be updated.
                    update_fill = curve.get_color() != new_curve[0].get_color()
                else:
                    update_fill = curve[0].get_color(
                    ) != new_curve[0].get_color()
                datafunctions.convert_single_line_to_waterfall(
                    ax, curve_index, need_to_update_fill=update_fill)
            else:
                # the curve has been reset to its original position so for a waterfall plot it needs to be re-offset.
                datafunctions.convert_single_line_to_waterfall(ax, curve_index)

            datafunctions.set_waterfall_fill_visible(ax, curve_index)

        ax.lines += errorbar_cap_lines
Esempio n. 5
0
    def waterfall_reverse_line_order(self):
        ax = self.canvas.figure.get_axes()[0]
        x, y = ax.waterfall_x_offset, ax.waterfall_y_offset
        fills = datafunctions.get_waterfall_fills(ax)
        ax.update_waterfall(0, 0)

        errorbar_cap_lines = datafunctions.remove_and_return_errorbar_cap_lines(ax)

        ax.lines.reverse()
        ax.lines += errorbar_cap_lines
        ax.collections += fills
        ax.collections.reverse()
        ax.update_waterfall(x, y)

        if ax.get_legend():
            ax.make_legend()
Esempio n. 6
0
def _overplot_waterfall(ax, no_of_lines):
    """
    If overplotting onto a waterfall axes, convert lines to waterfall (add x and y offset) before overplotting.

    :param ax: object of MantidAxes type to overplot onto.
    :param no_of_lines: number of lines to overplot onto input axes.
    """
    for i in range(no_of_lines):
        errorbar_cap_lines = datafunctions.remove_and_return_errorbar_cap_lines(
            ax)
        datafunctions.convert_single_line_to_waterfall(
            ax,
            len(ax.get_lines()) - (i + 1))

        if ax.waterfall_has_fill():
            datafunctions.waterfall_update_fill(ax)

        for cap in errorbar_cap_lines:
            ax.add_line(cap)
Esempio n. 7
0
    def toggle_error_bars_for(cls, ax, curve, make_visible=None):
        # get legend properties
        if ax.legend_:
            legend_props = LegendProperties.from_legend(ax.legend_)
        else:
            legend_props = None

        if isinstance(curve, Line2D):
            curve_index = ax.get_lines().index(curve)
        else:
            curve_index = ax.get_lines().index(curve[0])

        # get all curve properties
        curve_props = CurveProperties.from_curve(curve)
        # and remove the ones that matplotlib doesn't recognise
        plot_kwargs = curve_props.get_plot_kwargs()
        new_curve = cls.replot_curve(ax, curve, plot_kwargs)

        if isinstance(ax, MantidAxes):
            errorbar_cap_lines = datafunctions.remove_and_return_errorbar_cap_lines(
                ax)
        else:
            errorbar_cap_lines = []

        ax.lines.insert(curve_index, ax.lines.pop())

        if isinstance(ax, MantidAxes) and ax.is_waterfall():
            datafunctions.convert_single_line_to_waterfall(ax, curve_index)

        for cap in errorbar_cap_lines:
            ax.add_line(cap)

        # Inverts either the current state of hide_errors
        # or the make_visible kwarg that forces a state:
        # If make visible is True, then hide_errors must be False
        # for the intended effect
        curve_props.hide_errors = not curve_props.hide_errors if make_visible is None else not make_visible

        cls.toggle_errors(new_curve, curve_props)
        cls.update_limits_and_legend(ax, legend_props)
Esempio n. 8
0
def plot(workspaces,
         spectrum_nums=None,
         wksp_indices=None,
         errors=False,
         overplot=False,
         fig=None,
         plot_kwargs=None,
         ax_properties=None,
         window_title=None,
         tiled=False,
         waterfall=False,
         log_name=None,
         log_values=None):
    """
    Create a figure with a single subplot and for each workspace/index add a
    line plot to the new axes. show() is called before returning the figure instance. A legend
    is added.

    :param workspaces: A list of workspace handles or strings
    :param spectrum_nums: A list of spectrum number identifiers (general start from 1)
    :param wksp_indices: A list of workspace indexes (starts from 0)
    :param errors: If true then error bars are added for each plot
    :param overplot: If true then overplot over the current figure if one exists. If an axis object the overplotting
    will be done on the axis passed in
    :param fig: If not None then use this Figure object to plot
    :param plot_kwargs: Arguments that will be passed onto the plot function
    :param ax_properties: A dict of axes properties. E.g. {'yscale': 'log'}
    :param window_title: A string denoting name of the GUI window which holds the graph
    :param tiled: An optional flag controlling whether to do a tiled or overlayed plot
    :param waterfall: An optional flag controlling whether or not to do a waterfall plot
    :param log_name: The optional log being plotted against.
    :param log_values: An optional list of log values to plot against.
    :return: The figure containing the plots
    """
    plot_font = ConfigService.getString('plots.font')
    if plot_font:
        if len(mpl.rcParams['font.family']) > 1:
            mpl.rcParams['font.family'][0] = plot_font
        else:
            mpl.rcParams['font.family'].insert(0, plot_font)

    if plot_kwargs is None:
        plot_kwargs = {}
    _validate_plot_inputs(workspaces, spectrum_nums, wksp_indices, tiled,
                          overplot)
    workspaces = [ws for ws in workspaces if isinstance(ws, MatrixWorkspace)]

    if spectrum_nums is not None:
        kw, nums = 'specNum', spectrum_nums
    else:
        kw, nums = 'wkspIndex', wksp_indices

    _add_default_plot_kwargs_from_settings(plot_kwargs, errors)

    num_axes = len(workspaces) * len(nums) if tiled else 1

    fig, axes = get_plot_fig(overplot, ax_properties, window_title, num_axes,
                             fig)

    # Convert to a MantidAxes if it isn't already. Ignore legend since
    # a new one will be drawn later
    axes = [
        MantidAxes.from_mpl_axes(ax, ignore_artists=[Legend])
        if not isinstance(ax, MantidAxes) else ax for ax in axes
    ]

    assert axes, "No axes are associated with this plot"

    if tiled:
        ws_index = [(ws, index) for ws in workspaces for index in nums]
        for index, ax in enumerate(axes):
            if index < len(ws_index):
                _do_single_plot(ax, [ws_index[index][0]], errors, False,
                                [ws_index[index][1]], kw, plot_kwargs)
            else:
                ax.axis('off')
    else:
        show_title = ("on" == ConfigService.getString(
            "plots.ShowTitle").lower()) and not overplot
        ax = overplot if isinstance(overplot, MantidAxes) else axes[0]
        ax.axis('on')
        _do_single_plot(ax, workspaces, errors, show_title, nums, kw,
                        plot_kwargs, log_name, log_values)

    # Can't have a waterfall plot with only one line.
    if len(nums) * len(workspaces) == 1 and waterfall:
        waterfall = False

    # The plot's initial xlim and ylim are used to offset each curve in a waterfall plot.
    # Need to do this whether the current curve is a waterfall plot or not because it may be converted later.
    if not overplot:
        datafunctions.set_initial_dimensions(ax)

    if waterfall:
        ax.set_waterfall(True)

    if not overplot:
        fig.canvas.set_window_title(figure_title(workspaces, fig.number))
    else:
        if ax.is_waterfall():
            for i in range(len(nums) * len(workspaces)):
                errorbar_cap_lines = datafunctions.remove_and_return_errorbar_cap_lines(
                    ax)
                datafunctions.convert_single_line_to_waterfall(
                    ax,
                    len(ax.get_lines()) - (i + 1))

                if ax.waterfall_has_fill():
                    datafunctions.waterfall_update_fill(ax)

                ax.lines += errorbar_cap_lines

    # update and show figure
    return _update_show_figure(fig)
Esempio n. 9
0
    def _replot_current_curve(self, plot_kwargs):
        """Replot the selected curve with the given plot kwargs"""
        ax = self.get_selected_ax()
        curve = self.get_current_curve()

        waterfall = False
        if isinstance(ax, MantidAxes):
            waterfall = ax.is_waterfall()
        check_line_colour = False
        # If the plot is a waterfall plot and the user has set it so the area under each line is filled, and the fill
        # colour for each line is set as the line colour, after the curve is updated we need to check if its colour has
        # changed so the fill can be updated accordingly.
        if waterfall and ax.waterfall_has_fill() and datafunctions.waterfall_fill_is_line_colour(ax):
            check_line_colour = True

        if isinstance(curve, Line2D):
            curve_index = ax.get_lines().index(curve)
            errorbar = False
        else:
            curve_index = ax.get_lines().index(curve[0])
            errorbar = True

        # When you remove the curve on a waterfall plot, the remaining curves are repositioned so that they are
        # equally spaced apart. However since the curve is being replotted we don't want that to happen, so here
        # the waterfall offsets are set to 0 so the plot appears to be non-waterfall. The offsets are then re-set
        # after the curve is replotted.
        if waterfall:
            x_offset, y_offset = ax.waterfall_x_offset, ax.waterfall_y_offset
            ax.waterfall_x_offset = ax.waterfall_y_offset = 0

        new_curve = FigureErrorsManager.replot_curve(ax, curve, plot_kwargs)
        self.curve_names_dict[self.view.get_current_curve_name()] = new_curve

        if isinstance(ax, MantidAxes):
            errorbar_cap_lines = datafunctions.remove_and_return_errorbar_cap_lines(ax)
        else:
            errorbar_cap_lines = []

        # TODO: Accessing the ax.lines property is deprecated in mpl 3.5. It must be removed by mpl 3.7.
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            # When a curve is redrawn it is moved to the back of the list of curves so here it is moved back to its previous
            # position. This is so that the correct offset is applied to the curve if the plot is a waterfall plot, but it
            # also just makes sense for the curve order to remain unchanged.
            ax.lines.insert(curve_index, ax.lines.pop())

        if waterfall:
            # Set the waterfall offsets to what they were previously.
            ax.waterfall_x_offset, ax.waterfall_y_offset = x_offset, y_offset
            if check_line_colour:
                # curve can be either a Line2D or an ErrorContainer and the colour is accessed differently for each.
                if not errorbar:
                    # if the line colour hasn't changed then the fill colour doesn't need to be updated.
                    update_fill = curve.get_color() != new_curve[0].get_color()
                else:
                    update_fill = curve[0].get_color() != new_curve[0].get_color()
                datafunctions.convert_single_line_to_waterfall(ax, curve_index, need_to_update_fill=update_fill)
            else:
                # the curve has been reset to its original position so for a waterfall plot it needs to be re-offset.
                datafunctions.convert_single_line_to_waterfall(ax, curve_index)

            datafunctions.set_waterfall_fill_visible(ax, curve_index)

        for cap in errorbar_cap_lines:
            ax.add_line(cap)