Example #1
0
    def test_that_enable_observer_calls_on_view_when_triggered(self):
        presenter = ResultsTabPresenter(self.mock_view, self.mock_model)
        enable_notifier = GenericObservable()
        enable_notifier.add_subscriber(presenter.enable_tab_observer)

        enable_notifier._notify_subscribers_impl(arg=None)
        self.mock_view.setEnabled.assert_called_once_with(True)
    def test_that_disable_observer_calls_on_view_when_triggered(self):
        disable_notifier = GenericObservable()
        disable_notifier.add_subscriber(self.presenter.disable_tab_observer)
        self.view.setEnabled = mock.MagicMock()

        disable_notifier.notify_subscribers()
        self.view.setEnabled.assert_called_once_with(False)
    def test_that_enable_observer_will_disable_the_view_when_there_is_zero_datasets(self):
        mock_model_number_of_datasets = mock.PropertyMock(return_value=0)
        type(self.model).number_of_datasets = mock_model_number_of_datasets

        enable_notifier = GenericObservable()
        enable_notifier.add_subscriber(self.presenter.enable_tab_observer)
        self.view.setEnabled = mock.MagicMock()

        enable_notifier.notify_subscribers()
        mock_model_number_of_datasets.assert_called_once_with()
        self.view.setEnabled.assert_called_once_with(False)
    def test_that_enable_observer_calls_on_view_when_triggered(self):
        self.view.setEnabled(True)
        self.view.disable_widget()

        for widget in self.view.children():
            if str(widget.objectName()) in ['cancel_calculate_phase_table_button']:
                continue
            self.assertFalse(widget.isEnabled())

        enable_notifier = GenericObservable()
        enable_notifier.add_subscriber(self.presenter.enable_tab_observer)

        enable_notifier.notify_subscribers()
        for widget in self.view.children():
            if str(widget.objectName()) in ['cancel_calculate_phase_table_button']:
                continue
            self.assertTrue(widget.isEnabled())
Example #5
0
class FocusPresenter(object):
    def __init__(self, model, view):
        self.model = model
        self.view = view
        self.worker = None
        self.calibration_observer = CalibrationObserver(self)

        # Observable Setup
        self.focus_run_notifier = GenericObservable()

        # Connect view signals to local methods.
        self.view.set_on_focus_clicked(self.on_focus_clicked)
        self.view.set_enable_controls_connection(
            self.set_focus_controls_enabled)

        # Variables from other GUI tabs.
        self.current_calibration = CalibrationInfo()
        self.instrument = "ENGINX"
        self.rb_num = None

        last_van_path = get_setting(output_settings.INTERFACES_SETTINGS_GROUP,
                                    output_settings.ENGINEERING_PREFIX,
                                    "last_vanadium_run")
        if last_van_path:
            self.view.set_van_file_text_with_search(last_van_path)

    def add_focus_subscriber(self, obs):
        self.focus_run_notifier.add_subscriber(obs)

    def on_focus_clicked(self):
        if not self._validate():
            return
        regions_dict = self.current_calibration.create_focus_roi_dictionary()
        focus_paths = self.view.get_focus_filenames()
        van_path = self.view.get_vanadium_filename()
        if self._number_of_files_warning(focus_paths):
            self.start_focus_worker(focus_paths, van_path,
                                    self.view.get_plot_output(), self.rb_num,
                                    regions_dict)
        van_run = self.view.get_vanadium_run()
        set_setting(output_settings.INTERFACES_SETTINGS_GROUP,
                    output_settings.ENGINEERING_PREFIX, "last_vanadium_run",
                    van_run)

    def start_focus_worker(self, focus_paths: list, van_path: str,
                           plot_output: bool, rb_num: str,
                           regions_dict: dict) -> None:
        """
        Focus data in a separate thread to stop the main GUI from hanging.
        :param focus_paths: List of paths to the files containing the data to focus.
        :param plot_output: True if the output should be plotted.
        :param rb_num: The RB Number from the main window (often an experiment id)
        :param regions_dict: Dictionary containing the regions to focus over, mapping region_name -> grouping_ws_name
        """
        self.worker = AsyncTask(self.model.focus_run,
                                (focus_paths, van_path, plot_output,
                                 self.instrument, rb_num, regions_dict),
                                error_cb=self._on_worker_error,
                                finished_cb=self._on_worker_success)
        self.set_focus_controls_enabled(False)
        self.worker.start()

    def _on_worker_success(self):
        self.emit_enable_button_signal()
        self.focus_run_notifier.notify_subscribers(
            self.model.get_last_focused_files())

    def set_instrument_override(self, instrument):
        instrument = INSTRUMENT_DICT[instrument]
        self.view.set_instrument_override(instrument)
        self.instrument = instrument

    def set_rb_num(self, rb_num):
        self.rb_num = rb_num

    def _validate(self):
        """
        Ensure that the worker is ready to be started.
        :return: True if the worker can be started safely.
        """
        if self.view.is_searching():
            create_error_message(
                self.view, "Mantid is searching for data files. Please wait.")
            return False
        if not self.view.get_focus_valid():
            create_error_message(self.view, "Check run numbers/path is valid.")
            return False
        if not self.view.get_vanadium_valid():
            create_error_message(self.view,
                                 "Check vanadium run number/path is valid.")
            return False
        if not self.current_calibration.is_valid():
            create_error_message(
                self.view,
                "Create or Load a calibration via the Calibration tab before focusing."
            )
            return False
        if self.current_calibration.get_instrument() != self.instrument:
            create_error_message(
                self.view,
                "Please make sure the selected instrument matches instrument for the current calibration.\n"
                "The instrument for the current calibration is: " +
                self.current_calibration.get_instrument())
            return False
        return True

    def _number_of_files_warning(self, paths):
        if len(
                paths
        ) > 10:  # Just a guess on the warning for now. May change in future.
            response = QMessageBox.warning(
                self.view, 'Engineering Diffraction - Warning',
                'You are attempting to focus {} workspaces. This may take some time.\n\n Would you like to continue?'
                .format(len(paths)), QMessageBox.Ok | QMessageBox.Cancel)
            return response == QMessageBox.Ok
        else:
            return True

    def _on_worker_error(self, error_info):
        logger.error(str(error_info))
        self.emit_enable_button_signal()

    def set_focus_controls_enabled(self, enabled):
        self.view.set_focus_button_enabled(enabled)
        self.view.set_plot_output_enabled(enabled)

    def emit_enable_button_signal(self):
        self.view.sig_enable_controls.emit(True)

    def update_calibration(self, calibration):
        """
        Update the current calibration following an call from a CalibrationNotifier
        :param calibration: The new current calibration.
        """
        self.current_calibration = calibration
        region_text = calibration.get_roi_text()
        self.view.set_region_display_text(region_text)
Example #6
0
class FrequencyAnalysisGui(QtWidgets.QMainWindow):
    """
    The Frequency Domain Analaysis 2.0 interface.
    """
    @staticmethod
    def warning_popup(message):
        message_box.warning(str(message))

    def __init__(self, parent=None, window_flags=None):
        super(FrequencyAnalysisGui, self).__init__(parent)
        if window_flags:
            self.setWindowFlags(window_flags)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.setFocusPolicy(QtCore.Qt.StrongFocus)

        try:
            check_facility()
        except AttributeError as error:
            self.warning_popup(error.args[0])
        # load the feature flags
        feature_dict = load_features()

        # initialise the data storing classes of the interface
        self.loaded_data = MuonLoadData()
        self.data_context = MuonDataContext('Frequency Domain Data',
                                            self.loaded_data)
        self.gui_context = MuonGuiContext()
        self.plot_panes_context = PlotPanesContext()
        self.group_pair_context = MuonGroupPairContext(
            self.data_context.check_group_contains_valid_detectors)
        self.corrections_context = CorrectionsContext(self.loaded_data)
        self.phase_context = PhaseTableContext()
        self.fitting_context = BasicFittingContext(
            allow_double_pulse_fitting=True)
        self.results_context = ResultsContext()
        self.model_fitting_context = ModelFittingContext(
            allow_double_pulse_fitting=False)

        self.frequency_context = FrequencyContext()

        self.context = FrequencyDomainAnalysisContext(
            muon_data_context=self.data_context,
            muon_gui_context=self.gui_context,
            muon_group_context=self.group_pair_context,
            corrections_context=self.corrections_context,
            muon_phase_context=self.phase_context,
            plot_panes_context=self.plot_panes_context,
            fitting_context=self.fitting_context,
            results_context=self.results_context,
            model_fitting_context=self.model_fitting_context,
            frequency_context=self.frequency_context)

        # create the dockable widget
        self.plot_widget = FrequencyAnalysisPlotWidget(self.context,
                                                       parent=self)

        self.dockable_plot_widget_window = PlottingDockWidget(
            parent=self, plotting_widget=self.plot_widget.view)
        self.dockable_plot_widget_window.setMinimumWidth(575)

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

        # construct all the widgets.
        self.load_widget = LoadWidget(self.loaded_data, self.context, self)
        self.grouping_tab_widget = GroupingTabWidget(self.context, parent)
        self.corrections_tab = CorrectionsTabWidget(self.context, self)
        self.home_tab = HomeTabWidget(self.context, self)
        self.phase_tab = PhaseTabWidget(self.context, self)
        self.transform = TransformWidget(self.context,
                                         FFTWidget,
                                         MaxEntWidget,
                                         parent=self)
        self.fitting_tab = FittingTabWidget(self.context, self)
        self.seq_fitting_tab = SeqFittingTabWidget(
            self.context, self.fitting_tab.fitting_tab_model, self)
        self.results_tab = ResultsTabWidget(self.context.fitting_context,
                                            self.context, self)

        self.add_model_analysis = AddModelAnalysis(self, feature_dict)
        self.add_raw_plots = AddRawPlots(self, feature_dict)
        self.add_fitting = AddFitting(self, feature_dict)

        setup_group_ws = AddGroupingWorkspaces(self, feature_dict)

        self.setup_tabs()
        self.plot_widget.insert_plot_panes()

        self.help_widget = HelpWidget(self.context.window_title)

        central_widget = QtWidgets.QWidget()
        vertical_layout = QtWidgets.QVBoxLayout()

        vertical_layout.addWidget(self.load_widget.load_widget_view)
        vertical_layout.addWidget(self.tabs)
        vertical_layout.addWidget(self.help_widget.view)
        central_widget.setLayout(vertical_layout)
        central_widget.setSizePolicy(
            QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum,
                                  QtWidgets.QSizePolicy.Maximum))
        self.disable_notifier = GenericObservable()
        self.disable_observer = GenericObserver(
            self.disable_notifier.notify_subscribers)
        self.enable_notifier = GenericObservable()
        self.enable_observer = GenericObserver(
            self.enable_notifier.notify_subscribers)
        self.setup_disable_notifier()
        self.setup_enable_notifier()

        self.setCentralWidget(central_widget)
        self.setWindowTitle(self.context.window_title)

        self.setup_load_observers()

        self.setup_gui_variable_observers()

        self.setup_grouping_changed_observers()

        self.setup_corrections_changed_observers()

        self.setup_instrument_changed_notifier()

        self.setup_group_calculation_enable_notifier()

        self.setup_group_calculation_disabler_notifier()

        self.setup_on_load_enabler()

        self.setup_on_load_disabler()

        self.setup_phase_quad_changed_notifier()

        self.setup_phase_table_changed_notifier()
        self.setup_fitting_notifier()

        self.setup_counts_calculation_finished_notifier()

        self.setup_asymmetry_pair_and_diff_calculations_finished_notifier()

        self.setup_transform()

        self.context.data_context.message_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.message_observer)

        self.add_model_analysis.add_observers_to_feature(self)
        self.add_model_analysis.set_feature_observables(self)

        self.add_raw_plots.add_observers_to_feature(self)
        setup_group_ws.add_observers_to_feature(self)

    def setup_transform(self):
        self.transform.set_up_calculation_observers(
            self.fitting_tab.fitting_tab_view.enable_tab_observer,
            self.fitting_tab.fitting_tab_view.disable_tab_observer)
        self.transform.new_data_observer(self.transform_finished_observer)
        self.transform._maxent._presenter.calculation_finished_notifier.add_subscriber(
            self.plot_widget.maxent_mode.new_data_observer)
        self.transform._maxent._presenter.new_reconstructed_data.add_subscriber(
            self.plot_widget.maxent_mode.reconstructed_data_observer)
        self.transform._maxent._presenter.method_changed.add_subscriber(
            self.plot_widget.maxent_mode.method_changed)
        self.transform._maxent._presenter.period_changed.add_subscriber(
            self.plot_widget.maxent_mode.period_changed)
        self.context.data_context.instrumentNotifier.add_subscriber(
            self.plot_widget.maxent_mode.instrument_observer)
        self.update_fits_observer = GenericObserver(self.handle_units_changed)
        self.plot_widget.update_freq_units_add_subscriber(
            self.update_fits_observer)

    def setup_tabs(self):
        """
        Set up the tabbing structure; the tabs work similarly to conventional
        web browsers.
        """
        self.tabs = DetachableTabWidget(self)
        self.tabs.addTabWithOrder(self.home_tab.home_tab_view, 'Home')
        self.tabs.addTabWithOrder(self.grouping_tab_widget.group_tab_view,
                                  'Grouping')
        self.tabs.addTabWithOrder(self.corrections_tab.corrections_tab_view,
                                  "Corrections")
        self.tabs.addTabWithOrder(self.phase_tab.phase_table_view,
                                  'Phase Table')
        self.tabs.addTabWithOrder(self.transform.widget, 'Transform')
        self.tabs.addTabWithOrder(self.fitting_tab.fitting_tab_view, 'Fitting')
        self.tabs.addTabWithOrder(self.seq_fitting_tab.seq_fitting_tab_view,
                                  'Sequential Fitting')
        self.tabs.addTabWithOrder(self.results_tab.results_tab_view, 'Results')
        self.add_model_analysis.add_to_tab(self)

        self.transform_finished_observer = GenericObserverWithArgPassing(
            self.handle_transform_performed)
        self.tabs.set_slot_for_tab_changed(self.handle_tab_changed)
        self.tabs.setElideMode(QtCore.Qt.ElideNone)
        self.tabs.setUsesScrollButtons(False)

    def handle_tab_changed(self):
        index = self.tabs.currentIndex()
        if TAB_ORDER[index] in [
                "Home", "Grouping", "Corrections", "Phase Table"
        ]:  # Plot all the selected data
            plot_mode = self.plot_widget.data_index
        elif TAB_ORDER[index] in [
                "Fitting", "Sequential Fitting", "Transform"
        ]:  # Plot the displayed workspace
            plot_mode = self.plot_widget.fit_index
        elif TAB_ORDER[index] in ["Model Fitting"]:
            plot_mode = self.plot_widget.model_fit_index
        else:
            return

        self.plot_widget.set_plot_view(plot_mode)

    def handle_transform_performed(self, new_data_workspace_name):
        self.update_fit_ws_list()
        self.fitting_tab.fitting_tab_presenter.set_selected_dataset(
            new_data_workspace_name)

    def handle_units_changed(self):
        old_name = self.fitting_tab.fitting_tab_presenter.current_dataset()
        if old_name == "":
            return
        self.update_fit_ws_list()
        new_name = self.frequency_context.switch_units_in_name(old_name)
        self.fitting_tab.fitting_tab_presenter.set_selected_dataset(new_name)
        x_lim = self.frequency_context.range()
        self.fitting_tab.fitting_tab_presenter.update_start_and_end_x_in_view_and_model(
            x_lim[0], x_lim[1])

    def update_fit_ws_list(self):
        self.fitting_tab.fitting_tab_presenter.handle_new_data_loaded()
        self.seq_fitting_tab.seq_fitting_tab_presenter.handle_selected_workspaces_changed(
        )

    def set_tab_warning(self, tab_name: str, message: str):
        """Sets a warning message as the tooltip of the provided tab."""
        self.tabs.set_tab_warning(TAB_ORDER.index(tab_name), message)

    def setup_disable_notifier(self):

        self.disable_notifier.add_subscriber(
            self.home_tab.home_tab_widget.disable_observer)

        self.disable_notifier.add_subscriber(
            self.load_widget.load_widget.disable_observer)

        self.disable_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.disable_tab_observer)

        self.disable_notifier.add_subscriber(
            self.corrections_tab.corrections_tab_view.disable_tab_observer)

        self.disable_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_view.disable_tab_observer)

        self.disable_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.disable_tab_observer
        )

        self.disable_notifier.add_subscriber(
            self.phase_tab.phase_table_presenter.disable_tab_observer)

        self.disable_notifier.add_subscriber(
            self.results_tab.results_tab_presenter.disable_tab_observer)

        self.disable_notifier.add_subscriber(self.transform.disable_observer)

    def setup_enable_notifier(self):

        self.enable_notifier.add_subscriber(
            self.home_tab.home_tab_widget.enable_observer)

        self.enable_notifier.add_subscriber(
            self.load_widget.load_widget.enable_observer)

        self.enable_notifier.add_subscriber(
            self.corrections_tab.corrections_tab_view.enable_tab_observer)

        self.enable_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_view.enable_tab_observer)

        self.enable_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.enable_tab_observer)

        self.enable_notifier.add_subscriber(
            self.phase_tab.phase_table_presenter.enable_tab_observer)

        self.enable_notifier.add_subscriber(
            self.results_tab.results_tab_presenter.enable_tab_observer)

        self.enable_notifier.add_subscriber(self.transform.enable_observer)

        self.enable_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.enable_tab_observer)

    def setup_load_observers(self):
        self.load_widget.load_widget.loadNotifier.add_subscriber(
            self.home_tab.home_tab_widget.loadObserver)

        self.load_widget.load_widget.loadNotifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.loadObserver)

        self.load_widget.load_widget.loadNotifier.add_subscriber(
            self.corrections_tab.corrections_tab_presenter.load_observer)

        self.load_widget.load_widget.loadNotifier.add_subscriber(
            self.phase_tab.phase_table_presenter.run_change_observer)

        self.load_widget.load_widget.loadNotifier.add_subscriber(
            self.fitting_tab.fitting_tab_view.disable_tab_observer)

        self.load_widget.load_widget.loadNotifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.disable_tab_observer
        )

    def setup_gui_variable_observers(self):
        self.context.gui_context.gui_variables_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.gui_variables_observer
        )

        for observer in self.plot_widget.rebin_options_set_observers:
            self.context.gui_context.gui_variables_notifier.add_subscriber(
                observer)

        self.grouping_tab_widget.pairing_table_widget.selected_pair_changed_notifier.add_subscriber(
            self.plot_widget.data_mode.added_group_or_pair_observer)

        self.grouping_tab_widget.pairing_table_widget.selected_pair_changed_notifier.add_subscriber(
            self.transform.GroupPairObserver)

        self.grouping_tab_widget.grouping_table_widget.selected_group_changed_notifier.add_subscriber(
            self.transform.GroupPairObserver)

        self.grouping_tab_widget.grouping_table_widget.selected_group_changed_notifier.add_subscriber(
            self.plot_widget.data_mode.added_group_or_pair_observer)

        # differences
        self.grouping_tab_widget.diff_table.add_subscribers([
            self.transform.GroupPairObserver,
            self.plot_widget.data_mode.added_group_or_pair_observer
        ])

        self.fitting_tab.fitting_tab_presenter.fit_function_changed_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.
            fit_function_updated_observer)

        self.fitting_tab.fitting_tab_presenter.fit_parameter_changed_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.
            fit_parameter_updated_observer)

        self.seq_fitting_tab.seq_fitting_tab_presenter.fit_parameter_changed_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.
            fit_parameter_updated_observer)

        self.seq_fitting_tab.seq_fitting_tab_presenter.sequential_fit_finished_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.
            sequential_fit_finished_observer)

        self.fitting_tab.fitting_tab_presenter.selected_fit_results_changed.add_subscriber(
            self.plot_widget.fit_mode.plot_selected_fit_observer)

        self.seq_fitting_tab.seq_fitting_tab_presenter.selected_sequential_fit_notifier.add_subscriber(
            self.plot_widget.fit_mode.plot_selected_fit_observer)

        self.phase_tab.phase_table_presenter.selected_phasequad_changed_notifier.add_subscriber(
            self.plot_widget.data_mode.added_group_or_pair_observer)

        self.phase_tab.phase_table_presenter.selected_phasequad_changed_notifier.add_subscriber(
            self.transform.GroupPairObserver)

        self.phase_tab.phase_table_presenter.selected_phasequad_changed_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.
            selected_workspaces_observer)

    def setup_grouping_changed_observers(self):
        self.grouping_tab_widget.group_tab_presenter.groupingNotifier.add_subscriber(
            self.home_tab.home_tab_widget.groupingObserver)

        self.grouping_tab_widget.group_tab_presenter.groupingNotifier.add_subscriber(
            self.transform.GroupPairObserver)

        self.grouping_tab_widget.group_tab_presenter.groupingNotifier.add_subscriber(
            self.phase_tab.phase_table_presenter.group_change_observer)

    def setup_corrections_changed_observers(self):
        self.corrections_tab.corrections_tab_presenter.perform_corrections_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.gui_variables_observer
        )

    def setup_instrument_changed_notifier(self):
        self.context.data_context.instrumentNotifier.add_subscriber(
            self.home_tab.home_tab_widget.instrumentObserver)

        self.context.data_context.instrumentNotifier.add_subscriber(
            self.load_widget.load_widget.instrumentObserver)

        self.context.data_context.instrumentNotifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.instrumentObserver)

        self.context.data_context.instrumentNotifier.add_subscriber(
            self.corrections_tab.corrections_tab_presenter.
            instrument_changed_observer)

        self.context.data_context.instrumentNotifier.add_subscriber(
            self.transform.instrumentObserver)

        self.context.data_context.instrumentNotifier.add_subscriber(
            self.phase_tab.phase_table_presenter.instrument_changed_observer)

        self.context.data_context.instrumentNotifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.instrument_changed_observer)

        for observer in self.plot_widget.clear_plot_observers:
            self.context.data_context.instrumentNotifier.add_subscriber(
                observer)

    def setup_group_calculation_enable_notifier(self):
        self.grouping_tab_widget.group_tab_presenter.enable_editing_notifier.add_subscriber(
            self.enable_observer)

        self.corrections_tab.corrections_tab_presenter.enable_editing_notifier.add_subscriber(
            self.enable_observer)

        self.fitting_tab.fitting_tab_presenter.enable_editing_notifier.add_subscriber(
            self.enable_observer)

        self.phase_tab.phase_table_presenter.enable_editing_notifier.add_subscriber(
            self.enable_observer)

    def setup_group_calculation_disabler_notifier(self):
        self.grouping_tab_widget.group_tab_presenter.disable_editing_notifier.add_subscriber(
            self.disable_observer)

        self.corrections_tab.corrections_tab_presenter.disable_editing_notifier.add_subscriber(
            self.disable_observer)

        self.fitting_tab.fitting_tab_presenter.disable_editing_notifier.add_subscriber(
            self.disable_observer)

        self.phase_tab.phase_table_presenter.disable_editing_notifier.add_subscriber(
            self.disable_observer)

    def setup_on_load_enabler(self):
        self.load_widget.load_widget.load_run_widget.enable_notifier.add_subscriber(
            self.home_tab.home_tab_widget.enable_observer)

        self.load_widget.load_widget.load_run_widget.enable_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.enable_observer)

        self.load_widget.load_widget.load_run_widget.enable_notifier.add_subscriber(
            self.transform.enable_observer)

    def setup_on_load_disabler(self):
        self.load_widget.load_widget.load_run_widget.disable_notifier.add_subscriber(
            self.home_tab.home_tab_widget.disable_observer)

        self.load_widget.load_widget.load_run_widget.disable_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.disable_observer)

        self.load_widget.load_widget.load_run_widget.disable_notifier.add_subscriber(
            self.transform.disable_observer)

    def setup_counts_calculation_finished_notifier(self):
        self.grouping_tab_widget.group_tab_presenter.counts_calculation_finished_notifier.add_subscriber(
            self.corrections_tab.corrections_tab_presenter.
            pre_process_and_counts_calculated_observer)

    def setup_asymmetry_pair_and_diff_calculations_finished_notifier(self):
        for observer in self.plot_widget.data_changed_observers:
            self.corrections_tab.corrections_tab_presenter.asymmetry_pair_and_diff_calculations_finished_notifier.\
                add_subscriber(observer)
            self.phase_tab.phase_table_presenter.calculation_finished_notifier.add_subscriber(
                observer)

        self.corrections_tab.corrections_tab_presenter.asymmetry_pair_and_diff_calculations_finished_notifier.add_subscriber(
            self.transform.load_observer)

        self.corrections_tab.corrections_tab_presenter.asymmetry_pair_and_diff_calculations_finished_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.
            selected_workspaces_observer)

    def setup_phase_quad_changed_notifier(self):
        self.phase_tab.phase_table_presenter.phasequad_calculation_complete_notifier.add_subscriber(
            self.transform.phase_quad_observer)

    def setup_phase_table_changed_notifier(self):
        self.phase_tab.phase_table_presenter.phase_table_calculation_complete_notifier.add_subscriber(
            self.transform._maxent._presenter.phase_table_observer)
        self.transform._maxent._presenter.new_phase_table.add_subscriber(
            self.phase_tab.phase_table_presenter.phase_table_observer)

    def setup_fitting_notifier(self):
        """Connect fitting and results tabs to inform of new fits"""
        self.fitting_context.new_fit_results_notifier.add_subscriber(
            self.results_tab.results_tab_presenter.new_fit_performed_observer)

        self.fitting_tab.fitting_tab_presenter.remove_plot_guess_notifier.add_subscriber(
            self.plot_widget.fit_mode.remove_plot_guess_observer)

        self.fitting_tab.fitting_tab_presenter.update_plot_guess_notifier.add_subscriber(
            self.plot_widget.fit_mode.update_plot_guess_observer)

    def closeEvent(self, event):
        self.tabs.closeEvent(event)
        self.context.ads_observer.unsubscribe()
        self.grouping_tab_widget.group_tab_presenter.closePeriodInfoWidget()
        super(FrequencyAnalysisGui, self).closeEvent(event)
Example #7
0
class MuonAnalysisGui(QtWidgets.QMainWindow):
    """
    The Muon Analysis 2.0 interface.
    """
    @staticmethod
    def warning_popup(message):
        message_box.warning(str(message))

    def __init__(self, parent=None):
        super(MuonAnalysisGui, self).__init__(parent)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.setObjectName("MuonAnalysis2")
        self.current_tab = ''

        try:
            check_facility()
        except AttributeError as error:
            self.warning_popup(error.args[0])

        # initialise the data storing classes of the interface
        self.loaded_data = MuonLoadData()
        self.data_context = MuonDataContext('Muon Data', self.loaded_data)
        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 = DataAnalysisContext(
            muon_data_context=self.data_context,
            muon_gui_context=self.gui_context,
            muon_group_context=self.group_pair_context,
            fitting_context=self.fitting_context,
            muon_phase_context=self.phase_context)

        # create the Dockable plot widget
        self.fitting_tab = FittingTabWidget(self.context, self)
        self.plot_widget = PlotWidget(
            self.context,
            self.fitting_tab.fitting_tab_presenter.get_selected_fit_workspaces,
            parent=self)
        self.dockable_plot_widget_window = PlottingDockWidget(
            parent=self, plotting_widget=self.plot_widget.view)
        self.dockable_plot_widget_window.setMinimumWidth(575)

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

        self.disable_notifier = GenericObservable()
        self.disable_observer = GenericObserver(
            self.disable_notifier.notify_subscribers)
        self.enable_notifier = GenericObservable()
        self.enable_observer = GenericObserver(
            self.enable_notifier.notify_subscribers)

        # set up other widgets
        self.load_widget = LoadWidget(self.loaded_data, self.context, self)
        self.home_tab = HomeTabWidget(self.context, self)
        self.grouping_tab_widget = GroupingTabWidget(self.context)
        self.phase_tab = PhaseTabWidget(self.context, self)
        self.seq_fitting_tab = SeqFittingTabWidget(
            self.context, self.fitting_tab.fitting_tab_model, self)
        self.results_tab = ResultsTabWidget(self.context.fitting_context,
                                            self.context, self)

        self.setup_tabs()
        self.help_widget = HelpWidget("Muon Analysis 2")

        central_widget = QtWidgets.QWidget()
        vertical_layout = QtWidgets.QVBoxLayout()
        vertical_layout.addWidget(self.load_widget.load_widget_view)
        vertical_layout.addWidget(self.tabs)
        vertical_layout.addWidget(self.help_widget.view)
        central_widget.setLayout(vertical_layout)

        self.setCentralWidget(central_widget)
        self.setWindowTitle(self.context.window_title)

        self.setup_load_observers()

        self.setup_gui_variable_observers()

        self.setup_grouping_changed_observers()

        self.setup_instrument_changed_notifier()

        self.setup_group_calculation_enable_notifier()

        self.setup_group_calculation_disabler_notifier()

        self.setup_on_load_enabler()

        self.setup_on_load_disabler()

        self.setup_phase_quad_changed_notifier()

        self.setup_phase_table_changed_notifier()

        self.setup_fitting_notifier()

        self.setup_on_recalulation_finished_notifier()

        self.context.data_context.message_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.message_observer)

        self.setup_disable_notifier()

        self.setup_enable_notifier()

    def setup_tabs(self):
        """
        Set up the tabbing structure; the tabs work similarly to conventional
        web browsers.
        """
        self.tabs = DetachableTabWidget(self)
        self.tabs.addTabWithOrder(self.home_tab.home_tab_view, 'Home')
        self.tabs.addTabWithOrder(self.grouping_tab_widget.group_tab_view,
                                  'Grouping')
        self.tabs.addTabWithOrder(self.phase_tab.phase_table_view,
                                  'Phase Table')
        self.tabs.addTabWithOrder(self.fitting_tab.fitting_tab_view, 'Fitting')
        self.tabs.addTabWithOrder(self.seq_fitting_tab.seq_fitting_tab_view,
                                  'Sequential Fitting')
        self.tabs.addTabWithOrder(self.results_tab.results_tab_view, 'Results')
        self.update_plot_observer = GenericObserver(
            self.plot_widget.presenter.update_plot)
        self.tabs.set_slot_for_tab_changed(self.handle_tab_changed)

    def handle_tab_changed(self):
        index = self.tabs.currentIndex()
        if TAB_ORDER[index] in ["Home", "Grouping",
                                "Phase Table"]:  # Plot all the selected data
            plot_mode = PlotMode.Data
        elif TAB_ORDER[index] in ["Fitting", "Sequential Fitting"
                                  ]:  # Plot the displayed workspace
            plot_mode = PlotMode.Fitting
        else:
            return

        self.plot_widget.presenter.handle_plot_mode_changed(plot_mode)

    def setup_disable_notifier(self):

        self.disable_notifier.add_subscriber(
            self.home_tab.home_tab_widget.disable_observer)

        self.disable_notifier.add_subscriber(
            self.load_widget.load_widget.disable_observer)

        self.disable_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.disable_tab_observer)

        self.disable_notifier.add_subscriber(
            self.phase_tab.phase_table_presenter.disable_tab_observer)

        self.disable_notifier.add_subscriber(
            self.results_tab.results_tab_presenter.disable_tab_observer)

        self.disable_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.disable_tab_observer
        )

        self.disable_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.disable_tab_observer)

    def setup_enable_notifier(self):

        self.enable_notifier.add_subscriber(
            self.home_tab.home_tab_widget.enable_observer)

        self.enable_notifier.add_subscriber(
            self.load_widget.load_widget.enable_observer)

        self.enable_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.enable_tab_observer)

        self.enable_notifier.add_subscriber(
            self.phase_tab.phase_table_presenter.enable_tab_observer)

        self.enable_notifier.add_subscriber(
            self.results_tab.results_tab_presenter.enable_tab_observer)

        self.enable_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.enable_tab_observer)

        self.enable_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.enable_tab_observer)

    def setup_load_observers(self):
        self.load_widget.load_widget.loadNotifier.add_subscriber(
            self.home_tab.home_tab_widget.loadObserver)

        self.load_widget.load_widget.loadNotifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.loadObserver)

        self.load_widget.load_widget.loadNotifier.add_subscriber(
            self.phase_tab.phase_table_presenter.run_change_observer)

    def setup_gui_variable_observers(self):
        self.context.gui_context.gui_variables_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.gui_variables_observer
        )

        self.context.gui_context.gui_variables_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.gui_context_observer)

        self.context.gui_context.gui_variable_non_calulation_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.gui_context_observer)

        self.grouping_tab_widget.pairing_table_widget.selected_pair_changed_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.selected_group_pair_observer
        )

        self.grouping_tab_widget.pairing_table_widget.selected_pair_changed_notifier.add_subscriber(
            self.plot_widget.presenter.added_group_or_pair_observer)

        self.grouping_tab_widget.grouping_table_widget.selected_group_changed_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.selected_group_pair_observer
        )

        self.grouping_tab_widget.grouping_table_widget.selected_group_changed_notifier.add_subscriber(
            self.plot_widget.presenter.added_group_or_pair_observer)

        self.grouping_tab_widget.pairing_table_widget.selected_pair_changed_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.
            selected_workspaces_observer)

        self.grouping_tab_widget.grouping_table_widget.selected_group_changed_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.
            selected_workspaces_observer)

        self.fitting_tab.fitting_tab_presenter.fit_function_changed_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.
            fit_function_updated_observer)

        self.fitting_tab.fitting_tab_presenter.fit_parameter_changed_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.
            fit_parameter_updated_observer)

        self.fitting_tab.fitting_tab_presenter.fit_type_changed_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.
            fit_type_changed_observer)

        self.fitting_tab.fitting_tab_presenter.selected_single_fit_notifier.add_subscriber(
            self.plot_widget.presenter.plot_selected_fit_observer)

        self.seq_fitting_tab.seq_fitting_tab_presenter.selected_sequential_fit_notifier.add_subscriber(
            self.plot_widget.presenter.plot_selected_fit_observer)

    def setup_grouping_changed_observers(self):
        self.grouping_tab_widget.group_tab_presenter.groupingNotifier.add_subscriber(
            self.home_tab.home_tab_widget.groupingObserver)

        self.grouping_tab_widget.group_tab_presenter.groupingNotifier.add_subscriber(
            self.phase_tab.phase_table_presenter.group_change_observer)

    def setup_instrument_changed_notifier(self):
        self.context.data_context.instrumentNotifier.add_subscriber(
            self.home_tab.home_tab_widget.instrumentObserver)

        self.context.data_context.instrumentNotifier.add_subscriber(
            self.load_widget.load_widget.instrumentObserver)

        self.context.data_context.instrumentNotifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.instrumentObserver)

        self.context.data_context.instrumentNotifier.add_subscriber(
            self.phase_tab.phase_table_presenter.instrument_changed_observer)

        self.context.data_context.instrumentNotifier.add_subscriber(
            self.plot_widget.presenter.instrument_observer)

        self.context.data_context.instrumentNotifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.instrument_changed_observer)

    def setup_group_calculation_enable_notifier(self):

        self.grouping_tab_widget.group_tab_presenter.enable_editing_notifier.add_subscriber(
            self.enable_observer)

        self.fitting_tab.fitting_tab_presenter.enable_editing_notifier.add_subscriber(
            self.enable_observer)

        self.phase_tab.phase_table_presenter.enable_editing_notifier.add_subscriber(
            self.enable_observer)

    def setup_group_calculation_disabler_notifier(self):

        self.grouping_tab_widget.group_tab_presenter.disable_editing_notifier.add_subscriber(
            self.disable_observer)

        self.fitting_tab.fitting_tab_presenter.disable_editing_notifier.add_subscriber(
            self.disable_observer)

        self.phase_tab.phase_table_presenter.disable_editing_notifier.add_subscriber(
            self.disable_observer)

    def setup_on_load_enabler(self):
        self.load_widget.load_widget.load_run_widget.enable_notifier.add_subscriber(
            self.home_tab.home_tab_widget.enable_observer)

        self.load_widget.load_widget.load_run_widget.enable_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.enable_observer)

        self.load_widget.load_widget.load_run_widget.enable_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.enable_tab_observer)

    def setup_on_load_disabler(self):
        self.load_widget.load_widget.load_run_widget.disable_notifier.add_subscriber(
            self.home_tab.home_tab_widget.disable_observer)

        self.load_widget.load_widget.load_run_widget.disable_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.disable_observer)

        self.load_widget.load_widget.load_run_widget.disable_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.disable_tab_observer)

    def setup_on_recalulation_finished_notifier(self):
        self.grouping_tab_widget.group_tab_presenter.calculation_finished_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.input_workspace_observer)

        self.grouping_tab_widget.group_tab_presenter.calculation_finished_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.
            selected_workspaces_observer)

        self.grouping_tab_widget.group_tab_presenter.calculation_finished_notifier.add_subscriber(
            self.update_plot_observer)

    def setup_phase_quad_changed_notifier(self):
        pass

    def setup_phase_table_changed_notifier(self):
        pass

    def setup_fitting_notifier(self):
        """Connect fitting and results tabs to inform of new fits"""
        self.fitting_context.new_fit_results_notifier.add_subscriber(
            self.results_tab.results_tab_presenter.new_fit_performed_observer)

        self.fitting_context.plot_guess_notifier.add_subscriber(
            self.plot_widget.presenter.plot_guess_observer)

    def closeEvent(self, event):
        self.removeDockWidget(self.dockable_plot_widget_window)
        self.tabs.closeEvent(event)
        self.context.ads_observer.unsubscribe()
        self.context.ads_observer = None
        super(MuonAnalysisGui, self).closeEvent(event)
Example #8
0
class ElementalAnalysisGui(QtWidgets.QMainWindow):
    """
    The Elemental Analysis 2.0 interface.
    """
    def warning_popup(self, message):
        message_box.warning(str(message), parent=self)

    def __init__(self, parent=None):
        super(ElementalAnalysisGui, self).__init__(parent)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.setObjectName("ElementalAnalysis2")

        # setup error notifier and observer for context and group context
        self.error_notifier = GenericObservable()
        self.error_observer = GenericObserverWithArgPassing(self.warning_popup)
        self.error_notifier.add_subscriber(self.error_observer)

        self.loaded_data = MuonLoadData()
        self.data_context = DataContext(self.loaded_data)
        self.group_context = EAGroupContext(
            self.data_context.check_group_contains_valid_detectors,
            self.error_notifier)
        self.gui_context = MuonGuiContext()
        self.plot_panes_context = PlotPanesContext()
        self.context = ElementalAnalysisContext(self.data_context,
                                                self.group_context,
                                                self.gui_context,
                                                self.plot_panes_context,
                                                self.error_notifier)
        self.current_tab = ''

        self.plot_widget = EAPlotWidget(self.context, parent=self)
        self.dockable_plot_widget_window = PlottingDockWidget(
            parent=self, plotting_widget=self.plot_widget.view)
        self.dockable_plot_widget_window.setMinimumWidth(800)

        # Add dock widget to main Elemental analysis window
        self.addDockWidget(QtCore.Qt.RightDockWidgetArea,
                           self.dockable_plot_widget_window)
        # Need this line to stop the bug where the dock window snaps back to its original size after resizing.
        # 0 argument is arbitrary and has no effect on fit widget size
        # This is a qt bug reported at (https://bugreports.qt.io/browse/QTBUG-65592)
        if QT_VERSION >= LooseVersion("5.6"):
            self.resizeDocks({self.dockable_plot_widget_window}, {1},
                             QtCore.Qt.Horizontal)
        # disable and enable notifiers
        self.disable_notifier = GenericObservable()
        self.enable_notifier = GenericObservable()

        # disable and enable observers
        self.disable_observer = GenericObserver(
            self.disable_notifier.notify_subscribers)
        self.enable_observer = GenericObserver(
            self.enable_notifier.notify_subscribers)

        self.setup_dummy()

        self.setup_tabs()
        self.help_widget = HelpWidget("Elemental Analysis")

        central_widget = QtWidgets.QWidget()
        vertical_layout = QtWidgets.QVBoxLayout()
        vertical_layout.addWidget(self.load_widget.view)
        vertical_layout.addWidget(self.tabs)
        vertical_layout.addWidget(self.help_widget.view)
        central_widget.setLayout(vertical_layout)

        self.setCentralWidget(central_widget)
        self.setWindowTitle(self.context.name)

        # setup connections between notifiers and observers
        self.setup_enable_notifier()
        self.setup_disable_notifier()
        self.setup_load_observers()
        self.setup_gui_variable_observers()
        self.setup_group_calculation_enable_notifier()
        self.setup_group_calculation_disable_notifier()
        self.setup_grouping_changed_observers()
        self.setup_update_view_notifier()
        self.setMinimumHeight(800)

    def setup_dummy(self):
        self.load_widget = LoadWidget(self.loaded_data,
                                      self.context,
                                      parent=self)
        self.home_tab = QtWidgets.QLineEdit("home")
        self.grouping_tab_widget = EAGroupingTabWidget(self.context)
        self.fitting_tab = QtWidgets.QLineEdit("fitting")
        self.auto_tab = EAAutoTabWidget(self.context)

    def setup_tabs(self):
        """
        Set up the tabbing structure; the tabs work similarly to conventional
        web browsers.
        """
        self.tabs = DetachableTabWidget(self)
        self.tabs.addTabWithOrder(self.home_tab, 'Home')
        self.tabs.addTabWithOrder(self.grouping_tab_widget.group_tab_view,
                                  'Grouping')
        self.tabs.addTabWithOrder(self.auto_tab.auto_tab_view, 'Automatic')
        self.tabs.addTabWithOrder(self.fitting_tab, 'Fitting')

    def closeEvent(self, event):
        self.removeDockWidget(self.dockable_plot_widget_window)
        self.context.ads_observer.unsubscribe()
        self.context.ads_observer = None
        self.tabs.closeEvent(event)
        super(ElementalAnalysisGui, self).closeEvent(event)

    def setup_disable_notifier(self):
        self.disable_notifier.add_subscriber(
            self.load_widget.load_widget.disable_observer)

        self.disable_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.disable_tab_observer)

        self.disable_notifier.add_subscriber(
            self.grouping_tab_widget.grouping_table_view.disable_table_observer
        )

        self.disable_notifier.add_subscriber(
            self.auto_tab.auto_tab_presenter.disable_tab_observer)

    def setup_enable_notifier(self):
        self.enable_notifier.add_subscriber(
            self.load_widget.load_widget.enable_observer)

        self.enable_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.enable_tab_observer)

        self.enable_notifier.add_subscriber(
            self.grouping_tab_widget.grouping_table_view.enable_table_observer)

        self.enable_notifier.add_subscriber(
            self.auto_tab.auto_tab_presenter.enable_tab_observer)

    def setup_load_observers(self):
        self.load_widget.load_widget.loadNotifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.loadObserver)
        self.load_widget.load_widget.loadNotifier.add_subscriber(
            self.auto_tab.auto_tab_presenter.update_view_observer)

    def setup_gui_variable_observers(self):
        self.context.gui_context.gui_variables_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.gui_variables_observer
        )

    def setup_grouping_changed_observers(self):
        self.grouping_tab_widget.grouping_table_widget.data_changed_notifier.add_subscriber(
            self.auto_tab.auto_tab_presenter.group_change_observer)

        for observer in self.plot_widget.data_changed_observers:
            self.grouping_tab_widget.grouping_table_widget.selected_group_changed_notifier.add_subscriber(
                observer)

        for observer in self.plot_widget.workspace_deleted_from_ads_observers:
            self.context.deleted_plots_notifier.add_subscriber(observer)

    def setup_on_load_enabler(self):
        self.load_widget.load_widget.load_run_widget.enable_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.enable_observer)

    def setup_on_load_disabler(self):
        self.load_widget.load_widget.load_run_widget.disable_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.disable_observer)

    def setup_group_calculation_enable_notifier(self):
        self.load_widget.run_widget.enable_notifier.add_subscriber(
            self.enable_observer)

        self.grouping_tab_widget.group_tab_presenter.enable_editing_notifier.add_subscriber(
            self.enable_observer)

        self.context.calculation_finished_notifier.add_subscriber(
            self.enable_observer)

        self.auto_tab.auto_tab_presenter.model.calculation_finished_notifier.add_subscriber(
            self.enable_observer)

    def setup_group_calculation_disable_notifier(self):
        self.load_widget.run_widget.disable_notifier.add_subscriber(
            self.disable_observer)

        self.grouping_tab_widget.group_tab_presenter.disable_editing_notifier.add_subscriber(
            self.disable_observer)

        self.context.calculation_started_notifier.add_subscriber(
            self.disable_observer)

        self.auto_tab.auto_tab_presenter.model.calculation_started_notifier.add_subscriber(
            self.disable_observer)

    def setup_update_view_notifier(self):
        self.context.update_view_from_model_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.
            update_view_from_model_observer)
        self.context.update_view_from_model_notifier.add_subscriber(
            self.auto_tab.auto_tab_presenter.update_view_observer)
        self.context.update_view_from_model_notifier.add_subscriber(
            self.load_widget.load_widget.update_view_from_model_observer)
Example #9
0
class MuonAnalysisGui(QtWidgets.QMainWindow):
    """
    The Muon Analysis 2.0 interface.
    """
    @staticmethod
    def warning_popup(message):
        message_box.warning(str(message))

    def __init__(self, parent=None, window_flags=None):
        super(MuonAnalysisGui, self).__init__(parent)
        if window_flags:
            self.setWindowFlags(window_flags)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.setObjectName("MuonAnalysis2")
        self.current_tab = ''

        try:
            check_facility()
        except AttributeError as error:
            self.warning_popup(error.args[0])

        # initialise the data storing classes of the interface
        self.loaded_data = MuonLoadData()
        self.data_context = MuonDataContext('Muon Data', self.loaded_data)
        self.gui_context = MuonGuiContext()
        self.group_pair_context = MuonGroupPairContext(
            self.data_context.check_group_contains_valid_detectors)
        self.corrections_context = CorrectionsContext(self.loaded_data)
        self.phase_context = PhaseTableContext()
        self.fitting_context = TFAsymmetryFittingContext(
            allow_double_pulse_fitting=True)
        self.results_context = ResultsContext()
        self.model_fitting_context = ModelFittingContext(
            allow_double_pulse_fitting=False)
        self.plot_panes_context = PlotPanesContext()
        self.context = DataAnalysisContext(
            muon_data_context=self.data_context,
            muon_gui_context=self.gui_context,
            muon_group_context=self.group_pair_context,
            corrections_context=self.corrections_context,
            fitting_context=self.fitting_context,
            results_context=self.results_context,
            model_fitting_context=self.model_fitting_context,
            muon_phase_context=self.phase_context,
            plot_panes_context=self.plot_panes_context)

        # create the Dockable plot widget
        self.fitting_tab = FittingTabWidget(self.context, self)
        self.plot_widget = MuonAnalysisPlotWidget(self.context, parent=self)
        self.dockable_plot_widget_window = PlottingDockWidget(
            parent=self, plotting_widget=self.plot_widget.view)
        self.dockable_plot_widget_window.setMinimumWidth(800)

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

        self.disable_notifier = GenericObservable()
        self.disable_observer = GenericObserver(
            self.disable_notifier.notify_subscribers)
        self.enable_notifier = GenericObservable()
        self.enable_observer = GenericObserver(
            self.enable_notifier.notify_subscribers)

        # set up other widgets
        self.load_widget = LoadWidget(self.loaded_data, self.context, self)
        self.home_tab = HomeTabWidget(self.context, self)
        self.grouping_tab_widget = GroupingTabWidget(self.context, parent)
        self.corrections_tab = CorrectionsTabWidget(self.context, self)
        self.phase_tab = PhaseTabWidget(self.context, self)
        self.seq_fitting_tab = SeqFittingTabWidget(
            self.context, self.fitting_tab.fitting_tab_model, self)
        self.results_tab = ResultsTabWidget(self.context.fitting_context,
                                            self.context, self)
        #self.model_fitting_tab = ModelFittingTabWidget(self.context, self)

        self.setup_tabs()
        self.help_widget = HelpWidget("Muon Analysis 2")

        central_widget = QtWidgets.QWidget()
        vertical_layout = QtWidgets.QVBoxLayout()
        vertical_layout.addWidget(self.load_widget.load_widget_view)
        vertical_layout.addWidget(self.tabs)
        vertical_layout.addWidget(self.help_widget.view)
        central_widget.setLayout(vertical_layout)

        self.setCentralWidget(central_widget)
        self.setWindowTitle(self.context.window_title)

        self.setup_load_observers()

        self.setup_gui_variable_observers()

        self.setup_grouping_changed_observers()

        self.setup_corrections_changed_observers()

        self.setup_instrument_changed_notifier()

        self.setup_group_calculation_enable_notifier()

        self.setup_group_calculation_disabler_notifier()

        self.setup_on_load_enabler()

        self.setup_on_load_disabler()

        self.setup_phase_quad_changed_notifier()

        self.setup_phase_table_changed_notifier()

        self.setup_fitting_notifier()

        self.setup_counts_calculation_finished_notifier()

        self.setup_asymmetry_pair_and_diff_calculations_finished_notifier()

        self.setup_results_notifier()

        self.context.data_context.message_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.message_observer)

        self.setup_disable_notifier()

        self.setup_enable_notifier()

    def setup_tabs(self):
        """
        Set up the tabbing structure; the tabs work similarly to conventional
        web browsers.
        """
        self.tabs = DetachableTabWidget(self)
        self.tabs.addTabWithOrder(self.home_tab.home_tab_view, 'Home')
        self.tabs.addTabWithOrder(self.grouping_tab_widget.group_tab_view,
                                  'Grouping')
        self.tabs.addTabWithOrder(self.corrections_tab.corrections_tab_view,
                                  "Corrections")
        self.tabs.addTabWithOrder(self.phase_tab.phase_table_view,
                                  'Phase Table')
        self.tabs.addTabWithOrder(self.fitting_tab.fitting_tab_view, 'Fitting')
        self.tabs.addTabWithOrder(self.seq_fitting_tab.seq_fitting_tab_view,
                                  'Sequential Fitting')
        self.tabs.addTabWithOrder(self.results_tab.results_tab_view, 'Results')
        #self.tabs.addTabWithOrder(self.model_fitting_tab.model_fitting_tab_view, 'Model Fitting')
        self.tabs.set_slot_for_tab_changed(self.handle_tab_changed)
        self.tabs.setElideMode(QtCore.Qt.ElideNone)
        self.tabs.setUsesScrollButtons(False)

    def handle_tab_changed(self):
        index = self.tabs.currentIndex()
        # the plot mode indicies are from the order the plots are stored
        if TAB_ORDER[index] in [
                "Home", "Grouping", "Corrections", "Phase Table"
        ]:  # Plot all the selected data
            plot_mode = self.plot_widget.data_index
        # Plot the displayed workspace
        elif TAB_ORDER[index] in ["Fitting", "Sequential Fitting"]:
            plot_mode = self.plot_widget.fit_index
        #elif TAB_ORDER[index] in ["Model Fitting"]:
        #    plot_mode = self.plot_widget.model_fit_index
        else:
            return
        self.plot_widget.set_plot_view(plot_mode)

    def set_tab_warning(self, tab_name: str, message: str):
        """Sets a warning message as the tooltip of the provided tab."""
        self.tabs.set_tab_warning(TAB_ORDER.index(tab_name), message)

    def setup_disable_notifier(self):

        self.disable_notifier.add_subscriber(
            self.home_tab.home_tab_widget.disable_observer)

        self.disable_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.disable_tab_observer)

        self.disable_notifier.add_subscriber(
            self.corrections_tab.corrections_tab_view.disable_tab_observer)

        self.disable_notifier.add_subscriber(
            self.load_widget.load_widget.disable_observer)

        self.disable_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_view.disable_tab_observer)

        #self.disable_notifier.add_subscriber(self.model_fitting_tab.model_fitting_tab_view.disable_tab_observer)

        self.disable_notifier.add_subscriber(
            self.phase_tab.phase_table_presenter.disable_tab_observer)

        self.disable_notifier.add_subscriber(
            self.results_tab.results_tab_presenter.disable_tab_observer)

        self.disable_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.disable_tab_observer
        )

    def setup_enable_notifier(self):

        self.enable_notifier.add_subscriber(
            self.home_tab.home_tab_widget.enable_observer)

        self.enable_notifier.add_subscriber(
            self.corrections_tab.corrections_tab_view.enable_tab_observer)

        self.enable_notifier.add_subscriber(
            self.load_widget.load_widget.enable_observer)

        self.enable_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_view.enable_tab_observer)

        #self.enable_notifier.add_subscriber(self.model_fitting_tab.model_fitting_tab_view.enable_tab_observer)

        self.enable_notifier.add_subscriber(
            self.phase_tab.phase_table_presenter.enable_tab_observer)

        self.enable_notifier.add_subscriber(
            self.results_tab.results_tab_presenter.enable_tab_observer)

        self.enable_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.enable_tab_observer)

        self.enable_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.enable_tab_observer)

    def setup_load_observers(self):
        self.load_widget.load_widget.loadNotifier.add_subscriber(
            self.home_tab.home_tab_widget.loadObserver)

        self.load_widget.load_widget.loadNotifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.loadObserver)

        self.load_widget.load_widget.loadNotifier.add_subscriber(
            self.corrections_tab.corrections_tab_presenter.load_observer)

        self.load_widget.load_widget.loadNotifier.add_subscriber(
            self.phase_tab.phase_table_presenter.run_change_observer)

        self.load_widget.load_widget.loadNotifier.add_subscriber(
            self.fitting_tab.fitting_tab_view.disable_tab_observer)

        self.load_widget.load_widget.loadNotifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.disable_tab_observer
        )

        #self.load_widget.load_widget.loadNotifier.add_subscriber(
        #    self.plot_widget.raw_mode.new_data_observer)

    def setup_gui_variable_observers(self):
        self.context.gui_context.gui_variables_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.gui_variables_observer
        )

        self.context.gui_context.gui_variables_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.gui_context_observer)

        for observer in self.plot_widget.rebin_options_set_observers:
            self.context.gui_context.gui_variables_notifier.add_subscriber(
                observer)

        self.context.gui_context.gui_variable_non_calulation_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.gui_context_observer)

        self.grouping_tab_widget.pairing_table_widget.selected_pair_changed_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.selected_group_pair_observer
        )

        self.grouping_tab_widget.pairing_table_widget.selected_pair_changed_notifier.add_subscriber(
            self.plot_widget.data_mode.added_group_or_pair_observer)

        self.grouping_tab_widget.grouping_table_widget.selected_group_changed_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.selected_group_pair_observer
        )

        self.grouping_tab_widget.grouping_table_widget.selected_group_changed_notifier.add_subscriber(
            self.plot_widget.data_mode.added_group_or_pair_observer)

        self.grouping_tab_widget.pairing_table_widget.selected_pair_changed_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.
            selected_workspaces_observer)

        self.grouping_tab_widget.grouping_table_widget.selected_group_changed_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.
            selected_workspaces_observer)

        # differences
        self.grouping_tab_widget.diff_table.add_subscribers([
            self.fitting_tab.fitting_tab_presenter.
            selected_group_pair_observer, self.seq_fitting_tab.
            seq_fitting_tab_presenter.selected_workspaces_observer,
            self.plot_widget.data_mode.added_group_or_pair_observer
        ])

        # phase table set up
        self.phase_tab.phase_table_presenter.selected_phasequad_changed_notifier.add_subscriber(
            self.plot_widget.data_mode.added_group_or_pair_observer)

        self.phase_tab.phase_table_presenter.selected_phasequad_changed_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.selected_group_pair_observer
        )

        self.phase_tab.phase_table_presenter.selected_phasequad_changed_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.
            selected_workspaces_observer)

        self.fitting_tab.fitting_tab_presenter.fit_function_changed_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.
            fit_function_updated_observer)

        self.fitting_tab.fitting_tab_presenter.fit_parameter_changed_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.
            fit_parameter_updated_observer)

        self.seq_fitting_tab.seq_fitting_tab_presenter.fit_parameter_changed_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.
            fit_parameter_updated_observer)

        self.seq_fitting_tab.seq_fitting_tab_presenter.sequential_fit_finished_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.
            sequential_fit_finished_observer)

        self.fitting_tab.fitting_tab_presenter.fitting_mode_changed_notifier.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.
            fit_type_changed_observer)

        self.fitting_tab.fitting_tab_presenter.simultaneous_fit_by_specifier_changed.add_subscriber(
            self.seq_fitting_tab.seq_fitting_tab_presenter.
            selected_workspaces_observer)

        self.fitting_tab.fitting_tab_presenter.selected_fit_results_changed.add_subscriber(
            self.plot_widget.fit_mode.plot_selected_fit_observer)

        self.seq_fitting_tab.seq_fitting_tab_presenter.selected_sequential_fit_notifier.add_subscriber(
            self.plot_widget.fit_mode.plot_selected_fit_observer)

        #self.model_fitting_tab.model_fitting_tab_presenter.selected_fit_results_changed.add_subscriber(
        #    self.plot_widget.model_fit_mode.plot_selected_fit_observer)

        #self.model_fitting_tab.model_fitting_tab_presenter.update_plot_x_range_notifier.add_subscriber(
        #    self.plot_widget.model_fit_mode.update_x_range_observer)

        #self.model_fitting_tab.model_fitting_tab_presenter.update_override_tick_labels_notifier.add_subscriber(
        #    self.plot_widget.model_fit_mode.update_override_tick_labels_observer)

    def setup_grouping_changed_observers(self):
        self.grouping_tab_widget.group_tab_presenter.groupingNotifier.add_subscriber(
            self.home_tab.home_tab_widget.groupingObserver)

        self.grouping_tab_widget.group_tab_presenter.groupingNotifier.add_subscriber(
            self.corrections_tab.corrections_tab_presenter.
            group_change_observer)

        self.grouping_tab_widget.group_tab_presenter.groupingNotifier.add_subscriber(
            self.phase_tab.phase_table_presenter.group_change_observer)

    def setup_corrections_changed_observers(self):
        self.corrections_tab.corrections_tab_presenter.perform_corrections_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.gui_variables_observer
        )

    def setup_instrument_changed_notifier(self):
        self.context.data_context.instrumentNotifier.add_subscriber(
            self.home_tab.home_tab_widget.instrumentObserver)

        self.context.data_context.instrumentNotifier.add_subscriber(
            self.load_widget.load_widget.instrumentObserver)

        self.context.data_context.instrumentNotifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.instrumentObserver)

        self.context.data_context.instrumentNotifier.add_subscriber(
            self.corrections_tab.corrections_tab_presenter.
            instrument_changed_observer)

        self.context.data_context.instrumentNotifier.add_subscriber(
            self.phase_tab.phase_table_presenter.instrument_changed_observer)

        self.context.data_context.instrumentNotifier.add_subscriber(
            self.fitting_tab.fitting_tab_presenter.instrument_changed_observer)

        for observer in self.plot_widget.clear_plot_observers:
            self.context.data_context.instrumentNotifier.add_subscriber(
                observer)

    def setup_group_calculation_enable_notifier(self):

        self.grouping_tab_widget.group_tab_presenter.enable_editing_notifier.add_subscriber(
            self.enable_observer)

        self.corrections_tab.corrections_tab_presenter.enable_editing_notifier.add_subscriber(
            self.enable_observer)

        self.fitting_tab.fitting_tab_presenter.enable_editing_notifier.add_subscriber(
            self.enable_observer)

        self.phase_tab.phase_table_presenter.enable_editing_notifier.add_subscriber(
            self.enable_observer)

        #self.model_fitting_tab.model_fitting_tab_presenter.enable_editing_notifier.add_subscriber(
        #    self.enable_observer)

    def setup_group_calculation_disabler_notifier(self):

        self.grouping_tab_widget.group_tab_presenter.disable_editing_notifier.add_subscriber(
            self.disable_observer)

        self.corrections_tab.corrections_tab_presenter.disable_editing_notifier.add_subscriber(
            self.disable_observer)

        self.fitting_tab.fitting_tab_presenter.disable_editing_notifier.add_subscriber(
            self.disable_observer)

        self.phase_tab.phase_table_presenter.disable_editing_notifier.add_subscriber(
            self.disable_observer)

        #self.model_fitting_tab.model_fitting_tab_presenter.disable_editing_notifier.add_subscriber(
        #    self.disable_observer)

    def setup_on_load_enabler(self):
        self.load_widget.load_widget.load_run_widget.enable_notifier.add_subscriber(
            self.home_tab.home_tab_widget.enable_observer)

        self.load_widget.load_widget.load_run_widget.enable_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.enable_observer)

        self.load_widget.load_widget.load_run_widget.enable_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_view.enable_tab_observer)

    def setup_on_load_disabler(self):
        self.load_widget.load_widget.load_run_widget.disable_notifier.add_subscriber(
            self.home_tab.home_tab_widget.disable_observer)

        self.load_widget.load_widget.load_run_widget.disable_notifier.add_subscriber(
            self.grouping_tab_widget.group_tab_presenter.disable_observer)

        self.load_widget.load_widget.load_run_widget.disable_notifier.add_subscriber(
            self.fitting_tab.fitting_tab_view.disable_tab_observer)

    def setup_counts_calculation_finished_notifier(self):
        self.grouping_tab_widget.group_tab_presenter.counts_calculation_finished_notifier.add_subscriber(
            self.corrections_tab.corrections_tab_presenter.
            pre_process_and_counts_calculated_observer)

    def setup_asymmetry_pair_and_diff_calculations_finished_notifier(self):
        self.corrections_tab.corrections_tab_presenter.asymmetry_pair_and_diff_calculations_finished_notifier.\
            add_subscriber(self.fitting_tab.fitting_tab_presenter.input_workspace_observer)

        self.corrections_tab.corrections_tab_presenter.asymmetry_pair_and_diff_calculations_finished_notifier.\
            add_subscriber(self.seq_fitting_tab.seq_fitting_tab_presenter.selected_workspaces_observer)

        for observer in self.plot_widget.data_changed_observers:
            self.corrections_tab.corrections_tab_presenter.asymmetry_pair_and_diff_calculations_finished_notifier.\
                add_subscriber(observer)
            self.phase_tab.phase_table_presenter.calculation_finished_notifier.add_subscriber(
                observer)

    def setup_phase_quad_changed_notifier(self):
        pass

    def setup_phase_table_changed_notifier(self):
        pass

    def setup_fitting_notifier(self):
        """Connect fitting and results tabs to inform of new fits"""
        self.fitting_context.new_fit_results_notifier.add_subscriber(
            self.results_tab.results_tab_presenter.new_fit_performed_observer)

        self.fitting_tab.fitting_tab_presenter.remove_plot_guess_notifier.add_subscriber(
            self.plot_widget.fit_mode.remove_plot_guess_observer)

        self.fitting_tab.fitting_tab_presenter.update_plot_guess_notifier.add_subscriber(
            self.plot_widget.fit_mode.update_plot_guess_observer)

        #self.model_fitting_tab.model_fitting_tab_presenter.remove_plot_guess_notifier.add_subscriber(
        #    self.plot_widget.model_fit_mode.remove_plot_guess_observer)

        #self.model_fitting_tab.model_fitting_tab_presenter.update_plot_guess_notifier.add_subscriber(
        #    self.plot_widget.model_fit_mode.update_plot_guess_observer)

    def setup_results_notifier(self):
        """Connect results tab to the model fitting tab."""
        return  #self.results_tab.results_tab_presenter.results_table_created_notifier.add_subscriber(
        #    self.model_fitting_tab.model_fitting_tab_presenter.results_table_created_observer)

    def closeEvent(self, event):
        self.removeDockWidget(self.dockable_plot_widget_window)
        self.tabs.closeEvent(event)
        self.context.ads_observer.unsubscribe()
        self.grouping_tab_widget.group_tab_presenter.closePeriodInfoWidget()
        super(MuonAnalysisGui, self).closeEvent(event)