diff --git a/scripts/Frequency_Domain_Analysis.py b/scripts/Frequency_Domain_Analysis.py index 122e66c24cc1a56ac4736e67f9a85fb8051a7c0c..6989b453c827936a6981ffcc6936882cd2d39f38 100644 --- a/scripts/Frequency_Domain_Analysis.py +++ b/scripts/Frequency_Domain_Analysis.py @@ -12,7 +12,6 @@ from qtpy import QtCore Name = "Frequency_Domain_Analysis_2" - if 'muon_freq' in globals(): muon_freq = globals()['muon_freq'] if not muon_freq.isHidden(): diff --git a/scripts/Muon/GUI/Common/contexts/fitting_context.py b/scripts/Muon/GUI/Common/contexts/fitting_context.py index c9589c79f405f002c38785a726a7ff441a463ce6..434d6b115ded65a92d5e4082a6b2a43b9fe6b224 100644 --- a/scripts/Muon/GUI/Common/contexts/fitting_context.py +++ b/scripts/Muon/GUI/Common/contexts/fitting_context.py @@ -201,11 +201,12 @@ class FitInformation(object): self.fit_function_name = fit_function_name self.input_workspaces = [input_workspace] if isinstance( input_workspace, string_types) else input_workspace - self.output_workspace_names = output_workspace_names + self.output_workspace_names = [output_workspace_names] if isinstance( + output_workspace_names, string_types) else output_workspace_names def __eq__(self, other): """Objects are equal if each member is equal to the other""" - return self.parameters == other.parameters and \ + return self.parameter_workspace_name == other.parameter_workspace_name and \ self.fit_function_name == other.fit_function_name and \ self.input_workspaces == other.input_workspaces and \ self.output_workspace_names == other.output_workspace_names @@ -214,6 +215,10 @@ class FitInformation(object): def parameters(self): return self._fit_parameters + @property + def parameter_workspace_name(self): + return self._fit_parameters.parameter_workspace_name + def log_names(self, filter_fn=None): """ The names of the logs on the workspaces @@ -226,7 +231,7 @@ class FitInformation(object): filter_fn = filter_fn if filter_fn is not None else lambda x: True all_names = [] - for ws_name in self.input_workspaces: + for ws_name in self.output_workspace_names: logs = _run(ws_name).getLogData() all_names.extend([log.name for log in logs if filter_fn(log)]) @@ -237,7 +242,7 @@ class FitInformation(object): :param log_name: A string name :return: True if the log exists on all of the input workspaces False, otherwise """ - for ws_name in self.input_workspaces: + for ws_name in self.output_workspace_names: run = _run(ws_name) if not run.hasProperty(log_name): return False @@ -267,7 +272,7 @@ class FitInformation(object): values = [ value_from_workspace(wksp_name) - for wksp_name in self.input_workspaces + for wksp_name in self.output_workspace_names ] return np.mean(values) @@ -339,6 +344,15 @@ class FittingContext(object): return workspace_list + def remove_workspace_by_name(self, workspace_name): + list_of_fits_to_remove = [] + for fit in self.fit_list: + if workspace_name in fit.output_workspace_names or workspace_name==fit.parameter_workspace_name: + list_of_fits_to_remove.append(fit) + + for fit in list_of_fits_to_remove: + self.fit_list.remove(fit) + def log_names(self, filter_fn=None): """ The names of the logs on the workspaces associated with all of the workspaces. @@ -351,6 +365,9 @@ class FittingContext(object): name for fit in self.fit_list for name in fit.log_names(filter_fn) ] + def clear(self): + self.fit_list = [] + # Private functions def _run(ws_name): diff --git a/scripts/Muon/GUI/Common/contexts/muon_context.py b/scripts/Muon/GUI/Common/contexts/muon_context.py index caebc015e3643b9ea1a46409017a8e17bbfacff7..a8f87eca335fb52fcf083db5b181f500ce4cdd13 100644 --- a/scripts/Muon/GUI/Common/contexts/muon_context.py +++ b/scripts/Muon/GUI/Common/contexts/muon_context.py @@ -12,20 +12,17 @@ from Muon.GUI.Common.ADSHandler.workspace_naming import (get_raw_data_workspace_ get_pair_data_directory, get_group_asymmetry_name) from Muon.GUI.Common.calculate_pair_and_group import calculate_group_data, calculate_pair_data, \ estimate_group_asymmetry_data -from Muon.GUI.Common.contexts.muon_data_context import MuonDataContext -from Muon.GUI.Common.contexts.muon_group_pair_context import MuonGroupPairContext -from Muon.GUI.Common.contexts.muon_gui_context import MuonGuiContext -from Muon.GUI.Common.contexts.fitting_context import FittingContext -from Muon.GUI.Common.contexts.phase_table_context import PhaseTableContext from Muon.GUI.Common.utilities.run_string_utils import run_list_to_string, run_string_to_list import Muon.GUI.Common.ADSHandler.workspace_naming as wsName from Muon.GUI.Common.contexts.muon_group_pair_context import get_default_grouping +from Muon.GUI.Common.contexts.muon_context_ADS_observer import MuonContextADSObserver +from Muon.GUI.Common.observer_pattern import Observable class MuonContext(object): - def __init__(self, muon_data_context=MuonDataContext(), muon_gui_context=MuonGuiContext(), - muon_group_context=MuonGroupPairContext(), base_directory='Muon Data', muon_phase_context= PhaseTableContext(), - workspace_suffix=' MA', fitting_context=FittingContext()): + def __init__(self, muon_data_context=None, muon_gui_context=None, + muon_group_context=None, base_directory='Muon Data', muon_phase_context=None, + workspace_suffix=' MA', fitting_context=None): self._data_context = muon_data_context self._gui_context = muon_gui_context @@ -34,9 +31,12 @@ class MuonContext(object): self.fitting_context = fitting_context self.base_directory = base_directory self.workspace_suffix = workspace_suffix + self.ads_observer = MuonContextADSObserver(self.remove_workspace_by_name, self.clear_context) self.gui_context.update({'DeadTimeSource': 'None', 'LastGoodDataFromFile': True, 'selected_group_pair': ''}) + self.update_view_from_model_notifier = Observable() + @property def data_context(self): return self._data_context @@ -149,23 +149,8 @@ class MuonContext(object): 'RebinVariable' in self.gui_context and self.gui_context['RebinVariable']) def get_workspace_names_for_FFT_analysis(self, use_raw=True): - pair_names = list(self.group_pair_context.pair_names) - group_names = list(self.group_pair_context.group_names) - run_numbers = self.data_context.current_runs - workspace_options = [] - - for run in run_numbers: - workspace_options += self.phase_context.get_phase_quad(self.data_context.instrument, run_list_to_string(run)) - - for name in pair_names: - workspace_options.append( - wsName.get_pair_data_workspace_name(self, - str(name), - run_list_to_string(run), not use_raw)) - for group_name in group_names: - workspace_options.append( - wsName.get_group_asymmetry_name(self, str(group_name), run_list_to_string(run), - not use_raw)) + workspace_options = self.get_names_of_workspaces_to_fit(runs='All', group_and_pair='All', phasequad=True, + rebin=not use_raw) return workspace_options def get_detectors_excluded_from_default_grouping_tables(self): @@ -264,3 +249,17 @@ class MuonContext(object): equivalent_list.append(equivalent_group_pair) return equivalent_list + + def remove_workspace_by_name(self, workspace_name): + self.data_context.remove_workspace_by_name(workspace_name) + self.group_pair_context.remove_workspace_by_name(workspace_name) + self.phase_context.remove_workspace_by_name(workspace_name) + self.fitting_context.remove_workspace_by_name(workspace_name) + self.update_view_from_model_notifier.notify_subscribers(workspace_name) + + def clear_context(self): + self.data_context.clear() + self.group_pair_context.clear() + self.phase_context.clear() + self.fitting_context.clear() + self.update_view_from_model_notifier.notify_subscribers() diff --git a/scripts/Muon/GUI/Common/contexts/muon_context_ADS_observer.py b/scripts/Muon/GUI/Common/contexts/muon_context_ADS_observer.py new file mode 100644 index 0000000000000000000000000000000000000000..334f509f00edd456891208c6475917b35b75b497 --- /dev/null +++ b/scripts/Muon/GUI/Common/contexts/muon_context_ADS_observer.py @@ -0,0 +1,63 @@ +# Mantid Repository : https://github.com/mantidproject/mantid +# +# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, +# NScD Oak Ridge National Laboratory, European Spallation Source +# & Institut Laue - Langevin +# SPDX - License - Identifier: GPL - 3.0 + +from __future__ import (absolute_import, division, unicode_literals) +from mantid.api import AnalysisDataServiceObserver +from functools import wraps +import sys + + +def _catch_exceptions(func): + """ + Catch all exceptions in method and print a traceback to stderr + """ + + @wraps(func) + def wrapper(*args, **kwargs): + try: + func(*args, **kwargs) + except Exception: + sys.stderr.write("Error occurred in handler:\n") + import traceback + traceback.print_exc() + + return wrapper + + +class MuonContextADSObserver(AnalysisDataServiceObserver): + def __init__(self, delete_callback, clear_callback): + super(MuonContextADSObserver, self).__init__() + self.delete_callback = delete_callback + self.clear_callback = clear_callback + + self.observeDelete(True) + self.observeRename(True) + self.observeClear(True) + + @_catch_exceptions + def deleteHandle(self, workspace_name, workspace): + """ + Called when the ADS has deleted a workspace. Removes that workspace from the context and cleans up. + :param workspace_name: The name of the workspace + :param workspace: not used + """ + self.delete_callback(workspace_name) + + @_catch_exceptions + def renameHandle(self, old_workspace_name, new_workspace_name): + """ + Called when the ADS has renamed a workspace. Currently treats this the same as deletion. + :param workspace_name: The name of the workspace + :param workspace: not used + """ + self.delete_callback(old_workspace_name) + + @_catch_exceptions + def clearHandle(self): + """ + Called when the ADS has been cleared, removes all data and rests the GUI + """ + self.clear_callback() diff --git a/scripts/Muon/GUI/Common/contexts/muon_data_context.py b/scripts/Muon/GUI/Common/contexts/muon_data_context.py index 26f9c2aec076eb9367cefdcb8f317068c24423c8..b73416182346ae885676786fee32b5636bd40ef5 100644 --- a/scripts/Muon/GUI/Common/contexts/muon_data_context.py +++ b/scripts/Muon/GUI/Common/contexts/muon_data_context.py @@ -72,7 +72,6 @@ class MuonDataContext(object): Groups and Pairs associated to the current run are stored in _grousp and _pairs as ordered dictionaries. """ self._loaded_data = load_data - self._current_data = {"workspace": load_utils.empty_loaded_data(), 'run': []} # self.get_result(False) self._current_runs = [] self._main_field_direction = '' @@ -129,7 +128,6 @@ class MuonDataContext(object): def update_current_data(self): # Update the current data; resetting the groups and pairs to their default values - self._current_data = self._loaded_data.get_data(run=self.current_runs[0], instrument=self.instrument) if self.current_data['MainFieldDirection'] and self.current_data['MainFieldDirection'] != self._main_field_direction\ and self._main_field_direction: @@ -137,6 +135,14 @@ class MuonDataContext(object): ' data sets, click default to reset grouping if required') self._main_field_direction = self.current_data['MainFieldDirection'] + @property + def _current_data(self): + loaded_data = {} + if self.current_runs: + loaded_data = self._loaded_data.get_data(run=self.current_runs[0], instrument=self.instrument) + + return loaded_data if loaded_data else {"workspace": load_utils.empty_loaded_data(), 'run': []} + @property def current_data(self): return self._current_data["workspace"] @@ -221,7 +227,9 @@ class MuonDataContext(object): # Clearing data # ------------------------------------------------------------------------------------------------------------------ def clear(self): - self._current_data = {"workspace": load_utils.empty_loaded_data(), 'run': []} + self._loaded_data.clear() + self._current_runs = [] + self._main_field_direction = '' def _base_run_name(self, run=None): """ e.g. EMU0001234 """ @@ -266,6 +274,11 @@ class MuonDataContext(object): message += 'longitudinal field runs {}\n'.format(run_list_to_string(longitudinal)) return message + def remove_workspace_by_name(self, workspace_name): + runs_removed = self._loaded_data.remove_workspace_by_name(workspace_name, self.instrument) + + self.current_runs = [item for item in self.current_runs if item not in runs_removed] + class InstrumentNotifier(Observable): def __init__(self, outer): Observable.__init__(self) diff --git a/scripts/Muon/GUI/Common/contexts/muon_group_pair_context.py b/scripts/Muon/GUI/Common/contexts/muon_group_pair_context.py index 3ea5a11593e16ec7de84fa82998fa9c0bdda84dc..8b0f4dfda79c3b99fd1e93a0f1a587142e7c2c63 100644 --- a/scripts/Muon/GUI/Common/contexts/muon_group_pair_context.py +++ b/scripts/Muon/GUI/Common/contexts/muon_group_pair_context.py @@ -118,6 +118,10 @@ class MuonGroupPairContext(object): def pairs(self): return self._pairs + def clear(self): + self.clear_groups() + self.clear_pairs() + def clear_groups(self): self._groups = [] @@ -217,3 +221,7 @@ class MuonGroupPairContext(object): return equivalent_name return None + + def remove_workspace_by_name(self, workspace_name): + for item in self.groups + self.pairs: + item.remove_workspace_by_name(workspace_name) diff --git a/scripts/Muon/GUI/Common/contexts/phase_table_context.py b/scripts/Muon/GUI/Common/contexts/phase_table_context.py index 9eef8dbbd87c8e25467bbd69289375f8b7c4a4fa..1114c697a7673e141b3d74d20136543048a67957 100644 --- a/scripts/Muon/GUI/Common/contexts/phase_table_context.py +++ b/scripts/Muon/GUI/Common/contexts/phase_table_context.py @@ -30,3 +30,11 @@ class PhaseTableContext(object): def get_phase_quad(self, instrument, run): return [phase_quad.workspace_name for phase_quad in self.phase_quad if instrument in phase_quad.workspace_name and run in phase_quad.workspace_name] + + def remove_workspace_by_name(self, workspace_name): + self.phase_tables = [item for item in self.phase_tables if item.workspace_name != workspace_name] + self.phase_quad = [item for item in self.phase_tables if item.workspace_name != workspace_name] + + def clear(self): + self.phase_tables = [] + self.phase_quad = [] diff --git a/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_model.py b/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_model.py index 1d4db47bd87efc0061a6334a3a516bc1fd7729eb..f32163b40ceff0c60e294c0dc1818bc77409e01d 100644 --- a/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_model.py +++ b/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_model.py @@ -10,7 +10,7 @@ from Muon.GUI.Common.utilities.algorithm_utils import run_Fit, run_simultaneous_ import mantid from Muon.GUI.Common.ADSHandler.muon_workspace_wrapper import MuonWorkspaceWrapper from Muon.GUI.Common.ADSHandler.workspace_naming import get_fit_workspace_directory -from mantid.simpleapi import RenameWorkspace +from mantid.simpleapi import RenameWorkspace, CopyLogs class FittingTabModel(object): @@ -40,7 +40,9 @@ class FittingTabModel(object): def do_single_fit_and_return_workspace_parameters_and_fit_function( self, parameters_dict): alg = mantid.AlgorithmManager.create("Fit") - return run_Fit(parameters_dict, alg) + output_workspace, output_parameters, function_object, output_status, output_chi = run_Fit(parameters_dict, alg) + CopyLogs(InputWorkspace=parameters_dict['InputWorkspace'], OutputWorkspace=output_workspace, StoreInADS=False) + return output_workspace, output_parameters, function_object, output_status, output_chi def add_workspace_to_ADS(self, workspace, name, directory): workspace_wrapper = MuonWorkspaceWrapper(workspace, directory + name) @@ -97,9 +99,17 @@ class FittingTabModel(object): def do_simultaneous_fit_and_return_workspace_parameters_and_fit_function( self, parameters_dict): alg = mantid.AlgorithmManager.create("Fit") - return run_simultaneous_Fit(parameters_dict, alg) + + output_workspace, output_parameters, function_object, output_status, output_chi = run_simultaneous_Fit(parameters_dict, alg) + if len(parameters_dict['InputWorkspace']) > 1: + for input_workspace, output in zip(parameters_dict['InputWorkspace'], output_workspace.getNames()): + CopyLogs(InputWorkspace=input_workspace, OutputWorkspace=output, StoreInADS=False) + else: + CopyLogs(InputWorkspace=parameters_dict['InputWorkspace'][0], OutputWorkspace=output_workspace, StoreInADS=False) + return output_workspace, output_parameters, function_object, output_status, output_chi def rename_members_of_fitted_workspace_group(self, group_workspace, inputworkspace_list, function, group_name): + self.context.ads_observer.observeRename(False) output_workspace_list = [] for index, workspace_name in enumerate(group_workspace.getNames()): new_name, _ = self.create_fitted_workspace_name(inputworkspace_list[index], function, group_name) @@ -109,6 +119,8 @@ class FittingTabModel(object): RenameWorkspace(InputWorkspace=workspace_name, OutputWorkspace=new_name) + self.context.ads_observer.observeRename(True) + return output_workspace_list def do_sequential_fit(self, parameter_dict): diff --git a/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_presenter.py b/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_presenter.py index 6e0f29e40ee435854ad87e33da18e5adcde48a67..e97d52a604452bfd325081e5c948912e746c4aae 100644 --- a/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_presenter.py +++ b/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_presenter.py @@ -40,6 +40,8 @@ class FittingTabPresenter(object): self.enable_tab_observer = GenericObserver(lambda: self.view. setEnabled(True)) + self.update_view_from_model_observer = GenericObserverWithArgPassing(self.update_view_from_model) + def handle_select_fit_data_clicked(self): selected_data, dialog_return = WorkspaceSelectorView.get_selected_data( self.context.data_context.current_runs, @@ -316,3 +318,9 @@ class FittingTabPresenter(object): self._fit_status[current_index], self._fit_chi_squared[current_index]) self.view.update_global_fit_state(self._fit_status) + + def update_view_from_model(self, workspace_removed=None): + if workspace_removed: + self.selected_data = [item for item in self.selected_data if item != workspace_removed] + else: + self.selected_data = [] diff --git a/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_widget.py b/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_widget.py index 09d994252b58a917e3ebc051f1511908a2764839..b02982d7dd2884874967424ff2644bf4caac472c 100644 --- a/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_widget.py +++ b/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_widget.py @@ -30,3 +30,5 @@ class FittingTabWidget(object): self.fitting_tab_presenter.handle_function_structure_changed) self.fitting_tab_view.function_name_line_edit.textChanged.connect( self.fitting_tab_presenter.handle_fit_name_changed_by_user) + + context.update_view_from_model_notifier.add_subscriber(self.fitting_tab_presenter.update_view_from_model_observer) diff --git a/scripts/Muon/GUI/Common/grouping_tab_widget/grouping_tab_widget.py b/scripts/Muon/GUI/Common/grouping_tab_widget/grouping_tab_widget.py index 6247a97b86a141bcea658b548d03059bfc7ffdfd..6f563ff071489722b349e70af42cfc8141c56618 100644 --- a/scripts/Muon/GUI/Common/grouping_tab_widget/grouping_tab_widget.py +++ b/scripts/Muon/GUI/Common/grouping_tab_widget/grouping_tab_widget.py @@ -33,3 +33,5 @@ class GroupingTabWidget(object): self.group_tab_model, self.grouping_table_widget, self.pairing_table_widget) + + context.update_view_from_model_notifier.add_subscriber(self.group_tab_presenter.update_view_from_model_observer) diff --git a/scripts/Muon/GUI/Common/grouping_tab_widget/grouping_tab_widget_model.py b/scripts/Muon/GUI/Common/grouping_tab_widget/grouping_tab_widget_model.py index c8cbdbf4000243db98ec7663eded2cb22d744508..fd0ad7abf85607b706c72de74931b3af44cf149c 100644 --- a/scripts/Muon/GUI/Common/grouping_tab_widget/grouping_tab_widget_model.py +++ b/scripts/Muon/GUI/Common/grouping_tab_widget/grouping_tab_widget_model.py @@ -6,7 +6,6 @@ # SPDX - License - Identifier: GPL - 3.0 + from __future__ import (absolute_import, division, print_function) -from Muon.GUI.Common.contexts.muon_context import MuonContext from Muon.GUI.Common.contexts.muon_data_context import construct_empty_group, construct_empty_pair from Muon.GUI.Common.muon_group import MuonGroup from Muon.GUI.Common.muon_pair import MuonPair @@ -20,7 +19,7 @@ class GroupingTabModel(object): pairs and groups should be of type MuonGroup and MuonPair respectively. """ - def __init__(self, context=MuonContext()): + def __init__(self, context=None): self._context = context self._data = context.data_context self._groups_and_pairs = context.group_pair_context diff --git a/scripts/Muon/GUI/Common/grouping_tab_widget/grouping_tab_widget_presenter.py b/scripts/Muon/GUI/Common/grouping_tab_widget/grouping_tab_widget_presenter.py index 3802d0ab56b86bbef37caf707457d51490b2c1ff..9f6cb9c1b05e341de100a74f9a9b4704ea299427 100644 --- a/scripts/Muon/GUI/Common/grouping_tab_widget/grouping_tab_widget_presenter.py +++ b/scripts/Muon/GUI/Common/grouping_tab_widget/grouping_tab_widget_presenter.py @@ -6,7 +6,7 @@ # SPDX - License - Identifier: GPL - 3.0 + from __future__ import (absolute_import, division, print_function) -from Muon.GUI.Common.observer_pattern import Observer, Observable, GenericObservable +from Muon.GUI.Common.observer_pattern import Observer, Observable, GenericObservable, GenericObserver import Muon.GUI.Common.utilities.muon_file_utils as file_utils import Muon.GUI.Common.utilities.xml_utils as xml_utils import Muon.GUI.Common.utilities.algorithm_utils as algorithm_utils @@ -60,6 +60,12 @@ class GroupingTabPresenter(object): 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() diff --git a/scripts/Muon/GUI/Common/home_grouping_widget/home_grouping_widget_model.py b/scripts/Muon/GUI/Common/home_grouping_widget/home_grouping_widget_model.py index cba9d162758e0dd27960ccb6ce22eb8eca9721f0..70058e2bedf054306428e520786fadc99e1ac63a 100644 --- a/scripts/Muon/GUI/Common/home_grouping_widget/home_grouping_widget_model.py +++ b/scripts/Muon/GUI/Common/home_grouping_widget/home_grouping_widget_model.py @@ -6,12 +6,10 @@ # SPDX - License - Identifier: GPL - 3.0 + from __future__ import (absolute_import, division, print_function) -from Muon.GUI.Common.contexts.muon_context import MuonContext - class HomeGroupingWidgetModel(object): - def __init__(self, context=MuonContext()): + def __init__(self, context=None): self._data = context.data_context self._context = context diff --git a/scripts/Muon/GUI/Common/home_instrument_widget/home_instrument_widget_model.py b/scripts/Muon/GUI/Common/home_instrument_widget/home_instrument_widget_model.py index 5d1bffe3715ab04cb9d3dc0167f9f4f490380690..10353fdfd6137d866123630f4f9b1ce4d5653c56 100644 --- a/scripts/Muon/GUI/Common/home_instrument_widget/home_instrument_widget_model.py +++ b/scripts/Muon/GUI/Common/home_instrument_widget/home_instrument_widget_model.py @@ -11,8 +11,6 @@ from decimal import Decimal, InvalidOperation from mantid import api from mantid.api import ITableWorkspace -from Muon.GUI.Common.contexts.muon_context import MuonContext - class InstrumentWidgetModel(object): """ @@ -24,7 +22,7 @@ class InstrumentWidgetModel(object): GUI. """ - def __init__(self, context=MuonContext()): + def __init__(self, context=None): self._data = context.data_context self._context = context self._context.gui_context['RebinType'] = 'None' diff --git a/scripts/Muon/GUI/Common/home_instrument_widget/home_instrument_widget_view.py b/scripts/Muon/GUI/Common/home_instrument_widget/home_instrument_widget_view.py index 9c59d30a3133b83599284b857e28bd8104c424ea..2af53a7e4218b3f5d5e8107bf4f3736c6d06fad4 100644 --- a/scripts/Muon/GUI/Common/home_instrument_widget/home_instrument_widget_view.py +++ b/scripts/Muon/GUI/Common/home_instrument_widget/home_instrument_widget_view.py @@ -136,7 +136,7 @@ class InstrumentWidgetView(QtWidgets.QWidget): self.instrument_selector.setSizePolicy( self._fixed_aspect_ratio_size_policy( self.instrument_selector)) - self.instrument_selector.addItems(["None"] + allowed_instruments) + self.instrument_selector.addItems(allowed_instruments) self.instrument_label = QtWidgets.QLabel(self) self.instrument_label.setText("Instrument : ") diff --git a/scripts/Muon/GUI/Common/home_runinfo_widget/home_runinfo_widget_model.py b/scripts/Muon/GUI/Common/home_runinfo_widget/home_runinfo_widget_model.py index 7bb1e39fee2f483e85b5cb7a7a3e098a883d709c..350f0d9ddca0f1310f427ae0f9692991cb643caa 100644 --- a/scripts/Muon/GUI/Common/home_runinfo_widget/home_runinfo_widget_model.py +++ b/scripts/Muon/GUI/Common/home_runinfo_widget/home_runinfo_widget_model.py @@ -6,13 +6,11 @@ # SPDX - License - Identifier: GPL - 3.0 + from __future__ import (absolute_import, division, print_function) -from Muon.GUI.Common.contexts.muon_context import MuonContext - millions_counts_conversion = 1. / 1e6 class HomeRunInfoWidgetModel(object): - def __init__(self, context=MuonContext()): + def __init__(self, context=None): self._data = context.data_context def get_run_number(self): diff --git a/scripts/Muon/GUI/Common/home_tab/home_tab_model.py b/scripts/Muon/GUI/Common/home_tab/home_tab_model.py index fc4cef4e4992d96771a5fa33e3e1fc2b1ef8dade..8f8da2ad8d30f0a8f1d0924012383a47c99e756a 100644 --- a/scripts/Muon/GUI/Common/home_tab/home_tab_model.py +++ b/scripts/Muon/GUI/Common/home_tab/home_tab_model.py @@ -6,13 +6,10 @@ # SPDX - License - Identifier: GPL - 3.0 + from __future__ import (absolute_import, division, print_function) -# import mantid.simpleapi as mantid -from Muon.GUI.Common.contexts.muon_context import MuonContext - class HomeTabModel(object): - def __init__(self, context=MuonContext()): + def __init__(self, context=None): self._data = context.data_context self._context = context diff --git a/scripts/Muon/GUI/Common/home_tab/home_tab_presenter.py b/scripts/Muon/GUI/Common/home_tab/home_tab_presenter.py index 2050e9688bd3ddf34f60d05a4e938cd879ab4328..10d5530589c79f56b816867966e522bc9dd847aa 100644 --- a/scripts/Muon/GUI/Common/home_tab/home_tab_presenter.py +++ b/scripts/Muon/GUI/Common/home_tab/home_tab_presenter.py @@ -7,7 +7,7 @@ from __future__ import (absolute_import, division, print_function) from abc import ABCMeta, abstractmethod -from Muon.GUI.Common.observer_pattern import Observer +from Muon.GUI.Common.observer_pattern import Observer, GenericObserver class HomeTabSubWidget: @@ -39,6 +39,8 @@ class HomeTabPresenter(object): self.enable_observer = HomeTabPresenter.EnableWidgetObserver(self) self.disable_observer = HomeTabPresenter.DisableWidgetObserver(self) + self.update_view_from_model_observer = GenericObserver(self.update_all_widgets) + self.update_all_widgets() def show(self): diff --git a/scripts/Muon/GUI/Common/home_tab/home_tab_widget.py b/scripts/Muon/GUI/Common/home_tab/home_tab_widget.py index 2c1673837c63efbcfa4cef592dac331fb2a93a74..76aae34202db744e30c0cb18b9dec43807eb64ff 100644 --- a/scripts/Muon/GUI/Common/home_tab/home_tab_widget.py +++ b/scripts/Muon/GUI/Common/home_tab/home_tab_widget.py @@ -52,3 +52,5 @@ class HomeTabWidget(object): self.group_widget, self.plot_widget, self.run_info_widget]) + + context.update_view_from_model_notifier.add_subscriber(self.home_tab_widget.update_view_from_model_observer) diff --git a/scripts/Muon/GUI/Common/muon_group.py b/scripts/Muon/GUI/Common/muon_group.py index 99b01b855963cdf38b29ef7593fb7d30bc461bdd..e9424bc3fd3baf3d7af86521cef6cbd8b4b007a2 100644 --- a/scripts/Muon/GUI/Common/muon_group.py +++ b/scripts/Muon/GUI/Common/muon_group.py @@ -131,3 +131,26 @@ class MuonGroup(object): return self._counts_workspace[key].workspace_name return None + + def remove_workspace_by_name(self, workspace_name): + """ + Searches through all of the stored workspaces and remmves any which match the name given. This is used to handle + workspaces being removed from the ADS. + :param workspace_name: + :return: + + """ + + def _remove_workspace_from_dict_by_name(workspace_name, dictionary): + set_of_keys_to_remove = set() + for key, workspace_wrapper in dictionary.items(): + if workspace_wrapper.workspace_name == workspace_name: + set_of_keys_to_remove.add(key) + + for key in set_of_keys_to_remove: + dictionary.pop(key) + + _remove_workspace_from_dict_by_name(workspace_name, self._counts_workspace) + _remove_workspace_from_dict_by_name(workspace_name, self._asymmetry_estimate) + _remove_workspace_from_dict_by_name(workspace_name, self._counts_workspace_rebin) + _remove_workspace_from_dict_by_name(workspace_name, self._asymmetry_estimate_rebin) diff --git a/scripts/Muon/GUI/Common/muon_load_data.py b/scripts/Muon/GUI/Common/muon_load_data.py index 3eb7c117112cf49fa34f9b4a6a7afa604829e430..a993c0230070a88be1116e940b438ecd96b867e0 100644 --- a/scripts/Muon/GUI/Common/muon_load_data.py +++ b/scripts/Muon/GUI/Common/muon_load_data.py @@ -137,3 +137,17 @@ class MuonLoadData: return self.get_data(**kwargs)['workspace']['MainFieldDirection'] else: return None + + def remove_workspace_by_name(self, workspace_name, instrument=''): + list_of_workspace_names_to_remove = [] + for entry in self.params: + if any([workspace.workspace_name == workspace_name for workspace in entry['workspace']['OutputWorkspace']]): + list_of_workspace_names_to_remove.append(entry) + + runs_removed = [] + for entry in list_of_workspace_names_to_remove: + if instrument == entry['instrument']: + runs_removed.append(entry['run']) + self.remove_data(**entry) + + return runs_removed diff --git a/scripts/Muon/GUI/Common/muon_pair.py b/scripts/Muon/GUI/Common/muon_pair.py index d51d61323e74b728e78eecb519b9e974aa44df57..47e78c9859312c5d69e1550858786b310beff98e 100644 --- a/scripts/Muon/GUI/Common/muon_pair.py +++ b/scripts/Muon/GUI/Common/muon_pair.py @@ -115,3 +115,24 @@ class MuonPair(object): return self._workspace[key].workspace_name return None + + def remove_workspace_by_name(self, workspace_name): + """ + Searches through all of the stored workspaces and remmves any which match the name given. This is used to handle + workspaces being removed from the ADS. + :param workspace_name: + :return: + + """ + + def _remove_workspace_from_dict_by_name(workspace_name, dictionary): + set_of_keys_to_remove = set() + for key, workspace_wrapper in dictionary.items(): + if workspace_wrapper.workspace_name == workspace_name: + set_of_keys_to_remove.add(key) + + for key in set_of_keys_to_remove: + dictionary.pop(key) + + _remove_workspace_from_dict_by_name(workspace_name, self._workspace) + _remove_workspace_from_dict_by_name(workspace_name, self.workspace_rebin) diff --git a/scripts/Muon/GUI/Common/observer_pattern.py b/scripts/Muon/GUI/Common/observer_pattern.py index 57cf71eb24b80ef4f3d24e89045b1fccfc09e668..56d2bc94a1e872b43da62f76d2bf48a23e4f982f 100644 --- a/scripts/Muon/GUI/Common/observer_pattern.py +++ b/scripts/Muon/GUI/Common/observer_pattern.py @@ -5,6 +5,7 @@ # & Institut Laue - Langevin # SPDX - License - Identifier: GPL - 3.0 + from __future__ import (absolute_import, division, print_function) +from qtpy.QtCore import QMetaObject, QObject, Slot class Observer(object): @@ -24,14 +25,16 @@ class Observer(object): pass -class Observable(object): +class Observable(QObject): """ The Observable is an object which may be subscribed to by Observers. It maintains a list of subscribers to it, and when needed, it will notify those subscribers. """ - def __init__(self): + super(Observable, self).__init__() self._subscribers = [] + self.arg = None + self.kwargs = {} def add_subscriber(self, observer_instance): if not isinstance(observer_instance, Observer): @@ -49,8 +52,18 @@ class Observable(object): return len(self._subscribers) def notify_subscribers(self, arg=None, **kwargs): - for observer in self._subscribers: - observer.update(self, arg, **kwargs) + self.arg = arg + self.kwargs = kwargs + QMetaObject.invokeMethod(self, '_notify_subscribers_impl') + + @Slot() + def _notify_subscribers_impl(self): + try: + for observer in self._subscribers: + observer.update(self, self.arg, **self.kwargs) + finally: + self.arg = None + self.kwargs = {} class GenericObserver(Observer): diff --git a/scripts/Muon/GUI/Common/phase_table_widget/phase_table_presenter.py b/scripts/Muon/GUI/Common/phase_table_widget/phase_table_presenter.py index 82760816d5e8cf372b1ac39b7581c87cf9e87ca9..427fcbb110f70650819d429da9cecf0e56745908 100644 --- a/scripts/Muon/GUI/Common/phase_table_widget/phase_table_presenter.py +++ b/scripts/Muon/GUI/Common/phase_table_widget/phase_table_presenter.py @@ -40,9 +40,14 @@ class PhaseTablePresenter(object): self.phase_table_calculation_complete_notifier = Observable() self.phase_quad_calculation_complete_nofifier = Observable() + self.update_view_from_model_observer = GenericObserver(self.update_view_from_model) + self.update_current_phase_tables() def update_view_from_model(self): + self.view.set_input_combo_box(self.context.getGroupedWorkspaceNames()) + self.view.set_group_combo_boxes(self.context.group_pair_context.group_names) + self.update_current_phase_tables() for key, item in self.context.phase_context.options_dict.items(): setattr(self.view, key, item) @@ -190,8 +195,6 @@ class PhaseTablePresenter(object): backward_group = self.context.phase_context.options_dict['backward_group'] parameters['BackwardSpectra'] = self.context.group_pair_context[backward_group].detectors - parameters['DetectorTable'] = parameters['InputWorkspace'].replace('_raw_data', '') + "; PhaseTable" - parameters['DetectorTable'] = get_phase_table_workspace_name(parameters['InputWorkspace'], forward_group, backward_group) diff --git a/scripts/Muon/GUI/Common/phase_table_widget/phase_table_widget.py b/scripts/Muon/GUI/Common/phase_table_widget/phase_table_widget.py index 004f5541cbc102006e16af2e5faf73bac9983e58..e2ca32594bff3866062af16eae7cb73864aadf33 100644 --- a/scripts/Muon/GUI/Common/phase_table_widget/phase_table_widget.py +++ b/scripts/Muon/GUI/Common/phase_table_widget/phase_table_widget.py @@ -21,3 +21,5 @@ class PhaseTabWidget(object): self.phase_table_view.set_calculate_phase_quad_action(self.phase_table_presenter.handle_calculate_phase_quad_button_clicked) self.phase_table_view.set_cancel_action(self.phase_table_presenter.cancel) + + context.update_view_from_model_notifier.add_subscriber(self.phase_table_presenter.update_view_from_model_observer) diff --git a/scripts/Muon/GUI/Common/results_tab_widget/results_tab_presenter.py b/scripts/Muon/GUI/Common/results_tab_widget/results_tab_presenter.py index 45b387f86db3efc3008899ec5e178dbf7860496d..5cd73a59868d1714e02759844060ef6cbaa9fce3 100644 --- a/scripts/Muon/GUI/Common/results_tab_widget/results_tab_presenter.py +++ b/scripts/Muon/GUI/Common/results_tab_widget/results_tab_presenter.py @@ -23,6 +23,8 @@ class ResultsTabPresenter(QObject): self.new_fit_performed_observer = GenericObserver( self.on_new_fit_performed) + self.update_view_from_model_observer = GenericObserver(self.update_view_from_model) + self._init_view() # callbacks @@ -87,3 +89,6 @@ class ResultsTabPresenter(QObject): self.view.set_log_values( self.model.log_selection( existing_selection=self.view.log_values())) + + def update_view_from_model(self): + self.on_new_fit_performed() diff --git a/scripts/Muon/GUI/Common/results_tab_widget/results_tab_widget.py b/scripts/Muon/GUI/Common/results_tab_widget/results_tab_widget.py index 2b6cbdee667685be4da0331498c88df6f7e44d23..9c94af2aef5bbc569f6610d0351be85b144e0e8d 100644 --- a/scripts/Muon/GUI/Common/results_tab_widget/results_tab_widget.py +++ b/scripts/Muon/GUI/Common/results_tab_widget/results_tab_widget.py @@ -14,7 +14,7 @@ from Muon.GUI.Common.results_tab_widget.results_tab_model import ResultsTabModel class ResultsTabWidget(object): """Factory object to wire together components of the results tab""" - def __init__(self, fit_context, parent): + def __init__(self, fit_context, context, parent): """ Initialize the widget. :param fit_context: A reference to the a FitContext object used to store fit results @@ -23,3 +23,5 @@ class ResultsTabWidget(object): self.results_tab_view = ResultsTabView(parent=parent) self.results_tab_presenter = ResultsTabPresenter( self.results_tab_view, ResultsTabModel(fit_context)) + + context.update_view_from_model_notifier.add_subscriber(self.results_tab_presenter.update_view_from_model_observer) diff --git a/scripts/Muon/GUI/FrequencyDomainAnalysis/FFT/fft_presenter_new.py b/scripts/Muon/GUI/FrequencyDomainAnalysis/FFT/fft_presenter_new.py index 0d2588cc608c31982ea6cc2bf2139b7f12123b74..ac389dedd7fb86d93c30fa77e1a2cc0d055898e1 100644 --- a/scripts/Muon/GUI/FrequencyDomainAnalysis/FFT/fft_presenter_new.py +++ b/scripts/Muon/GUI/FrequencyDomainAnalysis/FFT/fft_presenter_new.py @@ -189,3 +189,6 @@ class FFTPresenter(object): muon_workspace_wrapper = MuonWorkspaceWrapper(fft_workspace, directory + fft_workspace_name) muon_workspace_wrapper.show() + + def update_view_from_model(self): + self.getWorkspaceNames() diff --git a/scripts/Muon/GUI/FrequencyDomainAnalysis/FFT/fft_widget_new.py b/scripts/Muon/GUI/FrequencyDomainAnalysis/FFT/fft_widget_new.py index 35cc02d8f7d7a46023053adb52cbe407721f5b9d..38b915cc0aab43e74a7f219dd73ff79024749840 100644 --- a/scripts/Muon/GUI/FrequencyDomainAnalysis/FFT/fft_widget_new.py +++ b/scripts/Muon/GUI/FrequencyDomainAnalysis/FFT/fft_widget_new.py @@ -37,3 +37,6 @@ class FFTWidget(QtWidgets.QWidget): def closeEvent(self, event): self._presenter.cancel() + + def update_view_from_model(self): + self._presenter.update_view_from_model() diff --git a/scripts/Muon/GUI/FrequencyDomainAnalysis/MaxEnt/maxent_presenter_new.py b/scripts/Muon/GUI/FrequencyDomainAnalysis/MaxEnt/maxent_presenter_new.py index 261c6c527e839c698703e537d30cd3bdf6063572..5cd355fa7b6bdd83568b7f5705f91c27c8b0b1ae 100644 --- a/scripts/Muon/GUI/FrequencyDomainAnalysis/MaxEnt/maxent_presenter_new.py +++ b/scripts/Muon/GUI/FrequencyDomainAnalysis/MaxEnt/maxent_presenter_new.py @@ -176,3 +176,6 @@ class MaxEntPresenter(object): 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() diff --git a/scripts/Muon/GUI/FrequencyDomainAnalysis/MaxEnt/maxent_widget_new.py b/scripts/Muon/GUI/FrequencyDomainAnalysis/MaxEnt/maxent_widget_new.py index ef62b20c3b5fb59cd801991686d202535012facc..52891621cc5cc229acd47e3fa24dfcfbd5f0e63b 100644 --- a/scripts/Muon/GUI/FrequencyDomainAnalysis/MaxEnt/maxent_widget_new.py +++ b/scripts/Muon/GUI/FrequencyDomainAnalysis/MaxEnt/maxent_widget_new.py @@ -35,3 +35,6 @@ class MaxEntWidget(QtWidgets.QWidget): def closeEvent(self, event): self._presenter.cancel() + + def update_view_from_model(self): + self._presenter.update_view_from_model() diff --git a/scripts/Muon/GUI/FrequencyDomainAnalysis/Transform/transform_widget.py b/scripts/Muon/GUI/FrequencyDomainAnalysis/Transform/transform_widget.py index bc3ab9fe64dcf6eaba9dbe840780e8b916fe97d7..d340aadd7dd1ba936cff6668ea3965e80b4e9118 100644 --- a/scripts/Muon/GUI/FrequencyDomainAnalysis/Transform/transform_widget.py +++ b/scripts/Muon/GUI/FrequencyDomainAnalysis/Transform/transform_widget.py @@ -9,7 +9,7 @@ from __future__ import (absolute_import, division, print_function) from Muon.GUI.FrequencyDomainAnalysis.Transform.transform_view import TransformView from Muon.GUI.FrequencyDomainAnalysis.TransformSelection.transform_selection_widget import TransformSelectionWidget -from Muon.GUI.Common.observer_pattern import Observer +from Muon.GUI.Common.observer_pattern import Observer, GenericObserver from qtpy import QtWidgets @@ -22,6 +22,7 @@ class TransformWidget(QtWidgets.QWidget): self._maxent = maxent_widget(load=load, parent=self) self._selector = TransformSelectionWidget(parent=self) self.LoadObserver = LoadObserver(self) + self.load = load self.instrumentObserver = instrumentObserver(self) self.GroupPairObserver = GroupPairObserver(self) self.enable_observer = EnableObserver(self) @@ -34,6 +35,12 @@ class TransformWidget(QtWidgets.QWidget): self._selector.setSelectionConnection(self.updateDisplay) self.updateDisplay('FFT') + self.update_view_from_model_observer = GenericObserver(self.update_view_from_model) + self.load.update_view_from_model_notifier.add_subscriber(self.update_view_from_model_observer) + + def update_view_from_model(self): + self._fft.update_view_from_model() + self._maxent.update_view_from_model() @property def widget(self): diff --git a/scripts/Muon/GUI/FrequencyDomainAnalysis/frequency_domain_analysis_2.py b/scripts/Muon/GUI/FrequencyDomainAnalysis/frequency_domain_analysis_2.py index 3548cace66bf6a12d997c3b0311e38ee2bf01245..a93b199444b7618489d2e32f3a9f6bf18d6b0627 100644 --- a/scripts/Muon/GUI/FrequencyDomainAnalysis/frequency_domain_analysis_2.py +++ b/scripts/Muon/GUI/FrequencyDomainAnalysis/frequency_domain_analysis_2.py @@ -16,6 +16,7 @@ from Muon.GUI.Common.contexts.muon_data_context import MuonDataContext from Muon.GUI.Common.contexts.muon_group_pair_context import MuonGroupPairContext from Muon.GUI.Common.contexts.phase_table_context import PhaseTableContext from Muon.GUI.Common.contexts.muon_gui_context import MuonGuiContext +from Muon.GUI.Common.contexts.fitting_context import FittingContext from Muon.GUI.Common.dock.dockable_tabs import DetachableTabWidget from Muon.GUI.Common.grouping_tab_widget.grouping_tab_widget import GroupingTabWidget from Muon.GUI.Common.help_widget.help_widget_presenter import HelpWidget @@ -66,10 +67,11 @@ class FrequencyAnalysisGui(QtWidgets.QMainWindow): self.gui_context = MuonGuiContext() self.group_pair_context = MuonGroupPairContext(self.data_context.check_group_contains_valid_detectors) self.phase_context = PhaseTableContext() + self.fitting_context = FittingContext() self.context = MuonContext(muon_data_context=self.data_context, muon_gui_context=self.gui_context, muon_group_context=self.group_pair_context, muon_phase_context=self.phase_context, - workspace_suffix=' FD') + workspace_suffix=' FD', fitting_context=self.fitting_context) # construct all the widgets. self.load_widget = LoadWidget(self.loaded_data, self.context, self) @@ -223,4 +225,5 @@ class FrequencyAnalysisGui(QtWidgets.QMainWindow): def closeEvent(self, event): self.tabs.closeEvent(event) + self.context.ads_observer = None super(FrequencyAnalysisGui, self).closeEvent(event) diff --git a/scripts/Muon/GUI/MuonAnalysis/load_widget/load_widget.py b/scripts/Muon/GUI/MuonAnalysis/load_widget/load_widget.py index 1a4b111716a8e615dade3da04a24ba3897a7be41..0e105c4b91e1c699cc4b21764f3cc6b972fc38df 100644 --- a/scripts/Muon/GUI/MuonAnalysis/load_widget/load_widget.py +++ b/scripts/Muon/GUI/MuonAnalysis/load_widget/load_widget.py @@ -37,3 +37,5 @@ class LoadWidget(object): self.load_widget.set_load_run_widget(self.run_widget) self.load_widget.set_current_instrument(context.data_context.instrument) + + context.update_view_from_model_notifier.add_subscriber(self.load_widget.update_view_from_model_observer) diff --git a/scripts/Muon/GUI/MuonAnalysis/load_widget/load_widget_presenter.py b/scripts/Muon/GUI/MuonAnalysis/load_widget/load_widget_presenter.py index 14e5d3367e18d0953d160252d39912761533b21b..39dd02d3f918252fd676619943d0bf3e81d648f4 100644 --- a/scripts/Muon/GUI/MuonAnalysis/load_widget/load_widget_presenter.py +++ b/scripts/Muon/GUI/MuonAnalysis/load_widget/load_widget_presenter.py @@ -6,7 +6,7 @@ # SPDX - License - Identifier: GPL - 3.0 + from __future__ import (absolute_import, division, print_function) -from Muon.GUI.Common.observer_pattern import Observer, Observable +from Muon.GUI.Common.observer_pattern import Observer, Observable, GenericObserver CO_ADD = 'Co-Add' SIMULTANEOUS = 'Simultaneous' @@ -52,6 +52,8 @@ class LoadWidgetPresenter(object): self.disable_observer = LoadWidgetPresenter.DisableObserver(self) self.enable_observer = LoadWidgetPresenter.EnableObserver(self) + self.update_view_from_model_observer = GenericObserver(self.update_view_from_model) + def set_load_run_widget(self, widget): self.load_run_widget = widget self.load_run_widget.update_view_from_model([]) @@ -112,6 +114,10 @@ class LoadWidgetPresenter(object): self._model.instrument) self.loadNotifier.notify_subscribers() + def update_view_from_model(self): + self.load_run_widget.update_view_from_model(self._model.runs) + self.load_file_widget.update_view_from_model(self._model.filenames) + @property def view(self): return self._view diff --git a/scripts/Muon/GUI/MuonAnalysis/muon_analysis_2.py b/scripts/Muon/GUI/MuonAnalysis/muon_analysis_2.py index f12e7c38b70299634bb050558eba3bc9592351f8..af2aa271227c87577f253aca8bcc6ab147e48280 100644 --- a/scripts/Muon/GUI/MuonAnalysis/muon_analysis_2.py +++ b/scripts/Muon/GUI/MuonAnalysis/muon_analysis_2.py @@ -84,7 +84,7 @@ class MuonAnalysisGui(QtWidgets.QMainWindow): self.home_tab = HomeTabWidget(self.context, self) self.phase_tab = PhaseTabWidget(self.context, self) self.fitting_tab = FittingTabWidget(self.context, self) - self.results_tab = ResultsTabWidget(self.context.fitting_context, self) + self.results_tab = ResultsTabWidget(self.context.fitting_context, self.context, self) self.setup_tabs() self.help_widget = HelpWidget("Muon Analysis 2") @@ -257,4 +257,5 @@ class MuonAnalysisGui(QtWidgets.QMainWindow): def closeEvent(self, event): self.tabs.closeEvent(event) + self.context.ads_observer = None super(MuonAnalysisGui, self).closeEvent(event) diff --git a/scripts/test/Muon/fft_presenter_context_interaction_test.py b/scripts/test/Muon/fft_presenter_context_interaction_test.py index 4a9054702cdc9b32b72c41a95abf8ff8e3fbe73f..36b7cd2a79705d5f8e3ff37ee6bd8ad7be95cd17 100644 --- a/scripts/test/Muon/fft_presenter_context_interaction_test.py +++ b/scripts/test/Muon/fft_presenter_context_interaction_test.py @@ -52,6 +52,8 @@ class FFTPresenterTest(GuiTest): self.context.update_current_data() test_pair = MuonPair('test_pair', 'top', 'bottom', alpha=0.75) self.context.group_pair_context.add_pair(pair=test_pair) + self.context.show_all_groups() + self.context.show_all_pairs() self.view.warning_popup = mock.MagicMock() @@ -62,53 +64,56 @@ class FFTPresenterTest(GuiTest): self.presenter.getWorkspaceNames() self.assertEqual(retrieve_combobox_info(self.view.ws), - ['MUSR22725; Pair Asym; test_pair; MA', - 'MUSR22725; Group; top; Asymmetry; MA', 'MUSR22725; Group; bkwd; Asymmetry; MA', - 'MUSR22725; Group; bottom; Asymmetry; MA', 'MUSR22725; Group; fwd; Asymmetry; MA']) + ['MUSR22725; Group; top; Asymmetry; MA', 'MUSR22725; Group; bkwd; Asymmetry; MA', + 'MUSR22725; Group; bottom; Asymmetry; MA', 'MUSR22725; Group; fwd; Asymmetry; MA', + 'MUSR22725; Pair Asym; test_pair; MA']) - self.assertEqual(retrieve_combobox_info(self.view.Im_ws), ['MUSR22725; Pair Asym; test_pair; MA', - 'MUSR22725; Group; top; Asymmetry; MA', + self.assertEqual(retrieve_combobox_info(self.view.Im_ws), ['MUSR22725; Group; top; Asymmetry; MA', 'MUSR22725; Group; bkwd; Asymmetry; MA', 'MUSR22725; Group; bottom; Asymmetry; MA', - 'MUSR22725; Group; fwd; Asymmetry; MA']) + 'MUSR22725; Group; fwd; Asymmetry; MA', + 'MUSR22725; Pair Asym; test_pair; MA']) def test_handle_use_raw_data_changed_when_no_rebin_set(self): self.view.set_raw_checkbox_state(False) self.assertEqual(retrieve_combobox_info(self.view.ws), - ['MUSR22725; Pair Asym; test_pair; MA', - 'MUSR22725; Group; top; Asymmetry; MA', 'MUSR22725; Group; bkwd; Asymmetry; MA', - 'MUSR22725; Group; bottom; Asymmetry; MA', 'MUSR22725; Group; fwd; Asymmetry; MA']) + ['MUSR22725; Group; top; Asymmetry; MA', 'MUSR22725; Group; bkwd; Asymmetry; MA', + 'MUSR22725; Group; bottom; Asymmetry; MA', 'MUSR22725; Group; fwd; Asymmetry; MA', + 'MUSR22725; Pair Asym; test_pair; MA']) - self.assertEqual(retrieve_combobox_info(self.view.Im_ws), ['MUSR22725; Pair Asym; test_pair; MA', - 'MUSR22725; Group; top; Asymmetry; MA', + self.assertEqual(retrieve_combobox_info(self.view.Im_ws), ['MUSR22725; Group; top; Asymmetry; MA', 'MUSR22725; Group; bkwd; Asymmetry; MA', 'MUSR22725; Group; bottom; Asymmetry; MA', - 'MUSR22725; Group; fwd; Asymmetry; MA']) + 'MUSR22725; Group; fwd; Asymmetry; MA', + 'MUSR22725; Pair Asym; test_pair; MA']) self.view.warning_popup.assert_called_once_with('No rebin options specified') def test_handle_use_raw_data_changed_when_rebin_set(self): self.context.gui_context.update({'RebinType': 'Fixed', 'RebinFixed': 2}) + self.context.show_all_groups() + self.context.show_all_pairs() self.view.set_raw_checkbox_state(False) self.assertEqual(retrieve_combobox_info(self.view.ws), - ['MUSR22725; Pair Asym; test_pair; Rebin; MA', - 'MUSR22725; Group; top; Asymmetry; Rebin; MA', 'MUSR22725; Group; bkwd; Asymmetry; Rebin; MA', + ['MUSR22725; Group; top; Asymmetry; Rebin; MA', 'MUSR22725; Group; bkwd; Asymmetry; Rebin; MA', 'MUSR22725; Group; bottom; Asymmetry; Rebin; MA', - 'MUSR22725; Group; fwd; Asymmetry; Rebin; MA']) + 'MUSR22725; Group; fwd; Asymmetry; Rebin; MA','MUSR22725; Pair Asym; test_pair; Rebin; MA']) - self.assertEqual(retrieve_combobox_info(self.view.Im_ws), ['MUSR22725; Pair Asym; test_pair; Rebin; MA', - 'MUSR22725; Group; top; Asymmetry; Rebin; MA', + self.assertEqual(retrieve_combobox_info(self.view.Im_ws), ['MUSR22725; Group; top; Asymmetry; Rebin; MA', 'MUSR22725; Group; bkwd; Asymmetry; Rebin; MA', 'MUSR22725; Group; bottom; Asymmetry; Rebin; MA', - 'MUSR22725; Group; fwd; Asymmetry; Rebin; MA']) + 'MUSR22725; Group; fwd; Asymmetry; Rebin; MA', + 'MUSR22725; Pair Asym; test_pair; Rebin; MA']) def test_get_pre_inputs_with_phase_quad(self): workspace_wrapper = mock.MagicMock() workspace_wrapper.workspace_name = 'MUSR22725_PhaseQuad_MUSR22725_phase_table' self.context.phase_context.add_phase_quad(workspace_wrapper) self.presenter.getWorkspaceNames() + index = self.view.ws.findText('MUSR22725_PhaseQuad_MUSR22725_phase_table') + self.view.ws.setCurrentIndex(index) self.assertEqual(self.presenter.get_pre_inputs(), {'ApodizationFunction': 'Lorentz', 'DecayConstant': 4.4, 'InputWorkspace': 'MUSR22725_PhaseQuad_MUSR22725_phase_table', @@ -116,7 +121,8 @@ class FFTPresenterTest(GuiTest): def test_pre_inputs(self): self.presenter.getWorkspaceNames() - self.view.ws.setCurrentIndex(1) + index = self.view.ws.findText('MUSR22725; Group; top; Asymmetry; MA') + self.view.ws.setCurrentIndex(index) self.assertEquals(self.presenter.get_pre_inputs(), {'ApodizationFunction': 'Lorentz', 'DecayConstant': 4.4, 'InputWorkspace': 'MUSR22725; Group; top; Asymmetry; MA', @@ -124,6 +130,9 @@ class FFTPresenterTest(GuiTest): def test_get_imaginary_pre_inputs(self): self.presenter.getWorkspaceNames() + index = self.view.Im_ws.findText('MUSR22725; Pair Asym; test_pair; MA') + self.view.Im_ws.setCurrentIndex(index) + self.assertEqual(self.presenter.get_imaginary_inputs(), {'ApodizationFunction': 'Lorentz', 'DecayConstant': 4.4, 'InputWorkspace': 'MUSR22725; Pair Asym; test_pair; MA', @@ -185,6 +194,8 @@ class FFTPresenterTest(GuiTest): workspace_wrapper.workspace_name = 'MUSR22725_PhaseQuad_MUSR22725_phase_table' self.context.phase_context.add_phase_quad(workspace_wrapper) self.presenter.getWorkspaceNames() + index = self.view.ws.findText('MUSR22725_PhaseQuad_MUSR22725_phase_table') + self.view.ws.setCurrentIndex(index) self.presenter.calculate_FFT() diff --git a/scripts/test/Muon/fit_information_test.py b/scripts/test/Muon/fit_information_test.py index fd8289f9c89f40992a8635dfd97e570c7bf86bdd..a3915285399b7caed06a797415987d379d6d97bb 100644 --- a/scripts/test/Muon/fit_information_test.py +++ b/scripts/test/Muon/fit_information_test.py @@ -97,7 +97,7 @@ class FitInformationTest(unittest.TestCase): def test_logs_from_workspace_without_logs_returns_emtpy_list(self): fake_ws = create_test_workspace() fit = FitInformation(mock.MagicMock(), 'func1', fake_ws.name(), - mock.MagicMock()) + fake_ws.name()) allowed_logs = fit.log_names() self.assertEqual(0, len(allowed_logs)) @@ -107,7 +107,7 @@ class FitInformationTest(unittest.TestCase): single_value_logs = (('sv_1', 'val1'), ('sv_2', 'val2')) fake_ws = create_test_workspace(time_series_logs=time_series_logs) fit = FitInformation(mock.MagicMock(), 'func1', fake_ws.name(), - mock.MagicMock()) + fake_ws.name()) log_names = fit.log_names() for name, _ in time_series_logs: @@ -126,7 +126,7 @@ class FitInformationTest(unittest.TestCase): fake2 = create_test_workspace( ws_name='fake2', time_series_logs=time_series_logs[2:]) fit = FitInformation(mock.MagicMock(), 'func1', - [fake1.name(), fake2.name()], mock.MagicMock()) + [fake1.name(), fake2.name()], [fake1.name(), fake2.name()]) log_names = fit.log_names() self.assertEqual(len(time_series_logs), len(log_names)) @@ -140,7 +140,7 @@ class FitInformationTest(unittest.TestCase): fake1 = create_test_workspace( ws_name='fake1', time_series_logs=time_series_logs) fit = FitInformation(mock.MagicMock(), 'func1', fake1.name(), - mock.MagicMock()) + fake1.name()) log_names = fit.log_names(lambda log: log.name == 'ts_1') self.assertEqual(1, len(log_names)) @@ -153,7 +153,7 @@ class FitInformationTest(unittest.TestCase): fake2 = create_test_workspace( ws_name='fake2', time_series_logs=time_series_logs) fit = FitInformation(mock.MagicMock(), 'func1', - [fake1.name(), fake2.name()], mock.MagicMock()) + [fake1.name(), fake2.name()], [fake1.name(), fake2.name()]) self.assertTrue(fit.has_log('ts_1')) @@ -163,7 +163,7 @@ class FitInformationTest(unittest.TestCase): ws_name='fake1', time_series_logs=time_series_logs) fake2 = create_test_workspace(ws_name='fake2') fit = FitInformation(mock.MagicMock(), 'func1', - [fake1.name(), fake2.name()], mock.MagicMock()) + [fake1.name(), fake2.name()], [fake1.name(), fake2.name()]) self.assertFalse( fit.has_log('ts_1'), @@ -174,7 +174,7 @@ class FitInformationTest(unittest.TestCase): fake1 = create_test_workspace( ws_name='fake1', string_value_logs=single_value_logs) fit = FitInformation(mock.MagicMock(), 'func1', [fake1.name()], - mock.MagicMock()) + [fake1.name()]) self.assertEqual( float(single_value_logs[0][1]), @@ -187,8 +187,7 @@ class FitInformationTest(unittest.TestCase): ("2000-05-01T12:00:10", 20.), ("2000-05-01T12:05:00", 30.)))] fake1 = create_test_workspace('fake1', time_series_logs) - fit = FitInformation(mock.MagicMock(), 'func1', [fake1.name()], - mock.MagicMock()) + fit = FitInformation(mock.MagicMock(), 'func1', [fake1.name()], [fake1.name()], ) time_average = (10 * 5 + 290 * 20) / 300. self.assertAlmostEqual(time_average, fit.log_value('ts_1'), places=6) @@ -206,7 +205,7 @@ class FitInformationTest(unittest.TestCase): ("2000-05-01T12:05:00", 40.)))] fake2 = create_test_workspace('fake2', time_series_logs2) fit = FitInformation(mock.MagicMock(), 'func1', - [fake1.name(), fake2.name()], mock.MagicMock()) + [fake1.name(), fake2.name()], [fake1.name(), fake2.name()]) time_average1 = (10 * 5 + 290 * 20) / 300. time_average2 = (75 * 10 + 195 * 30) / 270. diff --git a/scripts/test/Muon/fitting_context_test.py b/scripts/test/Muon/fitting_context_test.py index 19327ed92824c2657c1784f556c976cbb6dcc950..78cc7b92b6c70dc60455e25e776405ec578afb7d 100644 --- a/scripts/test/Muon/fitting_context_test.py +++ b/scripts/test/Muon/fitting_context_test.py @@ -183,10 +183,10 @@ class FittingContextTest(unittest.TestCase): ws_name='fake2', time_series_logs=time_series_logs[2:]) self.fitting_context.add_fit( FitInformation(mock.MagicMock(), 'func1', fake1.name(), - mock.MagicMock())) + fake1.name())) self.fitting_context.add_fit( FitInformation(mock.MagicMock(), 'func1', fake2.name(), - mock.MagicMock())) + fake2.name())) log_names = self.fitting_context.log_names() self.assertEqual(len(time_series_logs), len(log_names)) @@ -203,10 +203,10 @@ class FittingContextTest(unittest.TestCase): ws_name='fake2', time_series_logs=time_series_logs[2:]) self.fitting_context.add_fit( FitInformation(mock.MagicMock(), 'func1', fake1.name(), - mock.MagicMock())) + fake1.name())) self.fitting_context.add_fit( FitInformation(mock.MagicMock(), 'func1', fake2.name(), - mock.MagicMock())) + fake2.name())) required_logs = ('ts_2', 'ts_4') log_names = self.fitting_context.log_names( diff --git a/scripts/test/Muon/muon_context_test.py b/scripts/test/Muon/muon_context_test.py index d467c4e21617b8b11704769f564969e6e6e5c60d..c1dc3835ad39a3d5f0ec2cd6deb5aaba0d7a378a 100644 --- a/scripts/test/Muon/muon_context_test.py +++ b/scripts/test/Muon/muon_context_test.py @@ -18,22 +18,23 @@ from Muon.GUI.Common.contexts.muon_gui_context import MuonGuiContext from Muon.GUI.Common.muon_load_data import MuonLoadData from Muon.GUI.Common.utilities.load_utils import load_workspace_from_filename from Muon.GUI.Common.ADSHandler.muon_workspace_wrapper import MuonWorkspaceWrapper +from Muon.GUI.Common.test_helpers.context_setup import setup_context class MuonContextTest(unittest.TestCase): def setUp(self): AnalysisDataService.clear() self.filepath = FileFinder.findRuns('EMU00019489.nxs')[0] + self.load_result, self.run_number, self.filename, psi_data = load_workspace_from_filename(self.filepath) self.assert_(not psi_data) - self.loaded_data = MuonLoadData() - self.data_context = MuonDataContext(load_data=self.loaded_data) - self.gui_context = MuonGuiContext() - self.group_pair_context = MuonGroupPairContext() - self.gui_context.update({'RebinType': 'None'}) - - self.context = MuonContext(muon_data_context=self.data_context, muon_gui_context=self.gui_context, muon_group_context=self.group_pair_context) + self.context = setup_context() + self.context.gui_context.update({'RebinType': 'None'}) + self.loaded_data = self.context.data_context._loaded_data + self.data_context = self.context.data_context + self.gui_context = self.context.gui_context + self.group_pair_context = self.context.group_pair_context self.data_context.instrument = 'EMU' self.loaded_data.add_data(workspace=self.load_result, run=[self.run_number], filename=self.filename, diff --git a/scripts/test/Muon/phase_table_widget/phase_table_presenter_test.py b/scripts/test/Muon/phase_table_widget/phase_table_presenter_test.py index 9ceea8f4d12e0e213f778674bdef8cf99ab37a63..ecebe4e35091c687ce58fb2e4e9d69365808c1a5 100644 --- a/scripts/test/Muon/phase_table_widget/phase_table_presenter_test.py +++ b/scripts/test/Muon/phase_table_widget/phase_table_presenter_test.py @@ -8,9 +8,8 @@ import unittest from mantid.py3compat import mock from mantidqt.utils.qt.testing import GuiTest -from qtpy import QtCore from qtpy.QtWidgets import QApplication - +from qtpy import QtCore from Muon.GUI.Common.phase_table_widget.phase_table_presenter import PhaseTablePresenter from Muon.GUI.Common.phase_table_widget.phase_table_view import PhaseTableView from Muon.GUI.Common.muon_group import MuonGroup @@ -95,6 +94,7 @@ class PhaseTablePresenterTest(GuiTest): def test_handle_calculate_phase_table_clicked_behaves_correctly_for_succesful_calculation(self, run_algorith_mock): detector_table_mock = mock.MagicMock() self.view.set_input_combo_box(['MUSR22222_raw_data_period_1']) + self.context.getGroupedWorkspaceNames = mock.MagicMock(return_value=['MUSR22222_raw_data_period_1']) self.context.phase_context.options_dict['input_workspace'] = 'MUSR22222_raw_data_period_1' self.presenter.update_view_from_model() run_algorith_mock.return_value = (detector_table_mock, mock.MagicMock()) diff --git a/scripts/test/Muon/results_tab_widget/results_tab_model_test.py b/scripts/test/Muon/results_tab_widget/results_tab_model_test.py index 7dcdb1032350527588467e8eace3449110ffc8e0..dd699100c095236a781c8e23619a97f9b459e14b 100644 --- a/scripts/test/Muon/results_tab_widget/results_tab_model_test.py +++ b/scripts/test/Muon/results_tab_widget/results_tab_model_test.py @@ -251,7 +251,7 @@ class ResultsTabModelTest(unittest.TestCase): model.results_table_name()) def test_create_results_table_with_logs_selected(self): - _, model = create_test_model(('ws1', ), 'func1', self.parameters, [], + _, model = create_test_model(('ws1', ), 'func1', self.parameters, ('ws1', ), self.logs) selected_results = [('ws1', 0)] table = model.create_results_table(self.log_names, selected_results) @@ -350,10 +350,10 @@ class ResultsTabModelTest(unittest.TestCase): parameters = OrderedDict([('f0.Height', (100, 0.1))]) logs = [('log1', (1., 2.)), ('log2', (3., 4.)), ('log3', (4., 5.)), ('log4', (5., 6.))] - fits_logs1 = create_test_fits(('ws1', ), 'func1', parameters) + fits_logs1 = create_test_fits(('ws1', ), 'func1', parameters, output_workspace_names=('ws1', )) add_logs(fits_logs1[0].input_workspaces[0], logs[:2]) - fits_logs2 = create_test_fits(('ws2', ), 'func1', parameters) + fits_logs2 = create_test_fits(('ws2', ), 'func1', parameters, output_workspace_names=('ws2', )) add_logs(fits_logs2[0].input_workspaces[0], logs[2:]) model = ResultsTabModel(FittingContext(fits_logs1 + fits_logs2)) diff --git a/scripts/test/Muon/transformWidget_test.py b/scripts/test/Muon/transformWidget_test.py index 6b11db917a5494f72ad0bfb878118d25b562ed8b..b50cc33e2df291c7c74614632107c23175f0c6f4 100644 --- a/scripts/test/Muon/transformWidget_test.py +++ b/scripts/test/Muon/transformWidget_test.py @@ -21,7 +21,7 @@ from Muon.GUI.FrequencyDomainAnalysis.TransformSelection import transform_select class TransformTest(GuiTest): def setUp(self): - self.load = mock.create_autospec(load_utils.LoadUtils, spec_set=True) + self.load = mock.MagicMock() self.fft = mock.create_autospec(fft_presenter.FFTPresenter, spec_Set=True) self.maxent = mock.create_autospec(maxent_presenter.MaxEntPresenter, spec_set=True)