class DetectorsPresenterTest(unittest.TestCase):
    def setUp(self):
        self.mock_view = mock.Mock()
        self.mock_view.widgets = {
            'name1': 'GE1',
            'name2': 'GE2',
            'name3': 'GE3'
        }
        self.detector_presenter = DetectorsPresenter(self.mock_view)

    def test_that_all_view_widgets_are_appended_to_detector_list(self):
        self.assertEqual(self.detector_presenter.detectors,
                         list(self.mock_view.widgets.values()))

    def test_that_setStateQuietly_calls_view_setStateQuietly(self):
        name = 'name'
        state = 3
        self.detector_presenter.setStateQuietly(name, state)

        self.mock_view.setStateQuietly.assert_called_with(name, state)
        self.assertEqual(self.mock_view.setStateQuietly.call_count, 1)

    def test_that_get_names_gives_correct_list_of_names(self):
        self.assertEqual(self.detector_presenter.getNames(),
                         self.mock_view.widgets.keys())
 def setUp(self):
     self.mock_view = mock.Mock()
     self.mock_view.widgets = {
         'name1': 'GE1',
         'name2': 'GE2',
         'name3': 'GE3'
     }
     self.detector_presenter = DetectorsPresenter(self.mock_view)
Exemple #3
0
    def __init__(self, parent=None):
        super(ElementalAnalysisGui, self).__init__(parent)
        self.menu = self.menuBar()
        self.menu.addAction("File")
        edit_menu = self.menu.addMenu("Edit")
        edit_menu.addAction("Change Peak Data file", self.select_data_file)
        self.menu.addAction("Binning")
        self.menu.addAction("Normalise")

        self.ptable = PeriodicTablePresenter(PeriodicTableView(),
                                             PeriodicTableModel())
        self.ptable.register_table_lclicked(self.table_left_clicked)
        self.ptable.register_table_rclicked(self.table_right_clicked)

        self.load_widget = LoadPresenter(LoadView(), LoadModel(),
                                         CoLoadModel())

        self.load_widget.on_loading_finished(self.loading_finished)

        self.widget_list = QtGui.QVBoxLayout()

        self.detectors = DetectorsPresenter(DetectorsView())
        for detector in self.detectors.detectors:
            detector.on_checkbox_checked(self.add_plot)
            detector.on_checkbox_unchecked(self.del_plot)
        self.peaks = PeaksPresenter(PeaksView())
        self.peaks.major.on_checkbox_checked(self.major_peaks_checked)
        self.peaks.major.on_checkbox_unchecked(self.major_peaks_unchecked)
        self.peaks.minor.on_checkbox_checked(self.minor_peaks_checked)
        self.peaks.minor.on_checkbox_unchecked(self.minor_peaks_unchecked)
        self.peaks.gamma.on_checkbox_checked(self.gammas_checked)
        self.peaks.gamma.on_checkbox_unchecked(self.gammas_unchecked)
        self.peaks.electron.on_checkbox_checked(self.electrons_checked)
        self.peaks.electron.on_checkbox_unchecked(self.electrons_unchecked)

        self.widget_list.addWidget(self.peaks.view)
        self.widget_list.addWidget(self.detectors.view)
        self.widget_list.addWidget(self.load_widget.view)
        self.plotting = PlotPresenter(PlotView())
        self.plotting.view.setMinimumSize(self.plotting.view.sizeHint())

        self.box = QtGui.QHBoxLayout()
        self.box.addWidget(self.ptable.view)
        self.box.addLayout(self.widget_list)

        self.setCentralWidget(QtGui.QWidget(self))
        self.centralWidget().setLayout(self.box)
        self.setWindowTitle("Elemental Analysis")

        self.element_widgets = {}
        self.element_data = {}
        self.element_lines = {}
        self.gamma_lines = []
        self.gamma_peaks = self.ptable.peak_data["Gammas"]
        self.electron_peaks = self._get_electron_peaks()
        self.electron_lines = []
        self._generate_element_widgets()
        self._generate_element_data()

        self.line_colours = cycle(["r", "g", "b", "c", "m", "y"])
Exemple #4
0
    def __init__(self, parent=None):
        super(ElementalAnalysisGui, self).__init__(parent)
        # set menu
        self.menu = self.menuBar()
        self.menu.addAction("File")
        edit_menu = self.menu.addMenu("Edit")
        edit_menu.addAction("Change Peak Data file", self.select_data_file)
        self.menu.addAction("Binning")
        self.menu.addAction("Normalise")

        # periodic table stuff
        self.ptable = PeriodicTablePresenter(PeriodicTableView(),
                                             PeriodicTableModel())
        self.ptable.register_table_lclicked(self.table_left_clicked)
        self.ptable.register_table_rclicked(self.table_right_clicked)

        # load stuff
        self.load_widget = LoadPresenter(LoadView(), LoadModel(),
                                         CoLoadModel())
        self.load_widget.on_loading_finished(self.loading_finished)

        # detectors
        self.detectors = DetectorsPresenter(DetectorsView())
        for detector in self.detectors.detectors:
            detector.on_checkbox_checked(self.add_plot)
            detector.on_checkbox_unchecked(self.del_plot)

        # peaks boxes
        self.peaks = PeaksPresenter(PeaksView())
        self.peaks.major.setChecked(True)
        self.peaks.major.on_checkbox_checked(self.major_peaks_changed)
        self.peaks.major.on_checkbox_unchecked(self.major_peaks_changed)
        self.peaks.minor.on_checkbox_checked(self.minor_peaks_changed)
        self.peaks.minor.on_checkbox_unchecked(self.minor_peaks_changed)
        self.peaks.gamma.on_checkbox_checked(self.gammas_changed)
        self.peaks.gamma.on_checkbox_unchecked(self.gammas_changed)
        self.peaks.electron.on_checkbox_checked(self.electrons_changed)
        self.peaks.electron.on_checkbox_unchecked(self.electrons_changed)
        self.peaks.set_deselect_elements_slot(self.deselect_elements)

        # Line type boxes
        self.lines = LineSelectorPresenter(LineSelectorView())
        self.lines.total.setChecked(True)
        self.lines.total.on_checkbox_checked(self.line_total_changed)
        self.lines.total.on_checkbox_unchecked(self.line_total_changed)
        self.lines.prompt.on_checkbox_checked(self.line_prompt_changed)
        self.lines.prompt.on_checkbox_unchecked(self.line_prompt_changed)
        self.lines.delayed.on_checkbox_checked(self.line_delayed_changed)
        self.lines.delayed.on_checkbox_unchecked(self.line_delayed_changed)

        # set up
        self.widget_list = QtWidgets.QVBoxLayout()
        self.widget_list.addWidget(self.peaks.view)
        self.widget_list.addWidget(self.lines.view)
        self.widget_list.addWidget(self.detectors.view)
        self.widget_list.addWidget(self.load_widget.view)

        # plotting
        self.plot_window = None
        self.num_colors = len(mpl.rcParams['axes.prop_cycle'])
        self.used_colors = {}

        # layout
        self.box = QtWidgets.QHBoxLayout()
        self.box.addWidget(self.ptable.view)
        self.box.addLayout(self.widget_list)

        self.setCentralWidget(QtWidgets.QWidget(self))
        self.centralWidget().setLayout(self.box)
        self.setWindowTitle("Elemental Analysis")

        self.element_widgets = {}
        self.element_lines = {}
        self.electron_peaks = {}
        self._generate_element_widgets()
Exemple #5
0
class ElementalAnalysisGui(QtWidgets.QMainWindow):
    BLUE = 'C0'
    ORANGE = 'C1'
    GREEN = 'C2'

    def __init__(self, parent=None):
        super(ElementalAnalysisGui, self).__init__(parent)
        # set menu
        self.menu = self.menuBar()
        self.menu.addAction("File")
        edit_menu = self.menu.addMenu("Edit")
        edit_menu.addAction("Change Peak Data file", self.select_data_file)
        self.menu.addAction("Binning")
        self.menu.addAction("Normalise")

        # periodic table stuff
        self.ptable = PeriodicTablePresenter(PeriodicTableView(),
                                             PeriodicTableModel())
        self.ptable.register_table_lclicked(self.table_left_clicked)
        self.ptable.register_table_rclicked(self.table_right_clicked)

        # load stuff
        self.load_widget = LoadPresenter(LoadView(), LoadModel(),
                                         CoLoadModel())
        self.load_widget.on_loading_finished(self.loading_finished)

        # detectors
        self.detectors = DetectorsPresenter(DetectorsView())
        for detector in self.detectors.detectors:
            detector.on_checkbox_checked(self.add_plot)
            detector.on_checkbox_unchecked(self.del_plot)

        # peaks boxes
        self.peaks = PeaksPresenter(PeaksView())
        self.peaks.major.setChecked(True)
        self.peaks.major.on_checkbox_checked(self.major_peaks_changed)
        self.peaks.major.on_checkbox_unchecked(self.major_peaks_changed)
        self.peaks.minor.on_checkbox_checked(self.minor_peaks_changed)
        self.peaks.minor.on_checkbox_unchecked(self.minor_peaks_changed)
        self.peaks.gamma.on_checkbox_checked(self.gammas_changed)
        self.peaks.gamma.on_checkbox_unchecked(self.gammas_changed)
        self.peaks.electron.on_checkbox_checked(self.electrons_changed)
        self.peaks.electron.on_checkbox_unchecked(self.electrons_changed)
        self.peaks.set_deselect_elements_slot(self.deselect_elements)

        # Line type boxes
        self.lines = LineSelectorPresenter(LineSelectorView())
        self.lines.total.setChecked(True)
        self.lines.total.on_checkbox_checked(self.line_total_changed)
        self.lines.total.on_checkbox_unchecked(self.line_total_changed)
        self.lines.prompt.on_checkbox_checked(self.line_prompt_changed)
        self.lines.prompt.on_checkbox_unchecked(self.line_prompt_changed)
        self.lines.delayed.on_checkbox_checked(self.line_delayed_changed)
        self.lines.delayed.on_checkbox_unchecked(self.line_delayed_changed)

        # set up
        self.widget_list = QtWidgets.QVBoxLayout()
        self.widget_list.addWidget(self.peaks.view)
        self.widget_list.addWidget(self.lines.view)
        self.widget_list.addWidget(self.detectors.view)
        self.widget_list.addWidget(self.load_widget.view)

        # plotting
        self.plot_window = None
        self.num_colors = len(mpl.rcParams['axes.prop_cycle'])
        self.used_colors = {}

        # layout
        self.box = QtWidgets.QHBoxLayout()
        self.box.addWidget(self.ptable.view)
        self.box.addLayout(self.widget_list)

        self.setCentralWidget(QtWidgets.QWidget(self))
        self.centralWidget().setLayout(self.box)
        self.setWindowTitle("Elemental Analysis")

        self.element_widgets = {}
        self.element_lines = {}
        self.electron_peaks = {}
        self._generate_element_widgets()

    def get_color(self, element):
        """
        When requesting the colour for a new element, return the first unused colour of the matplotlib
        default colour cycle (i.e. mpl.rcParams['axes.prop_cycle']).
        If all colours are used, return the first among the least used ones.
        That is if C0-4 are all used twice and C5-9 are used once, C5 will be returned.

        When requesting the colour for an element that is already plotted return the colour of that element.
        This prevents the same element from being displayed in different colours in separate plots

        :param element: Chemical symbol of the element that one wants the colour of
        :return: Matplotlib colour string: C0, C1, ..., C9 to be used as plt.plot(..., color='C3')
        """
        if element in self.used_colors:
            return self.used_colors[element]

        occurrences = [
            list(self.used_colors.values()).count('C{}'.format(i))
            for i in range(self.num_colors)
        ]

        color_index = occurrences.index(min(occurrences))

        color = "C{}".format(color_index)
        self.used_colors[element] = color

        return color

    def closeEvent(self, event):
        if self.plot_window is not None:
            self.plot_window.closeEvent(event)
        super(ElementalAnalysisGui, self).closeEvent(event)

    # general functions

    def _gen_label(self, name, x_value_in, element=None):
        if element is None:
            return
        # check x value is a float
        try:
            x_value = float(x_value_in)
        except:
            return
        if name not in self.element_lines[element]:
            self.element_lines[element].append(name)
        # make sure the names are strings and x values are numbers
        return Label(str(name),
                     x_value,
                     False,
                     offset,
                     True,
                     rotation=-90,
                     protected=True)

    def _plot_line(self, name, x_value_in, color, element=None):
        label = self._gen_label(name, x_value_in, element)
        if self.plot_window is None:
            return
        for subplot in self.plotting.get_subplots():
            self._plot_line_once(subplot, x_value_in, label, color)

    def _plot_line_once(self, subplot, x_value, label, color):
        self.plotting.add_vline_and_annotate(subplot, x_value, label, color)

    def _rm_line(self, name):
        if self.plot_window is None:
            return
        for subplot in self.plotting.get_subplots():
            self.plotting.rm_vline_and_annotate(subplot, name)

    # setup element pop up
    def _generate_element_widgets(self):
        self.element_widgets = {}
        for element in self.ptable.peak_data:
            data = self.ptable.element_data(element)
            widget = PeakSelectorPresenter(PeakSelectorView(data, element))
            widget.on_finished(self._update_peak_data)
            self.element_widgets[element] = widget

    # interact with periodic table
    def table_right_clicked(self, item):
        self.element_widgets[item.symbol].view.show()

    def table_left_clicked(self, item):
        if self.ptable.is_selected(item.symbol):
            self._add_element_lines(item.symbol)
        else:
            self._remove_element_lines(item.symbol)

    def _add_element_lines(self, element, data=None):
        if data is None:
            data = self.element_widgets[element].get_checked()
        if element not in self.element_lines:
            self.element_lines[element] = []

        # Select a different color, if all used then use the first
        color = self.get_color(element)

        for name, x_value in data.items():
            try:
                x_value = float(x_value)
            except ValueError:
                continue
            full_name = gen_name(element, name)
            if full_name not in self.element_lines[element]:
                self.element_lines[element].append(full_name)
            self._plot_line(full_name, x_value, color, element)

    def _remove_element_lines(self, element):
        if element not in self.element_lines:
            return
        names = deepcopy(self.element_lines[element])
        for name in names:
            try:
                self.element_lines[element].remove(name)
                self._rm_line(name)
                if not self.element_lines[element]:
                    del self.element_lines[element]
                    if element in self.used_colors:
                        del self.used_colors[element]
            except:
                continue

        if element in self.element_lines and not self.element_lines[element]:
            del self.element_lines[element]

    # loading
    def load_run(self, detector, run):
        name = "{}; Detector {}".format(run, detector[-1])
        if self.plot_window is None:
            self.plot_window = MultiPlotWindow(str(run))
            self.plotting = self.plot_window.multi_plot
            self.add_detector_to_plot(detector, name)
            self.plotting.set_all_values()
            self.plotting.remove_subplot_connection(self.subplot_removed)
            self.plotting.remove_line_connection(self.uncheck_on_removed)
            self.plot_window.show()
            # untick detectors if plot window is closed
            self.plot_window.windowClosedSignal.connect(self._unset_detectors)
        else:
            self.add_detector_to_plot(detector, name)
            self.plot_window.show()
            self.plot_window.raise_()

    def loading_finished(self):
        last_run = self.load_widget.last_loaded_run()
        if last_run is None:
            return
        # check all detectors are loaded
        num_detectors = self.load_widget.get_run_num_loaded_detectors(last_run)
        for j, detector in enumerate(self.detectors.getNames()):
            if j < num_detectors:
                self.detectors.enableDetector(detector)
            else:
                # disable checkbox and uncheck box
                self.detectors.disableDetector(detector)
                self.detectors.setStateQuietly(detector, False)

        to_plot = deepcopy(
            [det.isChecked() for det in self.detectors.detectors])
        if self.plot_window is None and any(to_plot) is False:
            return
        # generate plots - if new run clear old plot(s) and replace it
        if self.plot_window is None:
            for name in self.detectors.getNames():
                self.detectors.setStateQuietly(name, False)
        else:
            for plot in self.plotting.get_subplots():
                self.plotting.remove_subplot(plot)
        for j in range(len(to_plot)):
            if to_plot[j]:
                self.detectors.detectors[j].setChecked(True)

    # detectors
    def add_detector_to_plot(self, detector, name):
        self.plotting.add_subplot(detector)
        ws = mantid.mtd[name]
        ws.setYUnit('Counts')
        # find correct detector number from the workspace group run
        if self.lines.total.isChecked():
            self.plotting.plot(detector,
                               ws.name(),
                               spec_num=spectrum_index["Total"],
                               color=self.BLUE)
        if self.lines.prompt.isChecked():
            self.plotting.plot(detector,
                               ws.name(),
                               spec_num=spectrum_index["Prompt"],
                               color=self.ORANGE)
        if self.lines.delayed.isChecked():
            self.plotting.plot(detector,
                               ws.name(),
                               spec_num=spectrum_index["Delayed"],
                               color=self.GREEN)

        # add current selection of lines
        for element in self.ptable.selection:
            self.add_peak_data(element.symbol, detector)

    def _unset_detectors(self):
        self.plot_window.windowClosedSignal.disconnect()
        self.plot_window = None
        for name in self.detectors.getNames():
            self.detectors.setStateQuietly(name, False)
        self.uncheck_detectors_if_no_line_plotted()

    # plotting
    def add_peak_data(self, element, subplot, data=None):
        # if already selected add to just new plot
        if data is None:
            data = self.element_widgets[element].get_checked()
        color = self.get_color(element)
        for name, x_value in data.items():
            if isinstance(x_value, float):
                full_name = gen_name(element, name)
                label = self._gen_label(full_name, x_value, element)
                self._plot_line_once(subplot, x_value, label, color)

    def _update_peak_data(self, element):
        if self.ptable.is_selected(element):
            # remove all of the lines for the element
            self._remove_element_lines(element)
            # add the ticked lines to the plot
            self._add_element_lines(element)
        else:
            self._remove_element_lines(element)

    def add_plot(self, checkbox):
        detector = checkbox.name
        last_run = self.load_widget.last_loaded_run()
        # not using load_last_run prevents two calls to last_loaded_run()
        if last_run is not None:
            self.load_run(detector, last_run)

    def del_plot(self, checkbox):
        if self.load_widget.last_loaded_run() is not None:
            self.plotting.remove_subplot(checkbox.name)
            if not self.plotting.get_subplots(
            ) and self.plot_window is not None:
                self.plot_window.close()
                self.plot_window = None

    def subplot_removed(self, name):
        # need to change the state without sending signal
        # as the plot has already been removed
        self.detectors.setStateQuietly(name, False)
        if not self.plotting.get_subplots():
            self.plot_window.close()
            self.plot_window = None

    # sets data file for periodic table
    def select_data_file(self):
        old_lines = deepcopy(list(self.element_lines.keys()))

        filename = QtWidgets.QFileDialog.getOpenFileName()
        if isinstance(filename, tuple):
            filename = filename[0]
        filename = str(filename)
        if filename:
            self.ptable.set_peak_datafile(filename)

        try:
            self._generate_element_widgets()
        except ValueError:
            message_box.warning(
                'The file does not contain correctly formatted data, resetting to default data file.'
                'See "https://docs.mantidproject.org/nightly/interfaces/'
                'Muon%20Elemental%20Analysis.html" for more information.')
            self.ptable.set_peak_datafile(None)
            self._generate_element_widgets()

        for element in old_lines:
            if element in self.element_widgets.keys():
                self.ptable.select_element(element)
            else:
                self._remove_element_lines(element)
        self._update_checked_data()

    def _update_checked_data(self):
        self.major_peaks_changed(self.peaks.major)
        self.minor_peaks_changed(self.peaks.minor)
        self.gammas_changed(self.peaks.gamma)
        self.electrons_changed(self.peaks.electron)

    # general checked data
    def checked_data(self, element, selection, state):
        for checkbox in selection:
            checkbox.setChecked(state)
        self._update_peak_data(element)

    def electrons_changed(self, electron_peaks):
        for element, selector in self.element_widgets.items():
            self.checked_data(element, selector.electron_checkboxes,
                              electron_peaks.isChecked())

    def gammas_changed(self, gamma_peaks):
        for element, selector in self.element_widgets.items():
            self.checked_data(element, selector.gamma_checkboxes,
                              gamma_peaks.isChecked())

    def major_peaks_changed(self, major_peaks):
        for element, selector in self.element_widgets.items():
            self.checked_data(element, selector.primary_checkboxes,
                              major_peaks.isChecked())

    def minor_peaks_changed(self, minor_peaks):
        for element, selector in self.element_widgets.items():
            self.checked_data(element, selector.secondary_checkboxes,
                              minor_peaks.isChecked())

    def deselect_elements(self):
        self.peaks.disable_deselect_elements_btn()
        for element in self.element_widgets.keys():
            self.ptable.deselect_element(element)
            self._remove_element_lines(element)
        self.peaks.enable_deselect_elements_btn()

    def add_line_by_type(self, run, _type):
        # Ensure all detectors are enabled
        last_run = self.load_widget.last_loaded_run()
        for i, detector in enumerate(self.detectors.detectors):
            if i < self.load_widget.get_run_num_loaded_detectors(last_run):
                if not detector.isEnabled():
                    detector.setEnabled(True)

        if self.plot_window is None:
            return

        # Plot the correct line type on all open subplots
        if _type == 'Total':
            color = self.BLUE
        elif _type == 'Prompt':
            color = self.ORANGE
        else:
            color = self.GREEN
        for subplot in self.plotting.get_subplots():
            name = '{}; Detector {}'.format(run, subplot[-1])
            ws = mantid.mtd[name]
            self.plotting.plot(subplot,
                               ws.name(),
                               spec_num=spectrum_index[_type],
                               color=color)

    def remove_line_type(self, run, _type):
        if self.plot_window is None:
            self.uncheck_detectors_if_no_line_plotted()
            return

        # Remove the correct line type on all open subplots
        for subplot in self.plotting.get_subplots():
            name = '{}; Detector {}'.format(run, subplot[-1])
            ws = mantid.mtd[name]
            self.plotting.remove_line(subplot,
                                      ws.name(),
                                      spec=spectrum_index[_type])

        # If no line type is selected do not allow plotting
        self.uncheck_detectors_if_no_line_plotted()

    def uncheck_detectors_if_no_line_plotted(self):
        last_run = self.load_widget.last_loaded_run()
        if not any([
                self.lines.total.isChecked(),
                self.lines.prompt.isChecked(),
                self.lines.delayed.isChecked()
        ]):
            for i, detector in enumerate(self.detectors.detectors):
                if i < self.load_widget.get_run_num_loaded_detectors(last_run):
                    detector.setEnabled(False)

    # When removing a line with the remove window uncheck the line here
    def uncheck_on_removed(self, removed_lines):
        if sum([
                1 if detector.isChecked() else 0
                for detector in self.detectors.detectors
        ]) > 1:
            return

        for line in removed_lines:
            if 'Total' in line:
                self.lines.total.blockSignals(True)
                self.lines.total.setChecked(False)
                self.lines.total.blockSignals(False)
            if 'Prompt' in line:
                self.lines.prompt.blockSignals(True)
                self.lines.prompt.setChecked(False)
                self.lines.prompt.blockSignals(False)
            if 'Delayed' in line:
                self.lines.delayed.blockSignals(True)
                self.lines.delayed.setChecked(False)
                self.lines.delayed.blockSignals(False)
        self.uncheck_detectors_if_no_line_plotted()

    # Line total
    def line_total_changed(self, line_total):
        self.lines.total.setChecked(line_total.isChecked())
        if line_total.isChecked():
            self.add_line_by_type(self.load_widget.last_loaded_run(), 'Total')
        else:
            self.remove_line_type(self.load_widget.last_loaded_run(), 'Total')

    # Line prompt
    def line_prompt_changed(self, line_prompt):
        self.lines.prompt.setChecked(line_prompt.isChecked())
        if line_prompt.isChecked():
            self.add_line_by_type(self.load_widget.last_loaded_run(), 'Prompt')
        else:
            self.remove_line_type(self.load_widget.last_loaded_run(), 'Prompt')

    # Line delayed
    def line_delayed_changed(self, line_delayed):
        self.lines.delayed.setChecked(line_delayed.isChecked())
        if line_delayed.isChecked():
            self.add_line_by_type(self.load_widget.last_loaded_run(),
                                  'Delayed')
        else:
            self.remove_line_type(self.load_widget.last_loaded_run(),
                                  'Delayed')
    def __init__(self, parent=None):
        super(ElementalAnalysisGui, self).__init__(parent)
        self.menu = self.menuBar()
        self.menu.addAction("File")
        edit_menu = self.menu.addMenu("Edit")
        edit_menu.addAction("Change Peak Data file", self.select_data_file)
        self.menu.addAction("Binning")
        self.menu.addAction("Normalise")

        self.ptable = PeriodicTablePresenter(
            PeriodicTableView(), PeriodicTableModel())
        self.ptable.register_table_lclicked(self.table_left_clicked)
        self.ptable.register_table_rclicked(self.table_right_clicked)

        self.load_widget = LoadPresenter(
            LoadView(), LoadModel(), CoLoadModel())

        self.load_widget.on_loading_finished(self.loading_finished)

        self.widget_list = QtGui.QVBoxLayout()

        self.detectors = DetectorsPresenter(DetectorsView())
        for detector in self.detectors.detectors:
            detector.on_checkbox_checked(self.add_plot)
            detector.on_checkbox_unchecked(self.del_plot)
        self.peaks = PeaksPresenter(PeaksView())
        self.peaks.major.on_checkbox_checked(self.major_peaks_checked)
        self.peaks.major.on_checkbox_unchecked(self.major_peaks_unchecked)
        self.peaks.minor.on_checkbox_checked(self.minor_peaks_checked)
        self.peaks.minor.on_checkbox_unchecked(self.minor_peaks_unchecked)
        self.peaks.gamma.on_checkbox_checked(self.gammas_checked)
        self.peaks.gamma.on_checkbox_unchecked(self.gammas_unchecked)
        self.peaks.electron.on_checkbox_checked(self.electrons_checked)
        self.peaks.electron.on_checkbox_unchecked(self.electrons_unchecked)

        self.widget_list.addWidget(self.peaks.view)
        self.widget_list.addWidget(self.detectors.view)
        self.widget_list.addWidget(self.load_widget.view)
        self.plotting = PlotPresenter(PlotView())
        self.plotting.view.setMinimumSize(self.plotting.view.sizeHint())

        self.plotting.removeSubplotConnection(self.subplotRemoved)

        self.box = QtGui.QHBoxLayout()
        self.box.addWidget(self.ptable.view)
        self.box.addLayout(self.widget_list)

        self.setCentralWidget(QtGui.QWidget(self))
        self.centralWidget().setLayout(self.box)
        self.setWindowTitle("Elemental Analysis")

        self.element_widgets = {}
        self.element_data = {}
        self.element_lines = {}
        self.gamma_lines = []
        self.gamma_peaks = self.ptable.peak_data["Gammas"]
        self.electron_peaks = self._get_electron_peaks()
        self.electron_lines = []
        self._generate_element_widgets()
        self._generate_element_data()

        self.line_colours = cycle(["r", "g", "b", "c", "m", "y"])
class ElementalAnalysisGui(QtGui.QMainWindow):

    def __init__(self, parent=None):
        super(ElementalAnalysisGui, self).__init__(parent)
        self.menu = self.menuBar()
        self.menu.addAction("File")
        edit_menu = self.menu.addMenu("Edit")
        edit_menu.addAction("Change Peak Data file", self.select_data_file)
        self.menu.addAction("Binning")
        self.menu.addAction("Normalise")

        self.ptable = PeriodicTablePresenter(
            PeriodicTableView(), PeriodicTableModel())
        self.ptable.register_table_lclicked(self.table_left_clicked)
        self.ptable.register_table_rclicked(self.table_right_clicked)

        self.load_widget = LoadPresenter(
            LoadView(), LoadModel(), CoLoadModel())

        self.load_widget.on_loading_finished(self.loading_finished)

        self.widget_list = QtGui.QVBoxLayout()

        self.detectors = DetectorsPresenter(DetectorsView())
        for detector in self.detectors.detectors:
            detector.on_checkbox_checked(self.add_plot)
            detector.on_checkbox_unchecked(self.del_plot)
        self.peaks = PeaksPresenter(PeaksView())
        self.peaks.major.on_checkbox_checked(self.major_peaks_checked)
        self.peaks.major.on_checkbox_unchecked(self.major_peaks_unchecked)
        self.peaks.minor.on_checkbox_checked(self.minor_peaks_checked)
        self.peaks.minor.on_checkbox_unchecked(self.minor_peaks_unchecked)
        self.peaks.gamma.on_checkbox_checked(self.gammas_checked)
        self.peaks.gamma.on_checkbox_unchecked(self.gammas_unchecked)
        self.peaks.electron.on_checkbox_checked(self.electrons_checked)
        self.peaks.electron.on_checkbox_unchecked(self.electrons_unchecked)

        self.widget_list.addWidget(self.peaks.view)
        self.widget_list.addWidget(self.detectors.view)
        self.widget_list.addWidget(self.load_widget.view)
        self.plotting = PlotPresenter(PlotView())
        self.plotting.view.setMinimumSize(self.plotting.view.sizeHint())

        self.plotting.removeSubplotConnection(self.subplotRemoved)

        self.box = QtGui.QHBoxLayout()
        self.box.addWidget(self.ptable.view)
        self.box.addLayout(self.widget_list)

        self.setCentralWidget(QtGui.QWidget(self))
        self.centralWidget().setLayout(self.box)
        self.setWindowTitle("Elemental Analysis")

        self.element_widgets = {}
        self.element_data = {}
        self.element_lines = {}
        self.gamma_lines = []
        self.gamma_peaks = self.ptable.peak_data["Gammas"]
        self.electron_peaks = self._get_electron_peaks()
        self.electron_lines = []
        self._generate_element_widgets()
        self._generate_element_data()

        self.line_colours = cycle(["r", "g", "b", "c", "m", "y"])

    def iterate_over_selectors(self, check_state, primary_checkboxes=True):
        """
        Iterates over element popups.

        :param check_state: True or False - i.e. check boxes or not
        :param primary_checkboxes: True if Primary, False if Secondary
        """
        for element, selector in iteritems(self.element_widgets):
            for checkbox in selector.primary_checkboxes if primary_checkboxes else selector.secondary_checkboxes:
                checkbox.setChecked(check_state)
            selector.finish_selection()

    def major_peaks_checked(self):
        self.iterate_over_selectors(True, primary_checkboxes=True)

    def major_peaks_unchecked(self):
        self.iterate_over_selectors(False, primary_checkboxes=True)
        self.plotting.update_canvas()

    def minor_peaks_checked(self):
        self.iterate_over_selectors(True, primary_checkboxes=False)

    def minor_peaks_unchecked(self):
        self.iterate_over_selectors(False, primary_checkboxes=False)
        self.plotting.update_canvas()

    def _plot_gammas(self, subplot, colour=None):
        if colour is None:
            colour = self.line_colours.next()
        for element, peaks in iteritems(self.gamma_peaks):
            for peak_type, peak in iteritems(peaks):
                if peak is None:
                    continue
                self.gamma_lines.append(
                    subplot.axvline(
                        peak, 0, 1, color=colour))
        self.plotting.update_canvas()

    def _iterate_over_gamma_selectors(self, check_state):
        for element, selector in iteritems(self.element_widgets):
            for checkbox in selector.gamma_checkboxes:
                checkbox.setChecked(check_state)
            selector.finish_selection()

    def gammas_checked(self):
        self._iterate_over_gamma_selectors(True)

    def gammas_unchecked(self):
        self._iterate_over_gamma_selectors(False)
        for line in self.gamma_lines:
            line.remove()
            del line
        self.gamma_lines = []
        self.plotting.update_canvas()

    def _get_electron_peaks(self):
        return self.ptable.peak_data["Electrons"].copy()

    def _plot_electrons(self, subplot, colour=None):
        if colour is None:
            colour = self.line_colours.next()
        for peak, intensity in iteritems(self.electron_peaks):
            # intensity will be used in the future for labelling lines
            self.electron_lines.append(
                subplot.axvline(
                    float(peak), 0, 1, color=colour))
        self.plotting.update_canvas()

    def electrons_checked(self):
        colour = self.line_colours.next()
        for subplot_name, subplot in iteritems(self.plotting.get_subplots()):
            self._plot_electrons(subplot, colour=colour)

    def electrons_unchecked(self):
        for line in self.electron_lines:
            line.remove()
            del line
        self.electron_lines = []
        self.plotting.update_canvas()

    def load_run(self, detector, run):
        name = "{}; Detector {}".format(run, detector[-1])
        subplot = self.plotting.add_subplot(detector)
        subplot.set_title(detector)
        for plot in mantid.mtd[name]:
            self.plotting.plot(detector, plot)
        if self.plotting.view.isHidden():
            self.plotting.view.show()
        if self.peaks.gamma.isChecked():
            self._plot_gammas(subplot)
        if self.peaks.electron.isChecked():
            self._plot_electrons(subplot)
        self.plotting.update_canvas()

    def load_last_run(self, detector):
        self.load_run(detector, self.load_widget.last_loaded_run())

    def loading_finished(self):
        last_run = self.load_widget.last_loaded_run()
        if last_run is None:
            return
        self.plotting.view.setWindowTitle(str(last_run))
        for plot in self.plotting.get_subplots():
            self.plotting.remove_subplot(plot)
        for detector in self.detectors.detectors:
            if detector.isChecked():
                self.load_run(detector.name, last_run)
        for item in self.ptable.selection:
            self._add_element_lines(
                item.symbol, self.element_data[item.symbol])

    def _generate_element_data(self):
        for element in self.ptable.peak_data:
            if element in ["Gammas", "Electrons"]:
                continue
            try:
                self.element_data[element] = self.ptable.peak_data[element]["Primary"].copy(
                )
            except KeyError:
                continue

    def _add_element_line(self, x_value, element, colour="b"):
        if x_value is None:
            return
        for plot_name in self.plotting.get_subplots():
            line = self.plotting.get_subplot(
                plot_name).axvline(x_value, 0, 1, color=colour)
            try:
                self.element_lines[element][x_value].append(line)
            except KeyError:
                self.element_lines[element][x_value] = [line]
        self.plotting.update_canvas()

    def _add_element_lines(self, element, data):
        self.element_lines[element] = {}
        colour = self.line_colours.next()
        for label, x_value in iteritems(data):
            # label will be used in the future for labelling lines
            self._add_element_line(x_value, element, colour=colour)

    def _remove_element_lines(self, element):
        for x_value, lines in iteritems(self.element_lines[element]):
            for line in lines:
                line.remove()
                del line
        self.plotting.update_canvas()
        self.element_lines[element] = {}

    def _update_element_lines(self, element, current_dict, new_dict):
        # can be split up: this section removes lines
        if len(current_dict) > len(new_dict):  # i.e. item removed
            dict_difference = {k: current_dict[k]
                               for k in set(current_dict) - set(new_dict)}
            for label, x_value in iteritems(dict_difference):
                # label will be used in the future for labelling lines
                for line in self.element_lines[element][x_value]:
                    line.remove()
                    del line
                self.element_lines[element][x_value] = []
                del current_dict[label]
            self.plotting.update_canvas()
        # can be split up: this section adds lines
        elif current_dict != new_dict:  # i.e. item added or not closed without changes
            colour = self.line_colours.next()
            dict_difference = {k: new_dict[k]
                               for k in set(new_dict) - set(current_dict)}
            for label, x_value in iteritems(dict_difference):
                # label will be used in the future for labelling lines
                self._add_element_line(x_value, element, colour)
            current_dict.update(dict_difference)

    def _update_peak_data(self, element, data):
        if self.ptable.is_selected(element):
            self._update_element_lines(
                element, self.element_data[element], data)
        else:
            self.element_data[element] = data.copy()

    def _generate_element_widgets(self):
        self.element_widgets = {}
        for element in self.ptable.peak_data:
            if element in ["Gammas", "Electrons"]:
                continue
            data = self.ptable.element_data(element)
            try:
                data["Gammas"] = self.ptable.peak_data["Gammas"][element]
            except KeyError:
                pass
            widget = PeakSelectorPresenter(PeakSelectorView(data, element))
            widget.on_finished(self._update_peak_data)
            self.element_widgets[element] = widget

    def table_left_clicked(self, item):
        if self.ptable.is_selected(item.symbol):
            self._add_element_lines(
                item.symbol, self.element_data[item.symbol])
        else:
            self._remove_element_lines(item.symbol)

    def table_right_clicked(self, item):
        self.element_widgets[item.symbol].view.show()

    def _clear_lines(self, lines):
        for line in lines:
            line.remove()
            del line
        return []

    def _clear_lines_after_data_file_selected(self):
        for element in self.element_lines.keys():
            self._remove_element_lines(element)
        self.electron_lines = self._clear_lines(self.electron_lines)
        self.gamma_lines = self._clear_lines(self.gamma_lines)
        for checkbox in self.peaks.peak_checkboxes:
            checkbox.setChecked(False)

    def select_data_file(self):
        filename = QtGui.QFileDialog.getOpenFileName()
        if isinstance(filename, tuple):
            filename = filename[0]
        filename = str(filename)
        if filename:
            self.ptable.set_peak_datafile(filename)
        self._clear_lines_after_data_file_selected()
        self._generate_element_widgets()
        self._generate_element_data()

    def add_plot(self, checkbox):
        detector = checkbox.name
        last_run = self.load_widget.last_loaded_run()
        # not using load_last_run prevents two calls to last_loaded_run()
        if last_run is not None:
            self.load_run(detector, last_run)
        colour = self.line_colours.next()
        for element in self.ptable.selection:
            for label, x_value in iteritems(self.element_data[element.symbol]):
                # label will be used in the future for labelling lines
                line = self.plotting.get_subplot(detector).axvline(
                    x_value, 1, 0, color=colour)
                try:
                    self.element_lines[element.symbol][x_value].append(line)
                except KeyError:
                    self.element_lines[element.symbol][x_value] = [line]
        self.plotting.update_canvas()

    def del_plot(self, checkbox):
        if self.load_widget.last_loaded_run() is not None:
            self.plotting.remove_subplot(checkbox.name)
            if not self.plotting.get_subplots():
                self.plotting.view.close()

    def subplotRemoved(self, name):
        # need to change the state without sending signal
        # as the plot has already been removed
        self.detectors.setStateQuietly(name, False)
    def __init__(self, parent=None):
        super(ElementalAnalysisGui, self).__init__(parent)
        # set menu
        self.menu = self.menuBar()
        self.menu.addAction("File")
        edit_menu = self.menu.addMenu("Edit")
        edit_menu.addAction("Change Peak Data file", self.select_data_file)
        self.menu.addAction("Binning")
        self.menu.addAction("Normalise")

        # periodic table stuff
        self.ptable = PeriodicTablePresenter(
            PeriodicTableView(), PeriodicTableModel())
        self.ptable.register_table_lclicked(self.table_left_clicked)
        self.ptable.register_table_rclicked(self.table_right_clicked)

        # load stuff
        self.load_widget = LoadPresenter(
            LoadView(), LoadModel(), CoLoadModel())
        self.load_widget.on_loading_finished(self.loading_finished)

        # detectors
        self.detectors = DetectorsPresenter(DetectorsView())
        for detector in self.detectors.detectors:
            detector.on_checkbox_checked(self.add_plot)
            detector.on_checkbox_unchecked(self.del_plot)

        # peaks boxes
        self.peaks = PeaksPresenter(PeaksView())
        self.peaks.major.setChecked(True)
        self.peaks.major.on_checkbox_checked(self.major_peaks_checked)
        self.peaks.major.on_checkbox_unchecked(self.major_peaks_unchecked)
        self.peaks.minor.on_checkbox_checked(self.minor_peaks_checked)
        self.peaks.minor.on_checkbox_unchecked(self.minor_peaks_unchecked)
        self.peaks.gamma.on_checkbox_checked(self.gammas_checked)
        self.peaks.gamma.on_checkbox_unchecked(self.gammas_unchecked)
        self.peaks.electron.on_checkbox_checked(self.electrons_checked)
        self.peaks.electron.on_checkbox_unchecked(self.electrons_unchecked)

        # set up
        self.widget_list = QtWidgets.QVBoxLayout()
        self.widget_list.addWidget(self.peaks.view)
        self.widget_list.addWidget(self.detectors.view)
        self.widget_list.addWidget(self.load_widget.view)

        # plotting
        self.plot_window = None

        # layout
        self.box = QtWidgets.QHBoxLayout()
        self.box.addWidget(self.ptable.view)
        self.box.addLayout(self.widget_list)

        self.setCentralWidget(QtWidgets.QWidget(self))
        self.centralWidget().setLayout(self.box)
        self.setWindowTitle("Elemental Analysis")

        self.element_widgets = {}
        self.element_lines = {}
        self.electron_peaks = self._get_electron_peaks()
        self._generate_element_widgets()
class ElementalAnalysisGui(QtWidgets.QMainWindow):

    def __init__(self, parent=None):
        super(ElementalAnalysisGui, self).__init__(parent)
        # set menu
        self.menu = self.menuBar()
        self.menu.addAction("File")
        edit_menu = self.menu.addMenu("Edit")
        edit_menu.addAction("Change Peak Data file", self.select_data_file)
        self.menu.addAction("Binning")
        self.menu.addAction("Normalise")

        # periodic table stuff
        self.ptable = PeriodicTablePresenter(
            PeriodicTableView(), PeriodicTableModel())
        self.ptable.register_table_lclicked(self.table_left_clicked)
        self.ptable.register_table_rclicked(self.table_right_clicked)

        # load stuff
        self.load_widget = LoadPresenter(
            LoadView(), LoadModel(), CoLoadModel())
        self.load_widget.on_loading_finished(self.loading_finished)

        # detectors
        self.detectors = DetectorsPresenter(DetectorsView())
        for detector in self.detectors.detectors:
            detector.on_checkbox_checked(self.add_plot)
            detector.on_checkbox_unchecked(self.del_plot)

        # peaks boxes
        self.peaks = PeaksPresenter(PeaksView())
        self.peaks.major.setChecked(True)
        self.peaks.major.on_checkbox_checked(self.major_peaks_checked)
        self.peaks.major.on_checkbox_unchecked(self.major_peaks_unchecked)
        self.peaks.minor.on_checkbox_checked(self.minor_peaks_checked)
        self.peaks.minor.on_checkbox_unchecked(self.minor_peaks_unchecked)
        self.peaks.gamma.on_checkbox_checked(self.gammas_checked)
        self.peaks.gamma.on_checkbox_unchecked(self.gammas_unchecked)
        self.peaks.electron.on_checkbox_checked(self.electrons_checked)
        self.peaks.electron.on_checkbox_unchecked(self.electrons_unchecked)

        # set up
        self.widget_list = QtWidgets.QVBoxLayout()
        self.widget_list.addWidget(self.peaks.view)
        self.widget_list.addWidget(self.detectors.view)
        self.widget_list.addWidget(self.load_widget.view)

        # plotting
        self.plot_window = None

        # layout
        self.box = QtWidgets.QHBoxLayout()
        self.box.addWidget(self.ptable.view)
        self.box.addLayout(self.widget_list)

        self.setCentralWidget(QtWidgets.QWidget(self))
        self.centralWidget().setLayout(self.box)
        self.setWindowTitle("Elemental Analysis")

        self.element_widgets = {}
        self.element_lines = {}
        self.electron_peaks = self._get_electron_peaks()
        self._generate_element_widgets()

    def closeEvent(self, event):
        if self.plot_window is not None:
            self.plot_window.closeEvent(event)
        super(ElementalAnalysisGui, self).closeEvent(event)

   # general functions
    def _gen_label(self, name, x_value_in, element=None):
        if element is None:
            return
        # check x value is a float
        x_value = 0.0
        try:
            x_value = float(x_value_in)
        except:
            return
        if name not in self.element_lines[element]:
            self.element_lines[element].append(name)
        # make sure the names are strings and x values are numbers
        return Label(str(name), float(x_value), False, offset, True, rotation=-90, protected=True)

    def _plot_line(self, name, x_value_in, element=None):
        label = self._gen_label(name, x_value_in, element)
        if self.plot_window is None:
            return
        for subplot in self.plotting.get_subplots():
            self.plotting.add_vline_and_annotate(
                subplot, float(x_value_in), label)

    def _plot_line_once(self, subplot, x_value, label):
        self.plotting.add_vline_and_annotate(subplot, float(x_value), label)

    def _rm_line(self, name):
        if self.plot_window is None:
            return
        for subplot in self.plotting.get_subplots():
            self.plotting.rm_vline_and_annotate(subplot, name)

    # setup element pop up
    def _generate_element_widgets(self):
        self.element_widgets = {}
        for element in self.ptable.peak_data:
            if element in ["Gammas", "Electrons"]:
                continue
            data = self.ptable.element_data(element)
            try:
                data["Gammas"] = self.ptable.peak_data["Gammas"][element]
            except KeyError:
                pass
            widget = PeakSelectorPresenter(PeakSelectorView(data, element))
            widget.on_finished(self._update_peak_data)
            self.element_widgets[element] = widget

    # interact with periodic table
    def table_right_clicked(self, item):
        self.element_widgets[item.symbol].view.show()

    def table_left_clicked(self, item):
        if self.ptable.is_selected(item.symbol):
            self._add_element_lines(
                item.symbol)
        else:
            self._remove_element_lines(item.symbol)

    def _add_element_lines(self, element, data=None):
        if data is None:
            data = self.element_widgets[element].get_checked()
        if element not in self.element_lines:
            self.element_lines[element] = []
        for name, x_value in iteritems(data):
            full_name = gen_name(element, name)
            self._plot_line(full_name, x_value, element)

    def _remove_element_lines(self, element):
        if element not in self.element_lines:
            return
        names = deepcopy(self.element_lines[element])
        for name in names:
            try:
                self.element_lines[element].remove(name)
                self._rm_line(name)
            except:
                continue

    # loading
    def load_run(self, detector, run):
        name = "{}; Detector {}".format(run, detector[-1])
        if self.plot_window is None:
            self.plot_window = MultiPlotWindow(str(run))
            self.plotting = self.plot_window.multi_plot
            self.add_detector_to_plot(detector, name)
            self.plotting.set_all_values()
            self.plotting.removeSubplotConnection(self.subplotRemoved)
            self.plot_window.show()
            # untick detectors if plot window is closed
            self.plot_window.windowClosedSignal.connect(self._unset_detectors)
        else:
            self.add_detector_to_plot(detector, name)
            self.plot_window.show()
            self.plot_window.raise_()

    def loading_finished(self):
        last_run = self.load_widget.last_loaded_run()
        if last_run is None:
            return
        to_plot = deepcopy([det.isChecked()
                           for det in self.detectors.detectors])
        if self.plot_window is None and any(to_plot) is False:
            return
        # generate plots - if new run clear old plot(s) and replace it
        if self.plot_window is None:
            for name in self.detectors.getNames():
                self.detectors.setStateQuietly(name, False)
        else:
            for plot in self.plotting.get_subplots():
                self.plotting.remove_subplot(plot)
        for j in range(len(to_plot)):
            if to_plot[j]:
                self.detectors.detectors[j].setChecked(True)

    # detectors
    def add_detector_to_plot(self, detector, name):
        self.plotting.add_subplot(detector)
        for ws in mantid.mtd[name]:
            self.plotting.plot(detector, ws.getName())
        # add current selection of lines
        for element in self.ptable.selection:
            self.add_peak_data(element.symbol, detector)

    def _unset_detectors(self):
        self.plot_window.windowClosedSignal.disconnect()
        self.plot_window = None
        for name in self.detectors.getNames():
            self.detectors.setStateQuietly(name, False)

    # plotting
    def add_peak_data(self, element, subplot, data=None):
        # if already selected add to just new plot
        if data is None:
            data = self.element_widgets[element].get_checked()
        for name, x_value in iteritems(data):
            full_name = gen_name(element, name)
            label = self._gen_label(full_name, x_value, element)
            self._plot_line_once(subplot, float(x_value), label)

    def _update_peak_data(self, element, data=None):
        if self.ptable.is_selected(element):
            # remove all of the lines for the element
            self._remove_element_lines(element)
            # add the ticked lines to the plot
            self._add_element_lines(element)
        else:
            self._remove_element_lines(element)

    def add_plot(self, checkbox):
        detector = checkbox.name
        last_run = self.load_widget.last_loaded_run()
        # not using load_last_run prevents two calls to last_loaded_run()
        if last_run is not None:
            self.load_run(detector, last_run)
            if self.peaks.electron.isChecked():
                self.add_peak_data("e-", detector, data=self.electron_peaks)

    def del_plot(self, checkbox):
        if self.load_widget.last_loaded_run() is not None:
            self.plotting.remove_subplot(checkbox.name)
            if not self.plotting.get_subplots() and self.plot_window is not None:
                self.plot_window.close()
                self.plot_window = None

    def subplotRemoved(self, name):
        # need to change the state without sending signal
        # as the plot has already been removed
        self.detectors.setStateQuietly(name, False)
        if not self.plotting.get_subplots():
            self.plot_window.close()
            self.plot_window = None

    # sets data file for periodic table
    def select_data_file(self):
        filename = QtWidgets.QFileDialog.getOpenFileName()
        if isinstance(filename, tuple):
            filename = filename[0]
        filename = str(filename)
        if filename:
            self.ptable.set_peak_datafile(filename)
        # these are commneted out as they are a bug
        # see issue 25326
        #self._clear_lines_after_data_file_selected()
        self._generate_element_widgets()
        #self._generate_element_data()

    # general checked data
    def checked_data(self, element, selection, state):
        for checkbox in selection:
            checkbox.setChecked(state)
        self._update_peak_data(
            element,
            self.element_widgets[element].get_checked())

    # electron Peaks
    def _get_electron_peaks(self):
        # make a dict so we can label the peaks
        electron_dict = {}
        electron_peaks = self.ptable.peak_data["Electrons"].copy()
        for peak, intensity in iteritems(electron_peaks):
            electron_dict[str(peak)] = peak
        return electron_dict

    def electrons_checked(self):
        self._add_element_lines("e-", self.electron_peaks)

    def electrons_unchecked(self):
        self._remove_element_lines("e-")

    # gamma Peaks
    def gammas_checked(self):
        for element, selector in iteritems(self.element_widgets):
            self.checked_data(element, selector.gamma_checkboxes, True)

    def gammas_unchecked(self):
        for element, selector in iteritems(self.element_widgets):
            self.checked_data(element, selector.gamma_checkboxes, False)

    # major peaks
    def major_peaks_checked(self):
        for element, selector in iteritems(self.element_widgets):
            self.checked_data(element, selector.primary_checkboxes, True)

    def major_peaks_unchecked(self):
        for element, selector in iteritems(self.element_widgets):
            self.checked_data(element, selector.primary_checkboxes, False)

    # minor peaks
    def minor_peaks_checked(self):
        for element, selector in iteritems(self.element_widgets):
            self.checked_data(element, selector.secondary_checkboxes, True)

    def minor_peaks_unchecked(self):
        for element, selector in iteritems(self.element_widgets):
            self.checked_data(element, selector.secondary_checkboxes, False)
Exemple #10
0
class ElementalAnalysisGui(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(ElementalAnalysisGui, self).__init__(parent)
        # set menu
        self.menu = self.menuBar()
        self.menu.addAction("File")
        edit_menu = self.menu.addMenu("Edit")
        edit_menu.addAction("Change Peak Data file", self.select_data_file)
        self.menu.addAction("Binning")
        self.menu.addAction("Normalise")

        # periodic table stuff
        self.ptable = PeriodicTablePresenter(PeriodicTableView(),
                                             PeriodicTableModel())
        self.ptable.register_table_lclicked(self.table_left_clicked)
        self.ptable.register_table_rclicked(self.table_right_clicked)

        # load stuff
        self.load_widget = LoadPresenter(LoadView(), LoadModel(),
                                         CoLoadModel())
        self.load_widget.on_loading_finished(self.loading_finished)

        # detectors
        self.detectors = DetectorsPresenter(DetectorsView())
        for detector in self.detectors.detectors:
            detector.on_checkbox_checked(self.add_plot)
            detector.on_checkbox_unchecked(self.del_plot)

        # peaks boxes
        self.peaks = PeaksPresenter(PeaksView())
        self.peaks.major.setChecked(True)
        self.peaks.major.on_checkbox_checked(self.major_peaks_checked)
        self.peaks.major.on_checkbox_unchecked(self.major_peaks_unchecked)
        self.peaks.minor.on_checkbox_checked(self.minor_peaks_checked)
        self.peaks.minor.on_checkbox_unchecked(self.minor_peaks_unchecked)
        self.peaks.gamma.on_checkbox_checked(self.gammas_checked)
        self.peaks.gamma.on_checkbox_unchecked(self.gammas_unchecked)
        self.peaks.electron.on_checkbox_checked(self.electrons_checked)
        self.peaks.electron.on_checkbox_unchecked(self.electrons_unchecked)

        # set up
        self.widget_list = QtWidgets.QVBoxLayout()
        self.widget_list.addWidget(self.peaks.view)
        self.widget_list.addWidget(self.detectors.view)
        self.widget_list.addWidget(self.load_widget.view)

        # plotting
        self.plot_window = None
        self.colors = ['b', 'g', 'r', 'y', 'c', 'm', 'b']
        self.color_index = 0

        # layout
        self.box = QtWidgets.QHBoxLayout()
        self.box.addWidget(self.ptable.view)
        self.box.addLayout(self.widget_list)

        self.setCentralWidget(QtWidgets.QWidget(self))
        self.centralWidget().setLayout(self.box)
        self.setWindowTitle("Elemental Analysis")

        self.element_widgets = {}
        self.element_lines = {}
        self.electron_peaks = self._get_electron_peaks()
        self._generate_element_widgets()

    # Return an unused colour, if all used then start with the first one
    def get_color(self):
        color = self.colors[self.color_index]
        self.color_index = (self.color_index + 1) % len(self.colors)

        return color

    def closeEvent(self, event):
        if self.plot_window is not None:
            self.plot_window.closeEvent(event)
        super(ElementalAnalysisGui, self).closeEvent(event)

# general functions

    def _gen_label(self, name, x_value_in, element=None):
        if element is None:
            return
        # check x value is a float
        x_value = 0.0
        try:
            x_value = float(x_value_in)
        except:
            return
        if name not in self.element_lines[element]:
            self.element_lines[element].append(name)
        # make sure the names are strings and x values are numbers
        return Label(str(name),
                     float(x_value),
                     False,
                     offset,
                     True,
                     rotation=-90,
                     protected=True)

    def _plot_line(self, name, x_value_in, color, element=None):
        label = self._gen_label(name, x_value_in, element)
        if self.plot_window is None:
            return
        for subplot in self.plotting.get_subplots():
            self._plot_line_once(subplot, float(x_value_in), label, color)

    def _plot_line_once(self, subplot, x_value, label, color):
        self.plotting.add_vline_and_annotate(subplot, float(x_value), label,
                                             color)

    def _rm_line(self, name):
        if self.plot_window is None:
            return
        for subplot in self.plotting.get_subplots():
            self.plotting.rm_vline_and_annotate(subplot, name)

    # setup element pop up
    def _generate_element_widgets(self):
        self.element_widgets = {}
        for element in self.ptable.peak_data:
            if element in ["Gammas", "Electrons"]:
                continue
            data = self.ptable.element_data(element)
            try:
                data["Gammas"] = self.ptable.peak_data["Gammas"][element]
            except KeyError:
                pass
            widget = PeakSelectorPresenter(PeakSelectorView(data, element))
            widget.on_finished(self._update_peak_data)
            self.element_widgets[element] = widget

    # interact with periodic table
    def table_right_clicked(self, item):
        self.element_widgets[item.symbol].view.show()

    def table_left_clicked(self, item):
        if self.ptable.is_selected(item.symbol):
            self._add_element_lines(item.symbol)
        else:
            self._remove_element_lines(item.symbol)

    def _add_element_lines(self, element, data=None):
        if data is None:
            data = self.element_widgets[element].get_checked()
        if element not in self.element_lines:
            self.element_lines[element] = []

        # Select a different color, if all used then use the first
        color = self.get_color()

        for name, x_value in iteritems(data):
            full_name = gen_name(element, name)
            self._plot_line(full_name, x_value, color, element)

    def _remove_element_lines(self, element):
        if element not in self.element_lines:
            return
        names = deepcopy(self.element_lines[element])
        for name in names:
            try:
                self.element_lines[element].remove(name)
                self._rm_line(name)
            except:
                continue

    # loading
    def load_run(self, detector, run):
        name = "{}; Detector {}".format(run, detector[-1])
        if self.plot_window is None:
            self.plot_window = MultiPlotWindow(str(run))
            self.plotting = self.plot_window.multi_plot
            self.add_detector_to_plot(detector, name)
            self.plotting.set_all_values()
            self.plotting.removeSubplotConnection(self.subplotRemoved)
            self.plot_window.show()
            # untick detectors if plot window is closed
            self.plot_window.windowClosedSignal.connect(self._unset_detectors)
        else:
            self.add_detector_to_plot(detector, name)
            self.plot_window.show()
            self.plot_window.raise_()

    def loading_finished(self):
        last_run = self.load_widget.last_loaded_run()
        if last_run is None:
            return
        to_plot = deepcopy(
            [det.isChecked() for det in self.detectors.detectors])
        if self.plot_window is None and any(to_plot) is False:
            return
        # generate plots - if new run clear old plot(s) and replace it
        if self.plot_window is None:
            for name in self.detectors.getNames():
                self.detectors.setStateQuietly(name, False)
        else:
            for plot in self.plotting.get_subplots():
                self.plotting.remove_subplot(plot)
        for j in range(len(to_plot)):
            if to_plot[j]:
                self.detectors.detectors[j].setChecked(True)

    # detectors
    def add_detector_to_plot(self, detector, name):
        self.plotting.add_subplot(detector)
        for ws in mantid.mtd[name]:
            self.plotting.plot(detector, ws.getName())
        # add current selection of lines
        for element in self.ptable.selection:
            self.add_peak_data(element.symbol, detector)

    def _unset_detectors(self):
        self.plot_window.windowClosedSignal.disconnect()
        self.plot_window = None
        for name in self.detectors.getNames():
            self.detectors.setStateQuietly(name, False)

    # plotting
    def add_peak_data(self, element, subplot, data=None):
        # if already selected add to just new plot
        if data is None:
            data = self.element_widgets[element].get_checked()
        color = self.get_color()
        for name, x_value in iteritems(data):
            full_name = gen_name(element, name)
            label = self._gen_label(full_name, x_value, element)
            self._plot_line_once(subplot, float(x_value), label, color)

    def _update_peak_data(self, element, data=None):
        if self.ptable.is_selected(element):
            # remove all of the lines for the element
            self._remove_element_lines(element)
            # add the ticked lines to the plot
            self._add_element_lines(element)
        else:
            self._remove_element_lines(element)

    def add_plot(self, checkbox):
        detector = checkbox.name
        last_run = self.load_widget.last_loaded_run()
        # not using load_last_run prevents two calls to last_loaded_run()
        if last_run is not None:
            self.load_run(detector, last_run)
            if self.peaks.electron.isChecked():
                self.add_peak_data("e-", detector, data=self.electron_peaks)

    def del_plot(self, checkbox):
        if self.load_widget.last_loaded_run() is not None:
            self.plotting.remove_subplot(checkbox.name)
            if not self.plotting.get_subplots(
            ) and self.plot_window is not None:
                self.plot_window.close()
                self.plot_window = None

    def subplotRemoved(self, name):
        # need to change the state without sending signal
        # as the plot has already been removed
        self.detectors.setStateQuietly(name, False)
        if not self.plotting.get_subplots():
            self.plot_window.close()
            self.plot_window = None

    # sets data file for periodic table
    def select_data_file(self):
        filename = QtWidgets.QFileDialog.getOpenFileName()
        if isinstance(filename, tuple):
            filename = filename[0]
        filename = str(filename)
        if filename:
            self.ptable.set_peak_datafile(filename)
        # these are commneted out as they are a bug
        # see issue 25326
        #self._clear_lines_after_data_file_selected()
        self._generate_element_widgets()
        #self._generate_element_data()

    # general checked data
    def checked_data(self, element, selection, state):
        for checkbox in selection:
            checkbox.setChecked(state)
        self._update_peak_data(element,
                               self.element_widgets[element].get_checked())

    # electron Peaks
    def _get_electron_peaks(self):
        # make a dict so we can label the peaks
        electron_dict = {}
        electron_peaks = self.ptable.peak_data["Electrons"].copy()
        for peak, intensity in iteritems(electron_peaks):
            electron_dict[str(peak)] = peak
        return electron_dict

    def electrons_checked(self):
        self._add_element_lines("e-", self.electron_peaks)

    def electrons_unchecked(self):
        self._remove_element_lines("e-")

    # gamma Peaks
    def gammas_checked(self):
        for element, selector in iteritems(self.element_widgets):
            self.checked_data(element, selector.gamma_checkboxes, True)

    def gammas_unchecked(self):
        for element, selector in iteritems(self.element_widgets):
            self.checked_data(element, selector.gamma_checkboxes, False)

    # major peaks
    def major_peaks_checked(self):
        for element, selector in iteritems(self.element_widgets):
            self.checked_data(element, selector.primary_checkboxes, True)

    def major_peaks_unchecked(self):
        for element, selector in iteritems(self.element_widgets):
            self.checked_data(element, selector.primary_checkboxes, False)

    # minor peaks
    def minor_peaks_checked(self):
        for element, selector in iteritems(self.element_widgets):
            self.checked_data(element, selector.secondary_checkboxes, True)

    def minor_peaks_unchecked(self):
        for element, selector in iteritems(self.element_widgets):
            self.checked_data(element, selector.secondary_checkboxes, False)
class ElementalAnalysisGui(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(ElementalAnalysisGui, self).__init__(parent)
        self.menu = self.menuBar()
        self.menu.addAction("File")
        edit_menu = self.menu.addMenu("Edit")
        edit_menu.addAction("Change Peak Data file", self.select_data_file)
        self.menu.addAction("Binning")
        self.menu.addAction("Normalise")

        self.ptable = PeriodicTablePresenter(PeriodicTableView(),
                                             PeriodicTableModel())
        self.ptable.register_table_lclicked(self.table_left_clicked)
        self.ptable.register_table_rclicked(self.table_right_clicked)

        self.load_widget = LoadPresenter(LoadView(), LoadModel(),
                                         CoLoadModel())

        self.load_widget.on_loading_finished(self.loading_finished)

        self.widget_list = QtGui.QVBoxLayout()

        self.detectors = DetectorsPresenter(DetectorsView())
        for detector in self.detectors.detectors:
            detector.on_checkbox_checked(self.add_plot)
            detector.on_checkbox_unchecked(self.del_plot)
        self.peaks = PeaksPresenter(PeaksView())
        self.peaks.major.on_checkbox_checked(self.major_peaks_checked)
        self.peaks.major.on_checkbox_unchecked(self.major_peaks_unchecked)
        self.peaks.minor.on_checkbox_checked(self.minor_peaks_checked)
        self.peaks.minor.on_checkbox_unchecked(self.minor_peaks_unchecked)
        self.peaks.gamma.on_checkbox_checked(self.gammas_checked)
        self.peaks.gamma.on_checkbox_unchecked(self.gammas_unchecked)
        self.peaks.electron.on_checkbox_checked(self.electrons_checked)
        self.peaks.electron.on_checkbox_unchecked(self.electrons_unchecked)

        self.widget_list.addWidget(self.peaks.view)
        self.widget_list.addWidget(self.detectors.view)
        self.widget_list.addWidget(self.load_widget.view)
        self.plotting = PlotPresenter(PlotView())
        self.plotting.view.setMinimumSize(self.plotting.view.sizeHint())

        self.plotting.removeSubplotConnection(self.subplotRemoved)

        self.box = QtGui.QHBoxLayout()
        self.box.addWidget(self.ptable.view)
        self.box.addLayout(self.widget_list)

        self.setCentralWidget(QtGui.QWidget(self))
        self.centralWidget().setLayout(self.box)
        self.setWindowTitle("Elemental Analysis")

        self.element_widgets = {}
        self.element_data = {}
        self.element_lines = {}
        self.gamma_lines = []
        self.gamma_peaks = self.ptable.peak_data["Gammas"]
        self.electron_peaks = self._get_electron_peaks()
        self.electron_lines = []
        self._generate_element_widgets()
        self._generate_element_data()

        self.line_colours = cycle(["r", "g", "b", "c", "m", "y"])

    def iterate_over_selectors(self, check_state, primary_checkboxes=True):
        """
        Iterates over element popups.

        :param check_state: True or False - i.e. check boxes or not
        :param primary_checkboxes: True if Primary, False if Secondary
        """
        for element, selector in iteritems(self.element_widgets):
            for checkbox in selector.primary_checkboxes if primary_checkboxes else selector.secondary_checkboxes:
                checkbox.setChecked(check_state)
            selector.finish_selection()

    def major_peaks_checked(self):
        self.iterate_over_selectors(True, primary_checkboxes=True)

    def major_peaks_unchecked(self):
        self.iterate_over_selectors(False, primary_checkboxes=True)
        self.plotting.update_canvas()

    def minor_peaks_checked(self):
        self.iterate_over_selectors(True, primary_checkboxes=False)

    def minor_peaks_unchecked(self):
        self.iterate_over_selectors(False, primary_checkboxes=False)
        self.plotting.update_canvas()

    def _plot_gammas(self, subplot, colour=None):
        if colour is None:
            colour = self.line_colours.next()
        for element, peaks in iteritems(self.gamma_peaks):
            for peak_type, peak in iteritems(peaks):
                if peak is None:
                    continue
                self.gamma_lines.append(
                    subplot.axvline(peak, 0, 1, color=colour))
        self.plotting.update_canvas()

    def _iterate_over_gamma_selectors(self, check_state):
        for element, selector in iteritems(self.element_widgets):
            for checkbox in selector.gamma_checkboxes:
                checkbox.setChecked(check_state)
            selector.finish_selection()

    def gammas_checked(self):
        self._iterate_over_gamma_selectors(True)

    def gammas_unchecked(self):
        self._iterate_over_gamma_selectors(False)
        for line in self.gamma_lines:
            line.remove()
            del line
        self.gamma_lines = []
        self.plotting.update_canvas()

    def _get_electron_peaks(self):
        return self.ptable.peak_data["Electrons"].copy()

    def _plot_electrons(self, subplot, colour=None):
        if colour is None:
            colour = self.line_colours.next()
        for peak, intensity in iteritems(self.electron_peaks):
            # intensity will be used in the future for labelling lines
            self.electron_lines.append(
                subplot.axvline(float(peak), 0, 1, color=colour))
        self.plotting.update_canvas()

    def electrons_checked(self):
        colour = self.line_colours.next()
        for subplot_name, subplot in iteritems(self.plotting.get_subplots()):
            self._plot_electrons(subplot, colour=colour)

    def electrons_unchecked(self):
        for line in self.electron_lines:
            line.remove()
            del line
        self.electron_lines = []
        self.plotting.update_canvas()

    def load_run(self, detector, run):
        name = "{}; Detector {}".format(run, detector[-1])
        subplot = self.plotting.add_subplot(detector)
        subplot.set_title(detector)
        for plot in mantid.mtd[name]:
            self.plotting.plot(detector, plot)
        if self.plotting.view.isHidden():
            self.plotting.view.show()
        if self.peaks.gamma.isChecked():
            self._plot_gammas(subplot)
        if self.peaks.electron.isChecked():
            self._plot_electrons(subplot)
        self.plotting.update_canvas()

    def load_last_run(self, detector):
        self.load_run(detector, self.load_widget.last_loaded_run())

    def loading_finished(self):
        last_run = self.load_widget.last_loaded_run()
        if last_run is None:
            return
        self.plotting.view.setWindowTitle(str(last_run))
        for plot in self.plotting.get_subplots():
            self.plotting.remove_subplot(plot)
        for detector in self.detectors.detectors:
            if detector.isChecked():
                self.load_run(detector.name, last_run)
        for item in self.ptable.selection:
            self._add_element_lines(item.symbol,
                                    self.element_data[item.symbol])

    def _generate_element_data(self):
        for element in self.ptable.peak_data:
            if element in ["Gammas", "Electrons"]:
                continue
            try:
                self.element_data[element] = self.ptable.peak_data[element][
                    "Primary"].copy()
            except KeyError:
                continue

    def _add_element_line(self, x_value, element, colour="b"):
        if x_value is None:
            return
        for plot_name in self.plotting.get_subplots():
            line = self.plotting.get_subplot(plot_name).axvline(x_value,
                                                                0,
                                                                1,
                                                                color=colour)
            try:
                self.element_lines[element][x_value].append(line)
            except KeyError:
                self.element_lines[element][x_value] = [line]
        self.plotting.update_canvas()

    def _add_element_lines(self, element, data):
        self.element_lines[element] = {}
        colour = self.line_colours.next()
        for label, x_value in iteritems(data):
            # label will be used in the future for labelling lines
            self._add_element_line(x_value, element, colour=colour)

    def _remove_element_lines(self, element):
        for x_value, lines in iteritems(self.element_lines[element]):
            for line in lines:
                line.remove()
                del line
        self.plotting.update_canvas()
        self.element_lines[element] = {}

    def _update_element_lines(self, element, current_dict, new_dict):
        # can be split up: this section removes lines
        if len(current_dict) > len(new_dict):  # i.e. item removed
            dict_difference = {
                k: current_dict[k]
                for k in set(current_dict) - set(new_dict)
            }
            for label, x_value in iteritems(dict_difference):
                # label will be used in the future for labelling lines
                for line in self.element_lines[element][x_value]:
                    line.remove()
                    del line
                self.element_lines[element][x_value] = []
                del current_dict[label]
            self.plotting.update_canvas()
        # can be split up: this section adds lines
        elif current_dict != new_dict:  # i.e. item added or not closed without changes
            colour = self.line_colours.next()
            dict_difference = {
                k: new_dict[k]
                for k in set(new_dict) - set(current_dict)
            }
            for label, x_value in iteritems(dict_difference):
                # label will be used in the future for labelling lines
                self._add_element_line(x_value, element, colour)
            current_dict.update(dict_difference)

    def _update_peak_data(self, element, data):
        if self.ptable.is_selected(element):
            self._update_element_lines(element, self.element_data[element],
                                       data)
        else:
            self.element_data[element] = data.copy()

    def _generate_element_widgets(self):
        self.element_widgets = {}
        for element in self.ptable.peak_data:
            if element in ["Gammas", "Electrons"]:
                continue
            data = self.ptable.element_data(element)
            try:
                data["Gammas"] = self.ptable.peak_data["Gammas"][element]
            except KeyError:
                pass
            widget = PeakSelectorPresenter(PeakSelectorView(data, element))
            widget.on_finished(self._update_peak_data)
            self.element_widgets[element] = widget

    def table_left_clicked(self, item):
        if self.ptable.is_selected(item.symbol):
            self._add_element_lines(item.symbol,
                                    self.element_data[item.symbol])
        else:
            self._remove_element_lines(item.symbol)

    def table_right_clicked(self, item):
        self.element_widgets[item.symbol].view.show()

    def _clear_lines(self, lines):
        for line in lines:
            line.remove()
            del line
        return []

    def _clear_lines_after_data_file_selected(self):
        for element in self.element_lines.keys():
            self._remove_element_lines(element)
        self.electron_lines = self._clear_lines(self.electron_lines)
        self.gamma_lines = self._clear_lines(self.gamma_lines)
        for checkbox in self.peaks.peak_checkboxes:
            checkbox.setChecked(False)

    def select_data_file(self):
        filename = str(QtGui.QFileDialog.getOpenFileName())
        if filename:
            self.ptable.set_peak_datafile(filename)
        self._clear_lines_after_data_file_selected()
        self._generate_element_widgets()
        self._generate_element_data()

    def add_plot(self, checkbox):
        detector = checkbox.name
        last_run = self.load_widget.last_loaded_run()
        # not using load_last_run prevents two calls to last_loaded_run()
        if last_run is not None:
            self.load_run(detector, last_run)
        colour = self.line_colours.next()
        for element in self.ptable.selection:
            for label, x_value in iteritems(self.element_data[element.symbol]):
                # label will be used in the future for labelling lines
                line = self.plotting.get_subplot(detector).axvline(
                    x_value, 1, 0, color=colour)
                try:
                    self.element_lines[element.symbol][x_value].append(line)
                except KeyError:
                    self.element_lines[element.symbol][x_value] = [line]
        self.plotting.update_canvas()

    def del_plot(self, checkbox):
        if self.load_widget.last_loaded_run() is not None:
            self.plotting.remove_subplot(checkbox.name)
            if not self.plotting.get_subplots():
                self.plotting.view.close()

    def subplotRemoved(self, name):
        # need to change the state without sending signal
        # as the plot has already been removed
        self.detectors.setStateQuietly(name, False)