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 __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 __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()
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()
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()
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)
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)