Ejemplo n.º 1
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
Ejemplo n.º 2
0
class FigureErrorsManagerTest(unittest.TestCase):
    """
    Test class that covers the interaction of the FigureErrorsManager with plots
    that use the mantid projection and have MantidAxes
    """
    @classmethod
    def setUpClass(cls):
        cls.ws2d_histo = CreateWorkspace(
            DataX=[10, 20, 30, 10, 20, 30, 10, 20, 30],
            DataY=[2, 3, 4, 5, 3, 5],
            DataE=[1, 2, 3, 4, 1, 1],
            NSpec=3,
            Distribution=True,
            UnitX='Wavelength',
            VerticalAxisUnit='DeltaE',
            VerticalAxisValues=[4, 6, 8],
            OutputWorkspace='ws2d_histo')
        # initialises the QApplication
        super(cls, FigureErrorsManagerTest).setUpClass()

    def setUp(self):
        self.fig, self.ax = plt.subplots(subplot_kw={'projection': 'mantid'})

        self.errors_manager = FigureErrorsManager(self.fig.canvas)

    def tearDown(self):
        plt.close('all')
        del self.fig
        del self.ax
        del self.errors_manager

    @plot_things(make_them_errors=False)
    def test_show_all_errors(self):
        # assert plot does not have errors
        self.assertEqual(0, len(self.ax.containers))

        self.errors_manager.toggle_all_errors(self.ax, make_visible=True)

        # check that the errors have been added
        self.assertEqual(2, len(self.ax.containers))

    @plot_things(make_them_errors=True)
    def test_hide_all_errors(self):
        self.assertEqual(2, len(self.ax.containers))
        self.errors_manager.toggle_all_errors(self.ax, make_visible=False)
        # errors still exist
        self.assertEqual(2, len(self.ax.containers))
        # they are just invisible
        self.assertFalse(self.ax.containers[0][2][0].get_visible())

    @plot_things(make_them_errors=True)
    def test_hide_all_errors_retains_legend_properties(self):
        # create a legend with a title
        self.ax.legend(title="Test")

        self.errors_manager.toggle_all_errors(self.ax, make_visible=False)

        # check that the legend still has a title
        self.assertEqual(self.ax.get_legend().get_title().get_text(), "Test")

    @plot_things(make_them_errors=False)
    def test_show_all_errors_retains_legend_properties(self):
        # create a legend with a title
        self.ax.legend(title="Test")

        self.errors_manager.toggle_all_errors(self.ax, make_visible=True)

        # check that the legend still has a title
        self.assertEqual(self.ax.get_legend().get_title().get_text(), "Test")

    def test_curve_has_all_errorbars_on_replot_after_error_every_increase(
            self):
        curve = self.ax.errorbar([0, 1, 2, 4], [0, 1, 2, 4],
                                 yerr=[0.1, 0.2, 0.3, 0.4])
        new_curve = FigureErrorsManager._replot_mpl_curve(
            self.ax, curve, {'errorevery': 2})
        self.assertEqual(2, len(new_curve[2][0].get_segments()))
        new_curve = FigureErrorsManager._replot_mpl_curve(
            self.ax, new_curve, {'errorevery': 1})
        self.assertTrue(hasattr(new_curve, 'errorbar_data'))
        self.assertEqual(4, len(new_curve[2][0].get_segments()))

    def test_show_all_errors_on_waterfall_plot_retains_waterfall(self):
        self.ax.plot([0, 1], [0, 1])
        self.ax.plot([0, 1], [0, 1])
        self.ax.set_waterfall(True)

        self.errors_manager.toggle_all_errors(self.ax, make_visible=True)

        self.assertFalse(
            array_equal(self.ax.get_lines()[0].get_data(),
                        self.ax.get_lines()[1].get_data()))

    def test_hide_all_errors_on_waterfall_plot_retains_waterfall(self):
        self.ax.plot([0, 1], [0, 1])
        self.ax.plot([0, 1], [0, 1])
        self.ax.set_waterfall(True)

        self.errors_manager.toggle_all_errors(self.ax, make_visible=True)
        self.errors_manager.toggle_all_errors(self.ax, make_visible=False)

        self.assertFalse(
            array_equal(self.ax.get_lines()[0].get_data(),
                        self.ax.get_lines()[1].get_data()))

    def test_creation_args_not_accessed_for_non_workspace_plots(self):
        self.ax.plot([1, 2], [1, 2])

        self.errors_manager.replot_curve(self.ax, self.ax.lines[0], {})
        self.assertEqual(0, len(self.ax.creation_args))
Ejemplo n.º 3
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)