def __init__(self, view, model, context):
        """
        :param view: A reference to the QWidget object for plotting
        :param model: A refence to a model which contains the plotting logic
        :param context: A reference to the Muon context object
        """
        self._view = view
        self._model = model
        self.context = context
        self._view.on_plot_button_clicked(self.handle_data_updated)
        self._view.on_rebin_options_changed(
            self.handle_use_raw_workspaces_changed)
        self._view.on_plot_type_changed(self.handle_plot_type_changed)

        self.input_workspace_observer = GenericObserver(self.handle_data_updated)
        self.fit_observer = GenericObserver(self.handle_fit_completed)
        self.group_pair_observer = GenericObserver(self.handle_group_pair_to_plot_changed)
        self.plot_type_observer = GenericObserver(self.handle_group_pair_to_plot_changed)
        self.rebin_options_set_observer = GenericObserver(self.handle_rebin_options_set)
        self.plot_type_changed_notifier = GenericObservable()

        self._force_redraw = False
        if self.context._frequency_context:
            for ext in FREQUENCY_EXTENSIONS.keys():
                self._view.addItem(FREQ_PLOT_TYPE+FREQUENCY_EXTENSIONS[ext])
            self._view.addItem(FREQ_PLOT_TYPE+"All")
        self.instrument_observer = GenericObserver(self.handle_instrument_changed)
        self.keep = False
Beispiel #2
0
    def __init__(self,
                 view,
                 model,
                 grouping_table_widget=None,
                 pairing_table_widget=None):
        self._view = view
        self._model = model

        self.grouping_table_widget = grouping_table_widget
        self.pairing_table_widget = pairing_table_widget

        # Synchronize the two tables
        self._view.on_grouping_table_changed(
            self.pairing_table_widget.update_view_from_model)
        self._view.on_pairing_table_changed(
            self.grouping_table_widget.update_view_from_model)

        self._view.set_description_text(self.text_for_description())
        self._view.on_add_pair_requested(self.add_pair_from_grouping_table)
        self._view.on_clear_grouping_button_clicked(self.on_clear_requested)
        self._view.on_load_grouping_button_clicked(
            self.handle_load_grouping_from_file)
        self._view.on_save_grouping_button_clicked(
            self.handle_save_grouping_file)
        self._view.on_default_grouping_button_clicked(
            self.handle_default_grouping_button_clicked)

        # monitors for loaded data changing
        self.loadObserver = GroupingTabPresenter.LoadObserver(self)
        self.instrumentObserver = GroupingTabPresenter.InstrumentObserver(self)

        # notifiers
        self.groupingNotifier = GroupingTabPresenter.GroupingNotifier(self)
        self.grouping_table_widget.on_data_changed(self.group_table_changed)
        self.pairing_table_widget.on_data_changed(self.pair_table_changed)
        self.enable_editing_notifier = GroupingTabPresenter.EnableEditingNotifier(
            self)
        self.disable_editing_notifier = GroupingTabPresenter.DisableEditingNotifier(
            self)
        self.calculation_finished_notifier = GenericObservable()

        self.guessAlphaObserver = GroupingTabPresenter.GuessAlphaObserver(self)
        self.pairing_table_widget.guessAlphaNotifier.add_subscriber(
            self.guessAlphaObserver)
        self.message_observer = GroupingTabPresenter.MessageObserver(self)
        self.gui_variables_observer = GroupingTabPresenter.GuiVariablesChangedObserver(
            self)
        self.enable_observer = GroupingTabPresenter.EnableObserver(self)
        self.disable_observer = GroupingTabPresenter.DisableObserver(self)

        self.update_view_from_model_observer = GenericObserver(
            self.update_view_from_model)
Beispiel #3
0
    def __init__(self, view, load):
        self.view = view
        self.load = load
        self.thread = None
        # set data
        self.getWorkspaceNames()
        # connect
        self.view.maxEntButtonSignal.connect(self.handleMaxEntButton)
        self.view.cancelSignal.connect(self.cancel)

        self.phase_table_observer = GenericObserver(self.update_phase_table_options)
        self.calculation_finished_notifier = GenericObservable()
        self.calculation_started_notifier = GenericObservable()
Beispiel #4
0
 def __init__(self, view, alg, load):
     self.view = view
     self.alg = alg
     self.load = load
     self.thread = None
     # set data
     self.getWorkspaceNames()
     # connect
     self.view.tableClickSignal.connect(self.tableClicked)
     self.view.buttonSignal.connect(self.handleButton)
     self.view.phaseCheckSignal.connect(self.phaseCheck)
     self.calculation_finished_notifier = GenericObservable()
     self.view.setup_raw_checkbox_changed(self.handle_use_raw_data_changed)
    def __init__(self, view, model):
        self._view = view
        self._model = model

        self._view.on_grouppair_selection_changed(
            self.handle_grouppair_selector_changed)

        self._view.on_alpha_changed(self.handle_user_changes_alpha)

        self._view.on_summed_periods_changed(self.handle_periods_changed)
        self._view.on_subtracted_periods_changed(self.handle_periods_changed)

        self.pairAlphaNotifier = HomeGroupingWidgetPresenter.PairAlphaNotifier(
            self)
        self.selected_group_pair_changed_notifier = GenericObservable()
Beispiel #6
0
class MaxEntPresenter(object):

    """
    This class links the MaxEnt model to the GUI
    """

    def __init__(self, view, load):
        self.view = view
        self.load = load
        self.thread = None
        # set data
        self.getWorkspaceNames()
        # connect
        self.view.maxEntButtonSignal.connect(self.handleMaxEntButton)
        self.view.cancelSignal.connect(self.cancel)

        self.phase_table_observer = GenericObserver(self.update_phase_table_options)
        self.calculation_finished_notifier = GenericObservable()
        self.calculation_started_notifier = GenericObservable()

    @property
    def widget(self):
        return self.view

    def runChanged(self):
        self.getWorkspaceNames()

    def clear(self):
        self.view.addItems([])
        self.view.update_phase_table_combo([])

    # functions
    def getWorkspaceNames(self):
        final_options = self.load.getGroupedWorkspaceNames()

        self.view.addItems(final_options)
        start = int(
            math.ceil(math.log(self.load.data_context.num_points) / math.log(2.0)))
        values = [str(2**k) for k in range(start, 21)]
        self.view.addNPoints(values)

    def cancel(self):
        if self.maxent_alg is not None:
            self.maxent_alg.cancel()

    # turn on button
    def activate(self):
        self.view.activateCalculateButton()

    # turn off button
    def deactivate(self):
        self.view.deactivateCalculateButton()

    def createThread(self):
        self.maxent_alg = mantid.AlgorithmManager.create("MuonMaxent")
        calculation_function = functools.partial(self.calculate_maxent, self.maxent_alg)
        self._maxent_calculation_model = ThreadModelWrapper(calculation_function)
        return thread_model.ThreadModel(self._maxent_calculation_model)

    # constructs the inputs for the MaxEnt algorithms
    # then executes them (see maxent_model to see the order
    # of execution
    def handleMaxEntButton(self):
        # put this on its own thread so not to freeze Mantid
        self.thread = self.createThread()
        self.thread.threadWrapperSetUp(self.deactivate, self.handleFinished, self.handle_error)
        self.calculation_started_notifier.notify_subscribers()
        self.thread.start()

    # kills the thread at end of execution
    def handleFinished(self):
        self.activate()
        self.calculation_finished_notifier.notify_subscribers()

    def handle_error(self, error):
        self.activate()
        self.view.warning_popup(error)

    def calculate_maxent(self, alg):
        maxent_parameters = self.get_parameters_for_maxent_calculation()

        maxent_workspace = run_MuonMaxent(maxent_parameters, alg)

        self.add_maxent_workspace_to_ADS(maxent_parameters['InputWorkspace'], maxent_workspace, alg)

    def get_parameters_for_maxent_calculation(self):
        inputs = {}

        inputs['InputWorkspace'] = self.view.input_workspace
        run = [float(re.search('[0-9]+', inputs['InputWorkspace']).group())]

        if self.view.phase_table != 'Construct':
            inputs['InputPhaseTable'] = self.view.phase_table

        if self.load.dead_time_table(run):
            inputs['InputDeadTimeTable'] = self.load.dead_time_table(run)

        inputs['FirstGoodTime'] = self.load.first_good_data(run)

        inputs['LastGoodTime'] = self.load.last_good_data(run)

        inputs['Npts'] = self.view.num_points

        inputs['InnerIterations'] = self.view.inner_iterations

        inputs['OuterIterations'] = self.view.outer_iterations

        inputs['DoublePulse'] = self.view.double_pulse

        inputs['Factor'] = self.view.lagrange_multiplier

        inputs['MaxField'] = self.view.maximum_field

        inputs['DefaultLevel'] = self.view.maximum_entropy_constant

        inputs['FitDeadTime'] = self.view.fit_dead_times

        return inputs

    def update_phase_table_options(self):
        phase_table_list = self.load.phase_context.get_phase_table_list(self.load.data_context.instrument)
        phase_table_list.insert(0, 'Construct')

        self.view.update_phase_table_combo(phase_table_list)

    def add_maxent_workspace_to_ADS(self, input_workspace, maxent_workspace, alg):
        run = re.search('[0-9]+', input_workspace).group()
        base_name = get_maxent_workspace_name(input_workspace)
        directory = get_maxent_workspace_group_name(base_name, self.load.data_context.instrument, self.load.workspace_suffix)

        muon_workspace_wrapper = MuonWorkspaceWrapper(maxent_workspace, directory + base_name)
        muon_workspace_wrapper.show()

        maxent_output_options = self.get_maxent_output_options()
        self.load._frequency_context.add_maxEnt(run, maxent_workspace)
        self.add_optional_outputs_to_ADS(alg, maxent_output_options, base_name, directory)

    def get_maxent_output_options(self):
        output_options = {}

        output_options['OutputPhaseTable'] = self.view.output_phase_table
        output_options['OutputDeadTimeTable'] = self.view.output_dead_times
        output_options['ReconstructedSpectra'] = self.view.output_reconstructed_spectra
        output_options['PhaseConvergenceTable'] = self.view.output_phase_convergence

        return output_options

    def add_optional_outputs_to_ADS(self, alg, output_options, base_name, directory):
        for key in output_options:
            if output_options[key]:
                output = alg.getProperty(key).value
                MuonWorkspaceWrapper(output, directory + base_name + optional_output_suffixes[key]).show()

    def update_view_from_model(self):
        self.getWorkspaceNames()
Beispiel #7
0
class FFTPresenter(object):
    """
    This class links the FFT model to the GUI
    """
    def __init__(self, view, alg, load):
        self.view = view
        self.alg = alg
        self.load = load
        self.thread = None
        # set data
        self.getWorkspaceNames()
        # connect
        self.view.tableClickSignal.connect(self.tableClicked)
        self.view.buttonSignal.connect(self.handleButton)
        self.view.phaseCheckSignal.connect(self.phaseCheck)
        self.calculation_finished_notifier = GenericObservable()
        self.view.setup_raw_checkbox_changed(self.handle_use_raw_data_changed)

    def cancel(self):
        if self.thread is not None:
            self.thread.cancel()

    def runChanged(self):
        self.getWorkspaceNames()

    @property
    def widget(self):
        return self.view

    # turn on button
    def activate(self):
        self.view.activateButton()

    # turn off button
    def deactivate(self):
        self.view.deactivateButton()

    def getWorkspaceNames(self):
        # get current values
        original_Re_name = self.view.workspace
        original_Im_name = self.view.imaginary_workspace
        final_options = self.load.get_workspace_names_for_FFT_analysis(
            self.view.use_raw_data)

        # update view
        self.view.addItems(final_options)
        self.view.removeRe('PhaseQuad')
        self.removePhaseFromIM(final_options)

        # make intelligent guess of what user wants
        current_group_pair = self.load.group_pair_context[
            self.load.group_pair_context.selected]
        Re_name_to_use = None
        Im_name_to_use = None
        default_name = None
        # will need to check this exists before using it
        if current_group_pair:
            default_name = current_group_pair.get_asymmetry_workspace_names(
                self.load.data_context.current_runs)
        # if the original selection is available we should use it
        if original_Re_name in final_options:
            Re_name_to_use = original_Re_name
        elif default_name:
            Re_name_to_use = default_name[0]
        self.view.workspace = Re_name_to_use
        if original_Im_name in final_options:
            Im_name_to_use = original_Im_name
        elif default_name:
            Im_name_to_use = default_name[0]
        self.view.imaginary_workspace = Im_name_to_use
        return

    def handle_use_raw_data_changed(self):
        if not self.view.use_raw_data and not self.load._do_rebin():
            self.view.set_raw_checkbox_state(True)
            self.view.warning_popup('No rebin options specified')
            return

        self.getWorkspaceNames()

    def removePhaseFromIM(self, final_options):
        for option in final_options:
            if "PhaseQuad" in option:
                self.view.removeIm(option)

    # functions
    def phaseCheck(self):
        self.view.phaseQuadChanged()
        # check if a phase table exists
        if mantid.AnalysisDataService.doesExist("PhaseTable"):
            self.view.setPhaseBox()

    def tableClicked(self, row, col):
        if row == self.view.getImBoxRow(
        ) and col == 1 and "PhaseQuad" not in self.view.workspace:
            self.view.changedHideUnTick(self.view.getImBox(),
                                        self.view.getImBoxRow() + 1)
        elif row == self.view.getShiftBoxRow() and col == 1:
            self.view.changed(self.view.getShiftBox(),
                              self.view.getShiftBoxRow() + 1)

    def createThread(self):
        self._phasequad_calculation_model = ThreadModelWrapper(
            self.calculate_FFT)
        return thread_model.ThreadModel(self._phasequad_calculation_model)

    # constructs the inputs for the FFT algorithms
    # then executes them (see fft_model to see the order
    # of execution
    def handleButton(self):
        # put this on its own thread so not to freeze Mantid
        self.thread = self.createThread()
        self.thread.threadWrapperSetUp(self.deactivate, self.handleFinished,
                                       self.handle_error)

        self.thread.start()

    def handle_error(self, error):
        self.view.activateButton()
        self.view.warning_popup(error)

    def get_pre_inputs(self):
        pre_inputs = self._get_generic_apodiazation_and_padding_inputs()
        pre_inputs['InputWorkspace'] = self.view.workspace

        return pre_inputs

    def get_imaginary_inputs(self):
        pre_inputs = self._get_generic_apodiazation_and_padding_inputs()

        pre_inputs['InputWorkspace'] = self.view.imaginary_workspace

        return pre_inputs

    def _get_generic_apodiazation_and_padding_inputs(self):
        pre_inputs = {}

        pre_inputs['InputWorkspace'] = self.view.imaginary_workspace
        pre_inputs["ApodizationFunction"] = self.view.apodization_function
        pre_inputs["DecayConstant"] = self.view.decay_constant
        pre_inputs["NegativePadding"] = self.view.negative_padding
        pre_inputs["Padding"] = self.view.padding_value

        return pre_inputs

    def get_fft_inputs(self,
                       real_workspace,
                       imaginary_workspace,
                       imanginary=0):
        FFTInputs = {}

        FFTInputs["AcceptXRoundingErrors"] = True
        FFTInputs['Real'] = 0
        FFTInputs['InputWorkspace'] = real_workspace
        FFTInputs['Transform'] = 'Forward'
        FFTInputs['AutoShift'] = self.view.auto_shift

        if self.view.imaginary_data:
            FFTInputs['InputImagWorkspace'] = imaginary_workspace
            FFTInputs['Imaginary'] = imanginary

        return FFTInputs

    # kills the thread at end of execution
    def handleFinished(self):
        self.activate()
        self.calculation_finished_notifier.notify_subscribers()

    def calculate_FFT(self):
        imaginary_workspace_index = 0
        real_workspace_padding_parameters = self.get_pre_inputs()
        imaginary_workspace_padding_parameters = self.get_imaginary_inputs()

        real_workspace_input = run_PaddingAndApodization(
            real_workspace_padding_parameters)

        if self.view.imaginary_data:
            if 'PhaseQuad' in self.view.workspace:
                imaginary_workspace_input = real_workspace_input
                imaginary_workspace_padding_parameters[
                    'InputWorkspace'] = real_workspace_padding_parameters[
                        'InputWorkspace']
                imaginary_workspace_index = 1
            else:
                imaginary_workspace_input = run_PaddingAndApodization(
                    imaginary_workspace_padding_parameters)
        else:
            imaginary_workspace_input = None
            imaginary_workspace_padding_parameters['InputWorkspace'] = ""

        fft_parameters = self.get_fft_inputs(real_workspace_input,
                                             imaginary_workspace_input,
                                             imaginary_workspace_index)

        frequency_domain_workspace = convert_to_field(run_FFT(fft_parameters))
        self.add_fft_workspace_to_ADS(
            real_workspace_padding_parameters['InputWorkspace'],
            imaginary_workspace_padding_parameters['InputWorkspace'],
            frequency_domain_workspace)

    def add_fft_workspace_to_ADS(self, input_workspace,
                                 imaginary_input_workspace, fft_workspace):
        run = re.search('[0-9]+', input_workspace).group()
        Im_run = ""
        if imaginary_input_workspace != "":
            Im_run = re.search('[0-9]+', imaginary_input_workspace).group()
        fft_workspace_name = get_fft_workspace_name(input_workspace,
                                                    imaginary_input_workspace)
        directory = get_fft_workspace_group_name(
            fft_workspace_name, self.load.data_context.instrument,
            self.load.workspace_suffix)
        Re = get_group_or_pair_from_name(input_workspace)
        Im = get_group_or_pair_from_name(imaginary_input_workspace)
        shift = 3 if fft_workspace.getNumberHistograms() == 6 else 0
        spectra = {
            "_" + FREQUENCY_EXTENSIONS["RE"]: 0 + shift,
            "_" + FREQUENCY_EXTENSIONS["IM"]: 1 + shift,
            "_" + FREQUENCY_EXTENSIONS["MOD"]: 2 + shift
        }
        for spec_type in list(spectra.keys()):
            extracted_ws = extract_single_spec(fft_workspace,
                                               spectra[spec_type])

            if 'PhaseQuad' in self.view.workspace:
                self.load._frequency_context.add_FFT(fft_workspace_name +
                                                     spec_type,
                                                     run,
                                                     Re,
                                                     Im_run,
                                                     Im,
                                                     phasequad=True)
            else:
                self.load._frequency_context.add_FFT(
                    fft_workspace_name + spec_type, run, Re, Im_run, Im)

            muon_workspace_wrapper = MuonWorkspaceWrapper(
                extracted_ws, directory + fft_workspace_name + spec_type)
            muon_workspace_wrapper.show()

    def update_view_from_model(self):
        self.getWorkspaceNames()
class HomePlotWidgetPresenter(HomeTabSubWidget):

    def __init__(self, view, model, context):
        """
        :param view: A reference to the QWidget object for plotting
        :param model: A refence to a model which contains the plotting logic
        :param context: A reference to the Muon context object
        """
        self._view = view
        self._model = model
        self.context = context
        self._view.on_plot_button_clicked(self.handle_data_updated)
        self._view.on_rebin_options_changed(
            self.handle_use_raw_workspaces_changed)
        self._view.on_plot_type_changed(self.handle_plot_type_changed)

        self.input_workspace_observer = GenericObserver(self.handle_data_updated)
        self.fit_observer = GenericObserver(self.handle_fit_completed)
        self.group_pair_observer = GenericObserver(self.handle_group_pair_to_plot_changed)
        self.plot_type_observer = GenericObserver(self.handle_group_pair_to_plot_changed)
        self.rebin_options_set_observer = GenericObserver(self.handle_rebin_options_set)
        self.plot_type_changed_notifier = GenericObservable()

        self._force_redraw = False
        if self.context._frequency_context:
            for ext in FREQUENCY_EXTENSIONS.keys():
                self._view.addItem(FREQ_PLOT_TYPE+FREQUENCY_EXTENSIONS[ext])
            self._view.addItem(FREQ_PLOT_TYPE+"All")
        self.instrument_observer = GenericObserver(self.handle_instrument_changed)
        self.keep = False

    def show(self):
        """
        Calls show on the view QtWidget
        """
        self._view.show()

    def update_view_from_model(self):
        """
        This is required in order for this presenter to match the base class interface
        """
        pass

    def handle_data_updated(self):
        """
        Handles the group, pair calculation finishing. Checks whether the list of workspaces has changed before doing
        anything as workspaces being modified in place is handled by the ADS handler observer.
        """
        if self._model.plotted_workspaces == self.get_workspaces_to_plot(
                self.context.group_pair_context.selected,
                self._view.if_raw(), self._view.get_selected()):
            # If the workspace names have not changed the ADS observer should
            # handle any updates
            return

        self.plot_standard_workspaces()

    def handle_use_raw_workspaces_changed(self):
        """
        Handles the use raw workspaces being changed. If
        """
        if not self._view.if_raw() and not self.context._do_rebin():
            self._view.set_raw_checkbox_state(True)
            self._view.warning_popup('No rebin options specified')
            return

        self.plot_standard_workspaces()

    def handle_plot_type_changed(self):
        """
        Handles the plot type being changed on the view
        """
        current_group_pair = self.context.group_pair_context[
            self.context.group_pair_context.selected]
        current_plot_type = self._view.get_selected()

        if isinstance(current_group_pair, MuonPair) and current_plot_type == COUNTS_PLOT_TYPE:
            self._view.plot_selector.blockSignals(True)
            self._view.plot_selector.setCurrentText(ASYMMETRY_PLOT_TYPE)
            self._view.plot_selector.blockSignals(False)
            self._view.warning_popup(
                'Pair workspaces have no counts workspace')
            return

        # force the plot to update
        self._force_redraw = True
        if self.context._frequency_context:
            self.context._frequency_context.plot_type = self._view.get_selected()[len(FREQ_PLOT_TYPE):]
        self.plot_type_changed_notifier.notify_subscribers()

        self.plot_standard_workspaces()

    def handle_group_pair_to_plot_changed(self):
        """
        Handles the selected group pair being changed on the view
        """
        if self.context.group_pair_context.selected == self._model.plotted_group:
            return
        self._model.plotted_group = self.context.group_pair_context.selected

        if not self._model.plot_figure:
            return

        self.plot_standard_workspaces()

    def plot_standard_workspaces(self):
        """
        Plots the standard list of workspaces in a new plot window, closing any existing plot windows.
        :return:
        """
        workspace_list = self.get_workspaces_to_plot(
            self.context.group_pair_context.selected, self._view.if_raw(),
            self._view.get_selected())
        self._model.plot(workspace_list, self.get_plot_title(), self.get_domain(), self._force_redraw, self.context.window_title)
        self._force_redraw = False

        self._model.plotted_workspaces_inverse_binning = {workspace: self.context.group_pair_context.get_equivalent_group_pair(workspace)
                                                          for workspace in workspace_list
                                                          if self.context.group_pair_context.get_equivalent_group_pair(workspace)}
        combined_ws_list = workspace_list + list(self._model.plotted_workspaces_inverse_binning.values())
        # This is checking whether the latest fit performed contains a fit which matches any of the workspaces just plotted
        # if it does then handle fit complete is also called to update the fit on the plot.
        if self.context.fitting_context.fit_list and \
                any([workspace in combined_ws_list
                     for workspace in self.context.fitting_context.fit_list[-1].input_workspaces]):
            self.handle_fit_completed()

    def handle_fit_completed(self):
        """
        When a new fit is done adds the fit to the plotted workspaces if appropriate
        :return:
        """
        if self._model.plot_figure is None:
            return

        for workspace_name in self._model.plotted_fit_workspaces:
            self._model.remove_workpace_from_plot(workspace_name)

        for index in range(1, self.context.fitting_context.number_of_fits + 1, 1):
            if self.context.fitting_context.fit_list:
                current_fit = self.context.fitting_context.fit_list[-index]
                combined_ws_list = self._model.plotted_workspaces + list(self._model.plotted_workspaces_inverse_binning.values())
                list_of_output_workspaces_to_plot = [output for output, input in
                                                     zip(current_fit.output_workspace_names, current_fit.input_workspaces)
                                                     if input in combined_ws_list]
                list_of_output_workspaces_to_plot = list_of_output_workspaces_to_plot if list_of_output_workspaces_to_plot\
                    else [current_fit.output_workspace_names[-1]]
            else:
                list_of_output_workspaces_to_plot = []

            for workspace_name in list_of_output_workspaces_to_plot:
                self._model.add_workspace_to_plot(workspace_name, 2, workspace_name + ': Fit')
                self._model.add_workspace_to_plot(workspace_name, 3, workspace_name + ': Diff')

        self._model.force_redraw()

    def get_workspaces_to_plot(self, current_group_pair, is_raw, plot_type):
        """
        :param current_group_pair: The group/pair currently selected
        :param is_raw: Whether to use raw or rebinned data
        :param plot_type: Whether to plot counts or asymmetry
        :return: a list of workspace names
        """
        if FREQ_PLOT_TYPE in plot_type:
            return self.get_freq_workspaces_to_plot(current_group_pair, plot_type)
        else:
            return self.get_time_workspaces_to_plot(current_group_pair, is_raw, plot_type)

    def get_freq_workspaces_to_plot(self, current_group_pair, plot_type):
        """
        :param current_group_pair: The group/pair currently selected
        :param plot_type: Whether to plot counts or asymmetry
        :return: a list of workspace names
        """
        try:
            runs = ""
            for run in self.context.data_context.current_runs:
                runs += ", " + str(run[0])
            workspace_list = self.context.get_names_of_frequency_domain_workspaces_to_fit(
                runs, current_group_pair, False, plot_type[len(FREQ_PLOT_TYPE):])

            return workspace_list
        except AttributeError:
            return []

    def get_time_workspaces_to_plot(self,current_group_pair, is_raw, plot_type):
        """
        :param current_group_pair: The group/pair currently selected
        :param is_raw: Whether to use raw or rebinned data
        :param plot_type: Whether to plot counts or asymmetry
        :return: a list of workspace names
        """
        try:
            if is_raw:
                workspace_list = self.context.group_pair_context[current_group_pair].get_asymmetry_workspace_names(
                    self.context.data_context.current_runs)
            else:
                workspace_list = self.context.group_pair_context[current_group_pair].get_asymmetry_workspace_names_rebinned(
                    self.context.data_context.current_runs)

            if plot_type == COUNTS_PLOT_TYPE:
                workspace_list = [item.replace(ASYMMETRY_PLOT_TYPE, COUNTS_PLOT_TYPE)
                                  for item in workspace_list if ASYMMETRY_PLOT_TYPE in item]

            return workspace_list
        except AttributeError:
            return []

    def get_plot_title(self):
        """
        Generates a title for the plot based on current instrument group and run numbers
        :return:
        """
        flattened_run_list = [
            item for sublist in self.context.data_context.current_runs for item in sublist]
        return self.context.data_context.instrument + ' ' + run_list_to_string(flattened_run_list) + ' ' + \
            self.context.group_pair_context.selected

    def handle_rebin_options_set(self):
        if self.context._do_rebin():
            self._view.set_raw_checkbox_state(False)
        else:
            self._view.set_raw_checkbox_state(True)

    def get_domain(self):
        if FREQ_PLOT_TYPE in self._view.get_selected():
            return "Frequency"
        else:
            return "Time"

    def handle_instrument_changed(self):
        if self._model.plot_figure is not None:
            from matplotlib import pyplot as plt
            plt.close(self._model.plot_figure)
Beispiel #9
0
class GroupingTabPresenter(object):
    """
    The grouping tab presenter is responsible for synchronizing the group and pair tables. It also maintains
    functionality which covers both groups/pairs ; e.g. loading/saving/updating data.
    """
    def __init__(self,
                 view,
                 model,
                 grouping_table_widget=None,
                 pairing_table_widget=None):
        self._view = view
        self._model = model

        self.grouping_table_widget = grouping_table_widget
        self.pairing_table_widget = pairing_table_widget

        # Synchronize the two tables
        self._view.on_grouping_table_changed(
            self.pairing_table_widget.update_view_from_model)
        self._view.on_pairing_table_changed(
            self.grouping_table_widget.update_view_from_model)

        self._view.set_description_text(self.text_for_description())
        self._view.on_add_pair_requested(self.add_pair_from_grouping_table)
        self._view.on_clear_grouping_button_clicked(self.on_clear_requested)
        self._view.on_load_grouping_button_clicked(
            self.handle_load_grouping_from_file)
        self._view.on_save_grouping_button_clicked(
            self.handle_save_grouping_file)
        self._view.on_default_grouping_button_clicked(
            self.handle_default_grouping_button_clicked)

        # monitors for loaded data changing
        self.loadObserver = GroupingTabPresenter.LoadObserver(self)
        self.instrumentObserver = GroupingTabPresenter.InstrumentObserver(self)

        # notifiers
        self.groupingNotifier = GroupingTabPresenter.GroupingNotifier(self)
        self.grouping_table_widget.on_data_changed(self.group_table_changed)
        self.pairing_table_widget.on_data_changed(self.pair_table_changed)
        self.enable_editing_notifier = GroupingTabPresenter.EnableEditingNotifier(
            self)
        self.disable_editing_notifier = GroupingTabPresenter.DisableEditingNotifier(
            self)
        self.calculation_finished_notifier = GenericObservable()

        self.guessAlphaObserver = GroupingTabPresenter.GuessAlphaObserver(self)
        self.pairing_table_widget.guessAlphaNotifier.add_subscriber(
            self.guessAlphaObserver)
        self.message_observer = GroupingTabPresenter.MessageObserver(self)
        self.gui_variables_observer = GroupingTabPresenter.GuiVariablesChangedObserver(
            self)
        self.enable_observer = GroupingTabPresenter.EnableObserver(self)
        self.disable_observer = GroupingTabPresenter.DisableObserver(self)

        self.update_view_from_model_observer = GenericObserver(
            self.update_view_from_model)

    def update_view_from_model(self):
        self.grouping_table_widget.update_view_from_model()
        self.pairing_table_widget.update_view_from_model()

    def show(self):
        self._view.show()

    def text_for_description(self):
        """
        Generate the text for the description edit at the top of the widget.
        """
        instrument = self._model.instrument
        n_detectors = self._model.num_detectors
        main_field = self._model.main_field_direction
        text = "{} , {} detectors, main field : {} to muon polarization".format(
            instrument, n_detectors, main_field)
        return text

    def update_description_text(self, description_text=''):
        if not description_text:
            description_text = self.text_for_description()
        self._view.set_description_text(description_text)

    def add_pair_from_grouping_table(self, group_name1, group_name2):
        """
        If user requests to add a pair from the grouping table.
        """
        pair = self._model.construct_empty_pair_with_group_names(
            group_name1, group_name2)
        self._model.add_pair(pair)
        self.pairing_table_widget.update_view_from_model()

    def handle_guess_alpha(self, pair_name, group1_name, group2_name):
        """
        Calculate alpha for the pair for which "Guess Alpha" button was clicked.
        """
        if len(self._model._data.current_runs) > 1:
            run, index, ok_clicked = RunSelectionDialog.get_run(
                self._model._data.current_runs, self._model._data.instrument,
                self._view)
            if not ok_clicked:
                return
            run_to_use = self._model._data.current_runs[index]
        else:
            run_to_use = self._model._data.current_runs[0]

        try:
            ws1 = self._model.get_group_workspace(group1_name, run_to_use)
            ws2 = self._model.get_group_workspace(group2_name, run_to_use)
        except KeyError:
            self._view.display_warning_box(
                'Group workspace not found, try updating all and then recalculating.'
            )
            return

        ws = algorithm_utils.run_AppendSpectra(ws1, ws2)

        new_alpha = algorithm_utils.run_AlphaCalc({
            "InputWorkspace": ws,
            "ForwardSpectra": [0],
            "BackwardSpectra": [1]
        })

        self._model.update_pair_alpha(pair_name, new_alpha)
        self.pairing_table_widget.update_view_from_model()

        self.handle_update_all_clicked()

    def handle_load_grouping_from_file(self):
        # Only XML format
        file_filter = file_utils.filter_for_extensions(["xml"])
        filename = self._view.show_file_browser_and_return_selection(
            file_filter, [""])

        groups, pairs, description, default = xml_utils.load_grouping_from_XML(
            filename)

        self._model.clear()
        for group in groups:
            try:
                self._model.add_group(group)
            except ValueError as error:
                self._view.display_warning_box(str(error))

        for pair in pairs:
            if pair.forward_group in self._model.group_names and pair.backward_group in self._model.group_names:
                self._model.add_pair(pair)

        self.grouping_table_widget.update_view_from_model()
        self.pairing_table_widget.update_view_from_model()
        self.update_description_text(description)
        self._model._context.group_pair_context.selected = default
        self.groupingNotifier.notify_subscribers()

        self.handle_update_all_clicked()

    def disable_editing(self):
        self._view.set_buttons_enabled(False)
        self.grouping_table_widget.disable_editing()
        self.pairing_table_widget.disable_editing()
        self.disable_editing_notifier.notify_subscribers()

    def enable_editing(self, result=None):
        self._view.set_buttons_enabled(True)
        self.grouping_table_widget.enable_editing()
        self.pairing_table_widget.enable_editing()
        self.enable_editing_notifier.notify_subscribers()

    def calculate_all_data(self):
        self._model.show_all_groups_and_pairs()

    def handle_update_all_clicked(self):
        self.update_thread = self.create_update_thread()
        self.update_thread.threadWrapperSetUp(self.disable_editing,
                                              self.handle_update_finished,
                                              self.error_callback)
        self.update_thread.start()

    def error_callback(self, error_message):
        self.enable_editing_notifier.notify_subscribers()
        self._view.display_warning_box(error_message)

    def handle_update_finished(self):
        self.enable_editing()
        self.groupingNotifier.notify_subscribers()
        self.calculation_finished_notifier.notify_subscribers()

    def handle_default_grouping_button_clicked(self):
        self._model.reset_groups_and_pairs_to_default()
        self.grouping_table_widget.update_view_from_model()
        self.pairing_table_widget.update_view_from_model()
        self.update_description_text()

    def on_clear_requested(self):
        self._model.clear()
        self.grouping_table_widget.update_view_from_model()
        self.pairing_table_widget.update_view_from_model()
        self.update_description_text()

    def handle_new_data_loaded(self):
        if self._model.is_data_loaded():
            self._model._context.show_raw_data()
            self.grouping_table_widget.update_view_from_model()
            self.pairing_table_widget.update_view_from_model()
            self.update_description_text()
            self.handle_update_all_clicked()
        else:
            self.on_clear_requested()

    def handle_save_grouping_file(self):
        filename = self._view.show_file_save_browser_and_return_selection()
        if filename != "":
            xml_utils.save_grouping_to_XML(
                self._model.groups,
                self._model.pairs,
                filename,
                description=self._view.get_description_text())

    def create_update_thread(self):
        self._update_model = ThreadModelWrapper(self.calculate_all_data)
        return thread_model.ThreadModel(self._update_model)

    # ------------------------------------------------------------------------------------------------------------------
    # Observer / Observable
    # ------------------------------------------------------------------------------------------------------------------

    def group_table_changed(self):
        self.handle_update_all_clicked()

    def pair_table_changed(self):
        self.handle_update_all_clicked()

    class LoadObserver(Observer):
        def __init__(self, outer):
            Observer.__init__(self)
            self.outer = outer

        def update(self, observable, arg):
            self.outer.handle_new_data_loaded()

    class InstrumentObserver(Observer):
        def __init__(self, outer):
            Observer.__init__(self)
            self.outer = outer

        def update(self, observable, arg):
            self.outer.on_clear_requested()

    class GuessAlphaObserver(Observer):
        def __init__(self, outer):
            Observer.__init__(self)
            self.outer = outer

        def update(self, observable, arg):
            self.outer.handle_guess_alpha(arg[0], arg[1], arg[2])

    class GuiVariablesChangedObserver(Observer):
        def __init__(self, outer):
            Observer.__init__(self)
            self.outer = outer

        def update(self, observable, arg):
            self.outer.handle_update_all_clicked()

    class GroupingNotifier(Observable):
        def __init__(self, outer):
            Observable.__init__(self)
            self.outer = outer  # handle to containing class

        def notify_subscribers(self, *args, **kwargs):
            Observable.notify_subscribers(self, *args, **kwargs)

    class MessageObserver(Observer):
        def __init__(self, outer):
            Observer.__init__(self)
            self.outer = outer

        def update(self, observable, arg):
            self.outer._view.display_warning_box(arg)

    class EnableObserver(Observer):
        def __init__(self, outer):
            Observer.__init__(self)
            self.outer = outer

        def update(self, observable, arg):
            self.outer.enable_editing()

    class DisableObserver(Observer):
        def __init__(self, outer):
            Observer.__init__(self)
            self.outer = outer

        def update(self, observable, arg):
            self.outer.disable_editing()

    class DisableEditingNotifier(Observable):
        def __init__(self, outer):
            Observable.__init__(self)
            self.outer = outer  # handle to containing class

        def notify_subscribers(self, *args, **kwargs):
            Observable.notify_subscribers(self, *args, **kwargs)

    class EnableEditingNotifier(Observable):
        def __init__(self, outer):
            Observable.__init__(self)
            self.outer = outer  # handle to containing class

        def notify_subscribers(self, *args, **kwargs):
            Observable.notify_subscribers(self, *args, **kwargs)
class HomeGroupingWidgetPresenter(HomeTabSubWidget):
    @staticmethod
    def string_to_list(text):
        # if text == "":
        #     return []
        # return [int(i) for i in text.split(",")]
        return run_string_to_list(text)

    def __init__(self, view, model):
        self._view = view
        self._model = model

        self._view.on_grouppair_selection_changed(
            self.handle_grouppair_selector_changed)

        self._view.on_alpha_changed(self.handle_user_changes_alpha)

        self._view.on_summed_periods_changed(self.handle_periods_changed)
        self._view.on_subtracted_periods_changed(self.handle_periods_changed)

        self.pairAlphaNotifier = HomeGroupingWidgetPresenter.PairAlphaNotifier(
            self)
        self.selected_group_pair_changed_notifier = GenericObservable()

    def show(self):
        self._view.show()

    def handle_user_changes_alpha(self):
        alpha = self._view.get_current_alpha()
        pair_name = str(self._view.get_currently_selected_group_pair())
        self._model.update_pair_alpha(pair_name, alpha)
        # notify any observers of the change
        self.pairAlphaNotifier.notify_subscribers()

    def update_group_pair_list(self):
        group_names = self._model.get_group_names()
        pair_names = self._model.get_pair_names()
        default_name = self._model.get_default_group_pair()
        self._view.populate_group_pair_selector(group_names, pair_names,
                                                default_name)

    def hide_multiperiod_widget_if_data_single_period(self):
        if self._model.is_data_multi_period():
            self._view.multi_period_widget_hidden(False)
        else:
            self._view.multi_period_widget_hidden(True)

    def handle_grouppair_selector_changed(self):
        name = str(self._view.get_selected_group_or_pair_name())
        self._model.update_selected_group_pair_in_context(name)
        if self._model.is_group(name):
            self._view.alpha_hidden(True)
        elif self._model.is_pair(name):
            self._view.alpha_hidden(False)
            alpha = self._model.get_alpha(name)
            self._view.set_current_alpha(alpha)
        else:
            self._view.alpha_hidden(True)

        self.selected_group_pair_changed_notifier.notify_subscribers()

    def update_view_from_model(self):
        self.update_group_pair_list()
        self.hide_multiperiod_widget_if_data_single_period()

        n_periods = self._model.number_of_periods()
        self._view.set_period_number_in_period_label(n_periods)

    def update_period_edits(self):
        summed_periods = self._model.get_summed_periods()
        subtracted_periods = self._model.get_subtracted_periods()

        self._view.set_summed_periods(",".join(
            [str(p) for p in summed_periods]))
        self._view.set_subtracted_periods(",".join(
            [str(p) for p in subtracted_periods]))

    def handle_periods_changed(self):
        self._view.summed_period_edit.blockSignals(True)
        self._view.subtracted_period_edit.blockSignals(True)
        summed = self.string_to_list(self._view.get_summed_periods())
        subtracted = self.string_to_list(self._view.get_subtracted_periods())

        subtracted = [i for i in subtracted if i not in summed]

        n_periods = self._model.number_of_periods()
        bad_periods = [period for period in summed if (period > n_periods) or period == 0] +\
                      [period for period in subtracted if(period > n_periods) or period == 0]
        if len(bad_periods) > 0:
            self._view.warning_popup(
                "The following periods are invalid : " +
                ",".join([str(period) for period in bad_periods]))

        summed = [
            p for p in summed
            if (p <= n_periods) and p > 0 and p not in bad_periods
        ]
        if not summed:
            summed = [1]

        subtracted = [
            p for p in subtracted
            if (p <= n_periods) and p > 0 and p not in bad_periods
        ]

        self._model.update_periods(summed, subtracted)

        self.update_period_edits()
        self._view.summed_period_edit.blockSignals(False)
        self._view.subtracted_period_edit.blockSignals(False)

    class PairAlphaNotifier(Observable):
        def __init__(self, outer):
            Observable.__init__(self)
            self.outer = outer  # handle to containing class

        def notify_subscribers(self, *args, **kwargs):
            Observable.notify_subscribers(self, *args, **kwargs)