def test_context_menu_change_axis_scale_is_axis_aware(self):
        fig = plot([self.ws, self.ws1], spectrum_nums=[1, 1], tiled=True)
        mock_canvas = MagicMock(figure=fig)
        fig_manager_mock = MagicMock(canvas=mock_canvas)
        fig_interactor = FigureInteraction(fig_manager_mock)
        scale_types = ("log", "log")

        ax = fig.axes[0]
        ax1 = fig.axes[1]
        current_scale_types = (ax.get_xscale(), ax.get_yscale())
        current_scale_types1 = (ax1.get_xscale(), ax1.get_yscale())
        self.assertEqual(current_scale_types, current_scale_types1)

        fig_interactor._quick_change_axes(scale_types, ax)
        current_scale_types2 = (ax.get_xscale(), ax.get_yscale())
        self.assertNotEqual(current_scale_types2, current_scale_types1)
Пример #2
0
    def test_scale_on_ragged_workspaces_maintained_when_toggling_normalisation(
            self):
        ws = CreateWorkspace(DataX=[1, 2, 3, 4, 2, 4, 6, 8],
                             DataY=[2] * 8,
                             NSpec=2,
                             OutputWorkspace="ragged_ws")
        fig = pcolormesh_from_names([ws])
        mock_canvas = MagicMock(figure=fig)
        fig_manager_mock = MagicMock(canvas=mock_canvas)
        fig_interactor = FigureInteraction(fig_manager_mock)
        fig_interactor._toggle_normalization(fig.axes[0])

        clim = fig.axes[0].images[0].get_clim()
        fig_interactor._toggle_normalization(fig.axes[0])
        self.assertEqual(clim, fig.axes[0].images[0].get_clim())
        self.assertNotEqual((-0.1, 0.1), fig.axes[0].images[0].get_clim())
Пример #3
0
    def test_right_click_gives_context_menu_for_plot_without_fit_enabled(
            self, mocked_figure_type, mocked_qmenu_cls):
        fig_manager = self._create_mock_fig_manager_to_accept_right_click()
        fig_manager.fit_browser.tool = None
        interactor = FigureInteraction(fig_manager)
        mouse_event = self._create_mock_right_click()
        mouse_event.inaxes.get_xlim.return_value = (1, 2)
        mouse_event.inaxes.get_ylim.return_value = (1, 2)
        mouse_event.inaxes.lines = []
        mocked_figure_type.return_value = FigureType.Line

        # Expect a call to QMenu() for the outer menu followed by two more calls
        # for the Axes, Normalization, and Markers menus
        outer_qmenu_call = MagicMock()
        qmenu_call2 = MagicMock()
        qmenu_call3 = MagicMock()
        qmenu_call4 = MagicMock()
        mocked_qmenu_cls.side_effect = [
            outer_qmenu_call, qmenu_call2, qmenu_call3, qmenu_call4
        ]

        with patch('workbench.plotting.figureinteraction.QActionGroup',
                   autospec=True):
            with patch('workbench.plotting.figureinteraction.QAction'):
                with patch.object(interactor.toolbar_manager, 'is_tool_active',
                                  lambda: False):
                    with patch.object(interactor, 'add_error_bars_menu',
                                      MagicMock()):
                        interactor.on_mouse_button_press(mouse_event)
                        self.assertEqual(
                            0, outer_qmenu_call.addSeparator.call_count)
                        self.assertEqual(1, outer_qmenu_call.addAction.
                                         call_count)  # Show/hide legend action
                        expected_qmenu_calls = [
                            call(),
                            call("Axes", outer_qmenu_call),
                            call("Normalization", outer_qmenu_call),
                            call("Markers", outer_qmenu_call)
                        ]
                        self.assertEqual(expected_qmenu_calls,
                                         mocked_qmenu_cls.call_args_list)
                        # 4 actions in Axes submenu
                        self.assertEqual(4, qmenu_call2.addAction.call_count)
                        # 2 actions in Normalization submenu
                        self.assertEqual(2, qmenu_call3.addAction.call_count)
                        # 3 actions in Markers submenu
                        self.assertEqual(3, qmenu_call4.addAction.call_count)
Пример #4
0
    def test_toggle_normalisation_applies_to_all_images_if_one_colorbar(self):
        fig = pcolormesh([self.ws, self.ws])

        mock_canvas = MagicMock(figure=fig)
        fig_manager_mock = MagicMock(canvas=mock_canvas)
        fig_interactor = FigureInteraction(fig_manager_mock)

        # there should be 3 axes, 2 colorplots and 1 colorbar
        self.assertEqual(3, len(fig.axes))
        fig.axes[0].tracked_workspaces.values()
        self.assertTrue(fig.axes[0].tracked_workspaces['ws'][0].is_normalized)
        self.assertTrue(fig.axes[1].tracked_workspaces['ws'][0].is_normalized)

        fig_interactor._toggle_normalization(fig.axes[0])

        self.assertFalse(fig.axes[0].tracked_workspaces['ws'][0].is_normalized)
        self.assertFalse(fig.axes[1].tracked_workspaces['ws'][0].is_normalized)
 def test_construction_registers_handler_for_button_press_event(self):
     fig_manager = MagicMock()
     fig_manager.canvas = MagicMock()
     interactor = FigureInteraction(fig_manager)
     expected_call = [
         call('button_press_event', interactor.on_mouse_button_press),
         call('button_release_event', interactor.on_mouse_button_release),
         call('draw_event', interactor.draw_callback),
         call('motion_notify_event', interactor.motion_event),
         call('resize_event', interactor.mpl_redraw_annotations),
         call('figure_leave_event', interactor.on_leave),
         call('axis_leave_event', interactor.on_leave),
         call('scroll_event', interactor.on_scroll)
     ]
     fig_manager.canvas.mpl_connect.assert_has_calls(expected_call)
     self.assertEqual(len(expected_call),
                      fig_manager.canvas.mpl_connect.call_count)
Пример #6
0
    def test_correct_yunit_label_when_overplotting_after_normaliztion_toggle(
            self):
        fig = plot([self.ws],
                   spectrum_nums=[1],
                   errors=True,
                   plot_kwargs={'distribution': True})
        mock_canvas = MagicMock(figure=fig)
        fig_manager_mock = MagicMock(canvas=mock_canvas)
        fig_interactor = FigureInteraction(fig_manager_mock)

        ax = fig.axes[0]
        fig_interactor._toggle_normalization(ax)
        self.assertEqual("Counts ($\AA$)$^{-1}$", ax.get_ylabel())
        plot([self.ws1],
             spectrum_nums=[1],
             errors=True,
             overplot=True,
             fig=fig)
        self.assertEqual("Counts ($\AA$)$^{-1}$", ax.get_ylabel())
Пример #7
0
    def test_correct_yunit_label_when_overplotting_after_normalization_toggle(self):
        # The earlier version of Matplotlib on RHEL throws an error when performing the second
        # plot in this test, if the lines have errorbars. The error occurred when it attempted
        # to draw an interactive legend. Plotting without errors still fulfills the purpose of this
        # test, so turn them off for old Matplotlib versions.
        errors = True
        if int(matplotlib.__version__[0]) < 2:
            errors = False

        fig = plot([self.ws], spectrum_nums=[1], errors=errors,
                   plot_kwargs={'distribution': True})
        mock_canvas = MagicMock(figure=fig)
        fig_manager_mock = MagicMock(canvas=mock_canvas)
        fig_interactor = FigureInteraction(fig_manager_mock)

        ax = fig.axes[0]
        fig_interactor._toggle_normalization(ax)
        self.assertEqual(r"Counts ($\AA$)$^{-1}$", ax.get_ylabel())
        plot([self.ws1], spectrum_nums=[1], errors=errors, overplot=True, fig=fig)
        self.assertEqual(r"Counts ($\AA$)$^{-1}$", ax.get_ylabel())
Пример #8
0
    def test_toggle_normalisation_on_contour_plot_maintains_contour_line_colour(
            self):
        from mantid.plots.utility import convert_color_to_hex
        ws = CreateWorkspace(DataX=[1, 2, 3, 4, 2, 4, 6, 8],
                             DataY=[2] * 8,
                             NSpec=2,
                             OutputWorkspace="test_ws")
        fig = plot_contour([ws])

        for col in fig.get_axes()[0].collections:
            col.set_color("#ff9900")

        mock_canvas = MagicMock(figure=fig)
        fig_manager_mock = MagicMock(canvas=mock_canvas)
        fig_interactor = FigureInteraction(fig_manager_mock)
        fig_interactor._toggle_normalization(fig.axes[0])

        self.assertTrue(
            all(
                convert_color_to_hex(col.get_edgecolor()[0]) == "#ff9900"
                for col in fig.get_axes()[0].collections))
    def test_right_click_gives_context_menu_for_plot_without_fit_enabled(
            self, mocked_figure_type, mocked_qmenu_cls):
        fig_manager = self._create_mock_fig_manager_to_accept_right_click()
        fig_manager.fit_browser.tool = None
        interactor = FigureInteraction(fig_manager)
        mouse_event = self._create_mock_right_click()
        mocked_figure_type.return_value = FigureType.Line

        # Expect a call to QMenu() for the outer menu followed by a call with the first
        # as its parent to generate the Axes menu.
        qmenu_call1 = MagicMock()
        qmenu_call2 = MagicMock()
        mocked_qmenu_cls.side_effect = [qmenu_call1, qmenu_call2]

        with patch('workbench.plotting.figureinteraction.QActionGroup',
                   autospec=True):
            interactor.on_mouse_button_press(mouse_event)
            self.assertEqual(0, qmenu_call1.addSeparator.call_count)
            self.assertEqual(0, qmenu_call1.addAction.call_count)
            expected_qmenu_calls = [call(), call("Axes", qmenu_call1)]
            self.assertEqual(expected_qmenu_calls,
                             mocked_qmenu_cls.call_args_list)
            self.assertEqual(4, qmenu_call2.addAction.call_count)
Пример #10
0
    def test_right_click_gives_context_menu_for_color_plot(
            self, mocked_figure_type, mocked_qmenu):
        fig_manager = self._create_mock_fig_manager_to_accept_right_click()
        interactor = FigureInteraction(fig_manager)
        mouse_event = self._create_mock_right_click()
        mocked_figure_type.return_value = FigureType.Image

        # Expect a call to QMenu() for the outer menu followed by three more calls
        # for the Axes, Normalization and Colorbar menus
        qmenu_call1 = MagicMock()
        qmenu_call2 = MagicMock()
        qmenu_call3 = MagicMock()
        qmenu_call4 = MagicMock()
        mocked_qmenu.side_effect = [
            qmenu_call1, qmenu_call2, qmenu_call3, qmenu_call4
        ]

        with patch('workbench.plotting.figureinteraction.QActionGroup',
                   autospec=True):
            with patch.object(interactor.toolbar_manager, 'is_tool_active',
                              lambda: False):
                interactor.on_mouse_button_press(mouse_event)
                self.assertEqual(0, qmenu_call1.addAction.call_count)
                expected_qmenu_calls = [
                    call(),
                    call("Axes", qmenu_call1),
                    call("Normalization", qmenu_call1),
                    call("Color bar", qmenu_call1)
                ]
                self.assertEqual(expected_qmenu_calls,
                                 mocked_qmenu.call_args_list)
                # 4 actions in Axes submenu
                self.assertEqual(4, qmenu_call2.addAction.call_count)
                # 2 actions in Normalization submenu
                self.assertEqual(2, qmenu_call3.addAction.call_count)
                # 2 actions in Colorbar submenu
                self.assertEqual(2, qmenu_call4.addAction.call_count)
Пример #11
0
    def _test_toggle_normalization(self, errorbars_on, plot_kwargs):
        fig = plot([self.ws], spectrum_nums=[1], errors=errorbars_on,
                   plot_kwargs=plot_kwargs)
        mock_canvas = MagicMock(figure=fig)
        fig_manager_mock = MagicMock(canvas=mock_canvas)
        fig_interactor = FigureInteraction(fig_manager_mock)

        # Earlier versions of matplotlib do not store the data assciated with a
        # line with high precision and hence we need to set a lower tolerance
        # when making comparisons of this data
        if matplotlib.__version__ < "2":
            decimal_tol = 1
        else:
            decimal_tol = 7

        ax = fig.axes[0]
        fig_interactor._toggle_normalization(ax)
        assert_almost_equal(ax.lines[0].get_xdata(), [15, 25])
        assert_almost_equal(ax.lines[0].get_ydata(), [0.2, 0.3], decimal=decimal_tol)
        self.assertEqual("Counts ($\\AA$)$^{-1}$", ax.get_ylabel())
        fig_interactor._toggle_normalization(ax)
        assert_almost_equal(ax.lines[0].get_xdata(), [15, 25])
        assert_almost_equal(ax.lines[0].get_ydata(), [2, 3], decimal=decimal_tol)
        self.assertEqual("Counts", ax.get_ylabel())
Пример #12
0
 def setUp(self):
     fig_manager = self._create_mock_fig_manager_to_accept_right_click()
     fig_manager.fit_browser.tool = None
     self.interactor = FigureInteraction(fig_manager)
     self.fig, self.ax = plt.subplots()  # type: matplotlib.figure.Figure, MantidAxes
Пример #13
0
    def __init__(self, canvas, num):
        assert QAppThreadCall.is_qapp_thread(
        ), "FigureManagerWorkbench cannot be created outside of the QApplication thread"
        QObject.__init__(self)

        parent, flags = get_window_config()
        self.window = FigureWindow(canvas, parent=parent, window_flags=flags)
        self.window.activated.connect(self._window_activated)
        self.window.closing.connect(canvas.close_event)
        self.window.closing.connect(self.destroy)
        self.window.visibility_changed.connect(self.fig_visibility_changed)

        self.window.setWindowTitle("Figure %d" % num)
        canvas.figure.set_label("Figure %d" % num)

        FigureManagerBase.__init__(self, canvas, num)
        # Give the keyboard focus to the figure instead of the
        # manager; StrongFocus accepts both tab and click to focus and
        # will enable the canvas to process event w/o clicking.
        # ClickFocus only takes the focus is the window has been
        # clicked
        # on. http://qt-project.org/doc/qt-4.8/qt.html#FocusPolicy-enum or
        # http://doc.qt.digia.com/qt/qt.html#FocusPolicy-enum
        canvas.setFocusPolicy(Qt.StrongFocus)
        canvas.setFocus()

        self.window._destroying = False

        # add text label to status bar
        self.statusbar_label = QLabel()
        self.window.statusBar().addWidget(self.statusbar_label)

        self.plot_options_dialog = None
        self.toolbar = self._get_toolbar(canvas, self.window)
        if self.toolbar is not None:
            self.window.addToolBar(self.toolbar)
            self.toolbar.message.connect(self.statusbar_label.setText)
            self.toolbar.sig_grid_toggle_triggered.connect(self.grid_toggle)
            self.toolbar.sig_toggle_fit_triggered.connect(self.fit_toggle)
            self.toolbar.sig_toggle_superplot_triggered.connect(
                self.superplot_toggle)
            self.toolbar.sig_copy_to_clipboard_triggered.connect(
                self.copy_to_clipboard)
            self.toolbar.sig_plot_options_triggered.connect(
                self.launch_plot_options)
            self.toolbar.sig_plot_help_triggered.connect(self.launch_plot_help)
            self.toolbar.sig_generate_plot_script_clipboard_triggered.connect(
                self.generate_plot_script_clipboard)
            self.toolbar.sig_generate_plot_script_file_triggered.connect(
                self.generate_plot_script_file)
            self.toolbar.sig_home_clicked.connect(
                self.set_figure_zoom_to_display_all)
            self.toolbar.sig_waterfall_reverse_order_triggered.connect(
                self.waterfall_reverse_line_order)
            self.toolbar.sig_waterfall_offset_amount_triggered.connect(
                self.launch_waterfall_offset_options)
            self.toolbar.sig_waterfall_fill_area_triggered.connect(
                self.launch_waterfall_fill_area_options)
            self.toolbar.sig_waterfall_conversion.connect(
                self.update_toolbar_waterfall_plot)
            self.toolbar.sig_change_line_collection_colour_triggered.connect(
                self.change_line_collection_colour)
            self.toolbar.setFloatable(False)
            tbs_height = self.toolbar.sizeHint().height()
        else:
            tbs_height = 0

        # resize the main window so it will display the canvas with the
        # requested size:
        cs = canvas.sizeHint()
        sbs = self.window.statusBar().sizeHint()
        self._status_and_tool_height = tbs_height + sbs.height()
        height = cs.height() + self._status_and_tool_height
        self.window.resize(cs.width(), height)

        self.fit_browser = FitPropertyBrowser(
            canvas, ToolbarStateManager(self.toolbar))
        self.fit_browser.closing.connect(self.handle_fit_browser_close)
        self.window.setCentralWidget(canvas)
        self.window.addDockWidget(Qt.LeftDockWidgetArea, self.fit_browser)

        self.superplot = None

        # Need this line to stop the bug where the dock window snaps back to its original size after resizing.
        # 0 argument is arbitrary and has no effect on fit widget size
        # This is a qt bug reported at (https://bugreports.qt.io/browse/QTBUG-65592)
        if QT_VERSION >= LooseVersion("5.6"):
            self.window.resizeDocks([self.fit_browser], [1], Qt.Horizontal)
        self.fit_browser.hide()

        if matplotlib.is_interactive():
            self.window.show()
            canvas.draw_idle()

        def notify_axes_change(fig):
            # This will be called whenever the current axes is changed
            if self.toolbar is not None:
                self.toolbar.update()

        canvas.figure.add_axobserver(notify_axes_change)

        # Register canvas observers
        self._fig_interaction = FigureInteraction(self)
        self._ads_observer = FigureManagerADSObserver(self)

        self.window.raise_()
Пример #14
0
 def test_construction_registers_handler_for_button_press_event(self):
     fig_manager = MagicMock()
     fig_manager.canvas = MagicMock()
     interactor = FigureInteraction(fig_manager)
     fig_manager.canvas.mpl_connect.assert_called_once_with(
         'button_press_event', interactor.on_mouse_button_press)
Пример #15
0
    def __init__(self, canvas, num):
        QObject.__init__(self)
        FigureManagerBase.__init__(self, canvas, num)
        # Patch show/destroy to be thread aware
        self._destroy_orig = self.destroy
        self.destroy = QAppThreadCall(self._destroy_orig)
        self._show_orig = self.show
        self.show = QAppThreadCall(self._show_orig)
        self._window_activated_orig = self._window_activated
        self._window_activated = QAppThreadCall(self._window_activated_orig)
        self.set_window_title_orig = self.set_window_title
        self.set_window_title = QAppThreadCall(self.set_window_title_orig)
        self.fig_visibility_changed_orig = self.fig_visibility_changed
        self.fig_visibility_changed = QAppThreadCall(
            self.fig_visibility_changed_orig)

        self.window = FigureWindow(canvas)
        self.window.activated.connect(self._window_activated)
        self.window.closing.connect(canvas.close_event)
        self.window.closing.connect(self.destroy)
        self.window.visibility_changed.connect(self.fig_visibility_changed)

        self.window.setWindowTitle("Figure %d" % num)
        canvas.figure.set_label("Figure %d" % num)

        # Give the keyboard focus to the figure instead of the
        # manager; StrongFocus accepts both tab and click to focus and
        # will enable the canvas to process event w/o clicking.
        # ClickFocus only takes the focus is the window has been
        # clicked
        # on. http://qt-project.org/doc/qt-4.8/qt.html#FocusPolicy-enum or
        # http://doc.qt.digia.com/qt/qt.html#FocusPolicy-enum
        canvas.setFocusPolicy(Qt.StrongFocus)
        canvas.setFocus()

        self.window._destroying = False

        # add text label to status bar
        self.statusbar_label = QLabel()
        self.window.statusBar().addWidget(self.statusbar_label)

        self.plot_options_dialog = None
        self.toolbar = self._get_toolbar(canvas, self.window)
        if self.toolbar is not None:
            self.window.addToolBar(self.toolbar)
            self.toolbar.message.connect(self.statusbar_label.setText)
            self.toolbar.sig_grid_toggle_triggered.connect(self.grid_toggle)
            self.toolbar.sig_toggle_fit_triggered.connect(self.fit_toggle)
            self.toolbar.sig_plot_options_triggered.connect(
                self.launch_plot_options)
            self.toolbar.sig_generate_plot_script_clipboard_triggered.connect(
                self.generate_plot_script_clipboard)
            self.toolbar.sig_generate_plot_script_file_triggered.connect(
                self.generate_plot_script_file)
            self.toolbar.sig_home_clicked.connect(
                self.set_figure_zoom_to_display_all)
            self.toolbar.sig_waterfall_reverse_order_triggered.connect(
                self.waterfall_reverse_line_order)
            self.toolbar.sig_waterfall_offset_amount_triggered.connect(
                self.launch_waterfall_offset_options)
            self.toolbar.sig_waterfall_fill_area_triggered.connect(
                self.launch_waterfall_fill_area_options)
            self.toolbar.sig_waterfall_conversion.connect(
                self.update_toolbar_waterfall_plot)
            self.toolbar.setFloatable(False)
            tbs_height = self.toolbar.sizeHint().height()
        else:
            tbs_height = 0

        # resize the main window so it will display the canvas with the
        # requested size:
        cs = canvas.sizeHint()
        sbs = self.window.statusBar().sizeHint()
        self._status_and_tool_height = tbs_height + sbs.height()
        height = cs.height() + self._status_and_tool_height
        self.window.resize(cs.width(), height)

        self.fit_browser = FitPropertyBrowser(
            canvas, ToolbarStateManager(self.toolbar))
        self.fit_browser.closing.connect(self.handle_fit_browser_close)
        self.window.setCentralWidget(canvas)
        self.window.addDockWidget(Qt.LeftDockWidgetArea, self.fit_browser)
        self.fit_browser.hide()

        if matplotlib.is_interactive():
            self.window.show()
            canvas.draw_idle()

        def notify_axes_change(fig):
            # This will be called whenever the current axes is changed
            if self.toolbar is not None:
                self.toolbar.update()

        canvas.figure.add_axobserver(notify_axes_change)

        # Register canvas observers
        self._fig_interaction = FigureInteraction(self)
        self._ads_observer = FigureManagerADSObserver(self)

        self.window.raise_()
 def setUp(self):
     fig_manager = self._create_mock_fig_manager_to_accept_right_click()
     fig_manager.fit_browser.tool = None
     self.interactor = FigureInteraction(fig_manager)