Esempio n. 1
0
 def start_calibration_worker(self, sample_path, plot_output, rb_num, bank=None, calfile=None,
                              spectrum_numbers=None):
     """
     Calibrate the data in a separate thread so as to not freeze the GUI.
     :param sample_path: Path to sample data file.
     :param plot_output: Whether to plot the output.
     :param rb_num: The current RB number set in the GUI.
     :param bank: Optional parameter to crop by bank.
     :param calfile: Custom calibration file the user can supply for the calibration region of interest.
     :param spectrum_numbers: Optional parameter to crop by spectrum number.
     """
     self.worker = AsyncTask(self.model.create_new_calibration, (sample_path,),
                             {
                             "plot_output": plot_output,
                             "instrument": self.instrument,
                             "rb_num": rb_num,
                             "bank": bank,
                             "calfile": calfile,
                             "spectrum_numbers": spectrum_numbers
                             },
                             error_cb=self._on_error,
                             success_cb=self._on_success)
     self.pending_calibration.set_calibration(sample_path, self.instrument)
     self.pending_calibration.set_roi_info(bank, calfile, spectrum_numbers)
     self.set_calibrate_controls_enabled(False)
     self.worker.start()
Esempio n. 2
0
 def start_calibration_worker(self,
                              vanadium_path,
                              sample_path,
                              plot_output,
                              rb_num,
                              bank=None,
                              spectrum_numbers=None):
     """
     Calibrate the data in a separate thread so as to not freeze the GUI.
     :param vanadium_path: Path to vanadium data file.
     :param sample_path: Path to sample data file.
     :param plot_output: Whether to plot the output.
     :param rb_num: The current RB number set in the GUI.
     :param bank: Optional parameter to crop by bank.
     :param spectrum_numbers: Optional parameter to crop by spectrum number.
     """
     self.worker = AsyncTask(self.model.create_new_calibration,
                             (vanadium_path, sample_path), {
                                 "plot_output": plot_output,
                                 "instrument": self.instrument,
                                 "rb_num": rb_num,
                                 "bank": bank,
                                 "spectrum_numbers": spectrum_numbers
                             },
                             error_cb=self._on_error,
                             success_cb=self._on_success)
     self.pending_calibration.set_calibration(vanadium_path, sample_path,
                                              self.instrument)
     self.set_calibrate_controls_enabled(False)
     self.worker.start()
Esempio n. 3
0
    def execute_async(self,
                      code_str,
                      line_offset,
                      filename=None,
                      blocking=False):
        """
        Execute the given code string on a separate thread. This function
        returns as soon as the new thread starts

        :param code_str: A string containing code to execute
        :param line_offset: See PythonCodeExecution.execute()
        :param filename: See PythonCodeExecution.execute()
        :param blocking: If True the call will block until the task is finished
        :returns: The created async task, only returns task if the blocking is False
        """
        # Stack is chopped on error to avoid the  AsyncTask.run->self.execute calls appearing
        # as these are not useful for the user in this context
        if not blocking:
            task = AsyncTask(self.execute,
                             args=(code_str, filename, line_offset),
                             success_cb=self._on_success,
                             error_cb=self._on_error)
            task.start()
            self._task = task
            return task
        else:
            self._task = BlockingAsyncTaskWithCallback(
                self.execute,
                args=(code_str, filename, line_offset),
                success_cb=self._on_success,
                error_cb=self._on_error,
                blocking_cb=QApplication.processEvents)
            return self._task.start()
Esempio n. 4
0
 def add_simpleapi_to_completions_if_required(self):
     """
     If the simpleapi functions haven't been added to the completions, start a separate thread to load them in.
     """
     if not self.simpleapi_in_completions and "from mantid.simpleapi import *" in self.editor.text():
         self.simpleapi_in_completions = True
         self.worker = AsyncTask(self._add_simpleapi_to_completions_if_required)
         self.worker.start()
Esempio n. 5
0
 def start_calibration_worker(self, plot_output):
     """
     Calibrate the data in a separate thread so as to not freeze the GUI.
     """
     self.worker = AsyncTask(self.model.create_new_calibration,
                             (self.current_calibration, self.rb_num, plot_output),
                             error_cb=self._on_error,
                             success_cb=self._on_success)
     self.set_calibrate_controls_enabled(False)
     self.worker.start()
Esempio n. 6
0
 def _start_load_worker(self, filenames):
     """
     Load one to many files into mantid that are tracked by the interface.
     :param filenames: Comma separated list of filenames to load
     """
     self.worker = AsyncTask(self.model.load_files, (filenames, ),
                             error_cb=self._on_worker_error,
                             finished_cb=self._emit_enable_button_signal,
                             success_cb=self._on_worker_success)
     self.worker.start()
Esempio n. 7
0
 def start_calibration_worker(self, vanadium_path, calib_path, plot_output, rb_num):
     """
     Calibrate the data in a separate thread so as to not freeze the GUI.
     :param vanadium_path: Path to vanadium data file.
     :param calib_path: Path to calibration data file.
     :param plot_output: Whether to plot the output.
     :param rb_num: The current RB number set in the GUI.
     """
     self.worker = AsyncTask(self.model.create_new_calibration, (vanadium_path, calib_path),
                             {"plot_output": plot_output, "instrument": self.instrument, "rb_num": rb_num},
                             error_cb=self._on_error, finished_cb=self.enable_calibrate_buttons)
     self.disable_calibrate_buttons()
     self.worker.start()
Esempio n. 8
0
    def test_successful_no_arg_operation_calls_success_and_finished_callback(self):
        def foo():
            return 42

        recv = AsyncTaskTest.Receiver()
        t = AsyncTask(foo, success_cb=recv.on_success, error_cb=recv.on_error,
                      finished_cb=recv.on_finished)
        t.start()
        t.join()
        self.assertTrue(recv.finished_cb_called)
        self.assertTrue(recv.success_cb_called)
        self.assertFalse(recv.error_cb_called)
        self.assertEqual(42, recv.task_output)
Esempio n. 9
0
 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()
Esempio n. 10
0
 def start_focus_worker(self, focus_paths, banks, plot_output, rb_num, spectrum_numbers=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 banks: A list of banks that are to be focused.
     :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 spectrum_numbers: Optional parameter to crop to a specific list of spectrum numbers.
     """
     self.worker = AsyncTask(self.model.focus_run,
                             (focus_paths, banks, plot_output, self.instrument, rb_num, spectrum_numbers),
                             error_cb=self._on_worker_error,
                             finished_cb=self.emit_enable_button_signal)
     self.set_focus_controls_enabled(False)
     self.worker.start()
Esempio n. 11
0
    def test_unsuccessful_args_and_kwargs_operation_calls_error_and_finished_callback(self):
        def foo(scale, shift):
            raise RuntimeError("Bad operation")

        recv = AsyncTaskTest.Receiver()
        scale, shift = 2, 4
        t = AsyncTask(foo, args = (scale,), kwargs={'shift': shift},
                      success_cb=recv.on_success, error_cb=recv.on_error,
                      finished_cb=recv.on_finished)
        t.start()
        t.join()
        self.assertTrue(recv.finished_cb_called)
        self.assertFalse(recv.success_cb_called)
        self.assertTrue(recv.error_cb_called)
        self.assertTrue(isinstance(recv.task_exc, RuntimeError))
Esempio n. 12
0
    def test_successful_args_and_kwargs_operation_calls_success_and_finished_callback(self):
        def foo(scale, shift):
            return scale*42 + shift

        recv = AsyncTaskTest.Receiver()
        scale, shift = 2, 4
        t = AsyncTask(foo, args = (scale,), kwargs={'shift': shift},
                      success_cb=recv.on_success, error_cb=recv.on_error,
                      finished_cb=recv.on_finished)
        t.start()
        t.join()
        self.assertTrue(recv.finished_cb_called)
        self.assertTrue(recv.success_cb_called)
        self.assertFalse(recv.error_cb_called)
        self.assertEqual(scale*42 + shift, recv.task_output)
Esempio n. 13
0
    def execute_async(self, code_str, filename=''):
        """
        Execute the given code string on a separate thread. This function
        returns as soon as the new thread starts

        :param code_str: A string containing code to execute
        :param filename: See PythonCodeExecution.execute()
        :returns: The created async task
        """
        # Stack is chopped on error to avoid the  AsyncTask.run->self.execute calls appearing
        # as these are not useful for the user in this context
        task = AsyncTask(self.execute, args=(code_str, filename),
                         success_cb=self._on_success, error_cb=self._on_error)
        task.start()
        self._task = task
        return task
Esempio n. 14
0
    def test_unsuccessful_operation_with_error_cb(self):
        def foo(scale, shift):
            def bar():
                raise RuntimeError("Bad operation")
            bar()

        recv = AsyncTaskTest.Receiver()
        scale, shift = 2, 4
        t = AsyncTask(foo, args = (scale,), kwargs={'shift': shift},
                      error_cb=recv.on_error)
        t.start()
        t.join()
        self.assertTrue(recv.error_cb_called)
        self.assertTrue(isinstance(recv.task_exc, RuntimeError))
        self.assertEqual(3, len(recv.task_exc_stack))
        self.assertEqual(135, recv.task_exc_stack[1][1])
        self.assertEqual(134, recv.task_exc_stack[2][1])
Esempio n. 15
0
    def execute_async(self, code_str, filename=None):
        """
        Execute the given code string on a separate thread. This function
        returns as soon as the new thread starts

        :param code_str: A string containing code to execute
        :param filename: See PythonCodeExecution.execute()
        :returns: The created async task
        """
        # Stack is chopped on error to avoid the  AsyncTask.run->self.execute calls appearing
        # as these are not useful for the user in this context
        task = AsyncTask(self.execute,
                         args=(code_str, filename),
                         success_cb=self._on_success,
                         error_cb=self._on_error)
        task.start()
        self._task = task
        return task
Esempio n. 16
0
 def __init__(self, configurations):
     self.jobs = []
     for config in configurations:
         print("CONFIG:", config)
         self.jobs.append(
             AsyncTask(TotalScatteringReduction,
                       args=(config, ),
                       success_cb=self.on_success,
                       error_cb=self.on_error,
                       finished_cb=self.on_finished))
Esempio n. 17
0
    def test_unsuccessful_no_arg_operation_calls_error_and_finished_callback(
            self):
        def foo():
            # this is a bad operation
            # that should appear in the stack trace
            raise RuntimeError("Bad operation")

        recv = AsyncTaskTest.Receiver()
        t = AsyncTask(foo,
                      success_cb=recv.on_success,
                      error_cb=recv.on_error,
                      finished_cb=recv.on_finished)
        t.start()
        t.join()
        self.assertTrue(recv.finished_cb_called)
        self.assertFalse(recv.success_cb_called)
        self.assertTrue(recv.error_cb_called)
        self.assertTrue(isinstance(recv.task_exc, RuntimeError),
                        msg="Expected RuntimeError, found " +
                        recv.task_exc.__class__.__name__)

        self.assertEqual(2, len(recv.task_exc_stack))
        # line number of self.target in asynchronous.py
        self.assertEqual(91, recv.task_exc_stack[0][1])
        # line number of raise statement above
        self.assertEqual(95, recv.task_exc_stack[1][1])
Esempio n. 18
0
    def test_unsuccessful_no_arg_operation_calls_error_and_finished_callback(self):
        def foo():
            # this is a bad operation
            # that should appear in the stack trace
            raise RuntimeError("Bad operation")

        recv = AsyncTaskTest.Receiver()
        t = AsyncTask(foo, success_cb=recv.on_success,
                      error_cb=recv.on_error,
                      finished_cb=recv.on_finished)
        t.start()
        t.join()
        self.assertTrue(recv.finished_cb_called)
        self.assertFalse(recv.success_cb_called)
        self.assertTrue(recv.error_cb_called)
        self.assertTrue(isinstance(recv.task_exc, RuntimeError),
                        msg="Expected RuntimeError, found " + recv.task_exc.__class__.__name__)

        self.assertEqual(2, len(recv.task_exc_stack))
        # line number of self.target in asynchronous.py
        self.assertEqual(91, recv.task_exc_stack[0][1])
        # line number of raise statement above
        self.assertEqual(95, recv.task_exc_stack[1][1])
Esempio n. 19
0
    def execute_async(self, code_str, filename=None, blocking=False):
        """
        Execute the given code string on a separate thread. This function
        returns as soon as the new thread starts

        :param code_str: A string containing code to execute
        :param filename: See PythonCodeExecution.execute()
        :param blocking: If True the call will block until the task is finished
        :returns: The created async task, only returns task if the blocking is False
        """
        # Stack is chopped on error to avoid the  AsyncTask.run->self.execute calls appearing
        # as these are not useful for the user in this context
        if not blocking:
            task = AsyncTask(self.execute, args=(code_str, filename),
                             success_cb=self._on_success, error_cb=self._on_error)
            task.start()
            self._task = task
            return task
        else:
            self._task = BlockingAsyncTaskWithCallback(self.execute, args=(code_str, filename),
                                                       success_cb=self._on_success, error_cb=self._on_error,
                                                       blocking_cb=QApplication.processEvents)
            return self._task.start()
Esempio n. 20
0
    def test_correct_exception_is_raised_when_called_on_other_thread(self):
        self.exc = None
        self.exit_code = None

        def collect_error(e):
            self.exc = e

        thread = AsyncTask(self.task, error_cb=collect_error)
        thread.start()
        while thread.is_alive():
            QApplication.processEvents()
        thread.join(0.5)
        self.assertTrue(isinstance(self.exc.exc_value, CustomException))
        self.assertEqual(TaskExitCode.ERROR, thread.exit_code)
Esempio n. 21
0
    def test_successful_no_arg_operation_calls_success_and_finished_callback(
            self):
        def foo():
            return 42

        recv = AsyncTaskTest.Receiver()
        t = AsyncTask(foo,
                      success_cb=recv.on_success,
                      error_cb=recv.on_error,
                      finished_cb=recv.on_finished)
        t.start()
        t.join()
        self.assertTrue(recv.finished_cb_called)
        self.assertTrue(recv.success_cb_called)
        self.assertFalse(recv.error_cb_called)
        self.assertEqual(42, recv.task_output)
Esempio n. 22
0
    def test_successful_positional_args_operation_calls_success_and_finished_callback(
            self):
        def foo(shift):
            return 42 + shift

        recv = AsyncTaskTest.Receiver()
        shift = 2
        t = AsyncTask(foo,
                      args=(shift, ),
                      success_cb=recv.on_success,
                      error_cb=recv.on_error,
                      finished_cb=recv.on_finished)
        t.start()
        t.join()
        self.assertTrue(recv.finished_cb_called)
        self.assertTrue(recv.success_cb_called)
        self.assertFalse(recv.error_cb_called)
        self.assertEqual(42 + shift, recv.task_output)
Esempio n. 23
0
    def test_unsuccessful_args_and_kwargs_operation_calls_error_and_finished_callback(
            self):
        def foo(scale, shift):
            raise RuntimeError("Bad operation")

        recv = AsyncTaskTest.Receiver()
        scale, shift = 2, 4
        t = AsyncTask(foo,
                      args=(scale, ),
                      kwargs={'shift': shift},
                      success_cb=recv.on_success,
                      error_cb=recv.on_error,
                      finished_cb=recv.on_finished)
        t.start()
        t.join()
        self.assertTrue(recv.finished_cb_called)
        self.assertFalse(recv.success_cb_called)
        self.assertTrue(recv.error_cb_called)
        self.assertTrue(isinstance(recv.task_exc, RuntimeError))
Esempio n. 24
0
    def test_unsuccessful_operation_with_error_cb(self):
        def foo(scale, shift):
            def bar():
                raise RuntimeError("Bad operation")

            bar()

        recv = AsyncTaskTest.Receiver()
        scale, shift = 2, 4
        t = AsyncTask(foo,
                      args=(scale, ),
                      kwargs={'shift': shift},
                      error_cb=recv.on_error)
        t.start()
        t.join()
        self.assertTrue(recv.error_cb_called)
        self.assertTrue(isinstance(recv.task_exc, RuntimeError))
        self.assertEqual(3, len(recv.task_exc_stack))
        self.assertEqual(135, recv.task_exc_stack[1][1])
        self.assertEqual(134, recv.task_exc_stack[2][1])
Esempio n. 25
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)
Esempio n. 26
0
class FittingDataPresenter(object):
    def __init__(self, model, view):
        self.model = model
        self.view = view
        self.worker = None

        self.row_numbers = TwoWayRowDict(
        )  # {ws_name: table_row} and {table_row: ws_name}
        self.plotted = set()  # List of plotted workspace names

        # Connect view signals to local methods
        self.view.set_on_load_clicked(self.on_load_clicked)
        self.view.set_enable_button_connection(self._enable_load_button)
        self.view.set_on_remove_selected_clicked(
            self._remove_selected_tracked_workspaces)
        self.view.set_on_remove_all_clicked(
            self._remove_all_tracked_workspaces)
        self.view.set_on_table_cell_changed(self._handle_table_cell_changed)

        # Observable Setup
        self.plot_added_notifier = GenericObservable()
        self.plot_removed_notifier = GenericObservable()
        self.all_plots_removed_notifier = GenericObservable()

    def on_load_clicked(self):
        if self._validate():
            filenames = self._get_filenames()
            self._start_load_worker(filenames)

    def remove_workspace(self, ws_name):
        if ws_name in self.get_loaded_workspaces():
            removed = self.get_loaded_workspaces().pop(ws_name)
            self.plot_removed_notifier.notify_subscribers(removed)
            self.plotted.discard(ws_name)
            self._repopulate_table()

    def rename_workspace(self, old_name, new_name):
        loaded_workspaces = self.get_loaded_workspaces()
        if old_name in loaded_workspaces:
            ws = loaded_workspaces.pop(old_name)
            loaded_workspaces[new_name] = ws
            if old_name in self.plotted:
                self.plotted.remove(old_name)
                self.plotted.add(new_name)
            self._repopulate_table()

    def clear_workspaces(self):
        self.get_loaded_workspaces().clear()
        self.plotted.clear()
        self.row_numbers.clear()
        self._repopulate_table()

    def replace_workspace(self, name, workspace):
        if name in self.get_loaded_workspaces():
            self.get_loaded_workspaces()[name] = workspace
            if name in self.plotted:
                self.all_plots_removed_notifier.notify_subscribers()
            self._repopulate_table()

    def get_loaded_workspaces(self):
        return self.model.get_loaded_workspaces()

    def _start_load_worker(self, filenames):
        """
        Load one to many files into mantid that are tracked by the interface.
        :param filenames: Comma separated list of filenames to load
        """
        self.worker = AsyncTask(self.model.load_files, (filenames, ),
                                error_cb=self._on_worker_error,
                                finished_cb=self._emit_enable_button_signal,
                                success_cb=self._on_worker_success)
        self.worker.start()

    def _on_worker_error(self, _):
        logger.error("Error occurred when loading files.")
        self._emit_enable_button_signal()

    def _on_worker_success(self, _):
        if self.view.get_add_to_plot():
            self.plotted.update(self.model.get_last_added())
        self._repopulate_table()

    def _repopulate_table(self):
        """
        Populate the table with the information from the loaded workspaces.
        Will also handle any workspaces that need to be plotted.
        """
        self._remove_all_table_rows()
        self.row_numbers.clear()
        self.all_plots_removed_notifier.notify_subscribers()
        workspaces = self.get_loaded_workspaces()
        for i, name in enumerate(workspaces):
            try:
                run_no = self.model.get_sample_log_from_ws(name, "run_number")
                bank = self.model.get_sample_log_from_ws(name, "bankid")
                if bank == 0:
                    bank = "cropped"
                checked = name in self.plotted
                self._add_row_to_table(name, i, run_no, bank, checked)
            except RuntimeError:
                self._add_row_to_table(name, i)
            self._handle_table_cell_changed(i, 2)

    def _remove_selected_tracked_workspaces(self):
        row_numbers = self._remove_selected_table_rows()
        for row_no in row_numbers:
            ws_name = self.row_numbers.pop(row_no)
            removed = self.get_loaded_workspaces().pop(ws_name)
            self.plot_removed_notifier.notify_subscribers(removed)
            self.plotted.discard(ws_name)
        self._repopulate_table()

    def _remove_all_tracked_workspaces(self):
        self.clear_workspaces()
        self._remove_all_table_rows()

    def _handle_table_cell_changed(self, row, col):
        if col == 2 and row in self.row_numbers:  # Is from the plot check column
            ws = self.model.get_loaded_workspaces()[self.row_numbers[row]]
            ws_name = self.row_numbers[row]
            if self.view.get_item_checked(row, col):  # Plot Box is checked
                self.plot_added_notifier.notify_subscribers(ws)
                self.plotted.add(ws_name)
            else:  # Plot box is unchecked
                self.plot_removed_notifier.notify_subscribers(ws)
                self.plotted.discard(ws_name)

    def _enable_load_button(self, enabled):
        self.view.set_load_button_enabled(enabled)

    def _emit_enable_button_signal(self):
        self.view.sig_enable_load_button.emit(True)

    def _get_filenames(self):
        return self.view.get_filenames_to_load()

    def _is_searching(self):
        return self.view.is_searching()

    def _files_are_valid(self):
        return self.view.get_filenames_valid()

    def _validate(self):
        if self._is_searching():
            create_error_message(
                self.view, "Mantid is searching for files. Please wait.")
            return False
        elif not self._files_are_valid():
            create_error_message(self.view, "Entered files are not valid.")
            return False
        return True

    def _add_row_to_table(self,
                          ws_name,
                          row,
                          run_no=None,
                          bank=None,
                          checked=False):
        words = ws_name.split("_")
        if run_no is not None and bank is not None:
            self.view.add_table_row(run_no, bank, checked)
            self.row_numbers[ws_name] = row
        elif len(words) == 4 and words[2] == "bank":
            logger.notice(
                "No sample logs present, determining information from workspace name."
            )
            self.view.add_table_row(words[1], words[3], checked)
            self.row_numbers[ws_name] = row
        else:
            logger.warning(
                "The workspace '{}' was not in the correct naming format. Files should be named in the following way: "
                "INSTRUMENT_RUNNUMBER_bank_BANK. Using workspace name as identifier."
                .format(ws_name))
            self.view.add_table_row(ws_name, "N/A", checked)
            self.row_numbers[ws_name] = row

    def _remove_table_row(self, row_no):
        self.view.remove_table_row(row_no)

    def _remove_selected_table_rows(self):
        return self.view.remove_selected()

    def _remove_all_table_rows(self):
        self.view.remove_all()
Esempio n. 27
0
class FittingDataPresenter(object):
    def __init__(self, model, view):
        self.model = model
        self.view = view
        self.worker = None

        self.row_numbers = TwoWayRowDict(
        )  # {ws_name: table_row} and {table_row: ws_name}
        self.plotted = set()  # List of plotted workspace names

        # Connect view signals to local methods
        self.view.set_on_load_clicked(self.on_load_clicked)
        self.view.set_enable_load_button_connection(self._enable_load_button)
        self.view.set_enable_inspect_bg_button_connection(
            self._enable_inspect_bg_button)
        self.view.set_on_remove_selected_clicked(
            self._remove_selected_tracked_workspaces)
        self.view.set_on_remove_all_clicked(
            self._remove_all_tracked_workspaces)
        self.view.set_on_plotBG_clicked(self._plotBG)
        self.view.set_on_table_cell_changed(self._handle_table_cell_changed)
        self.view.set_on_xunit_changed(self._log_xunit_change)
        self.view.set_table_selection_changed(self._handle_selection_changed)

        # Observable Setup
        self.plot_added_notifier = GenericObservable()
        self.plot_removed_notifier = GenericObservable()
        self.all_plots_removed_notifier = GenericObservable()

    def _log_xunit_change(self, xunit):
        logger.notice(
            "Subsequent files will be loaded with the x-axis unit:\t{}".format(
                xunit))

    def on_load_clicked(self, xunit):
        if self._validate():
            filenames = self._get_filenames()
            self._start_load_worker(filenames, xunit)

    def remove_workspace(self, ws_name):
        if ws_name in self.get_loaded_workspaces():
            removed = self.get_loaded_workspaces().pop(ws_name)
            self.plot_removed_notifier.notify_subscribers(removed)
            self.plotted.discard(ws_name)
            self._repopulate_table()
            self.model.repopulate_logs()  # so matches new table
        elif ws_name in self.model.get_log_workspaces_name():
            logger.warning(
                'Deleting the log workspace may cause unexpected errors.')

    def rename_workspace(self, old_name, new_name):
        if old_name in self.get_loaded_workspaces():
            self.model.update_workspace_name(old_name, new_name)
            if old_name in self.plotted:
                self.plotted.remove(old_name)
                self.plotted.add(new_name)
            self._repopulate_table()
            self.model.repopulate_logs()  # so matches new table

    def clear_workspaces(self):
        self.get_loaded_workspaces().clear()
        self.plotted.clear()
        self.row_numbers.clear()
        self._repopulate_table()

    def replace_workspace(self, name, workspace):
        if name in self.get_loaded_workspaces():
            self.get_loaded_workspaces()[name] = workspace
            if name in self.plotted:
                self.all_plots_removed_notifier.notify_subscribers()
            self._repopulate_table()

    def get_loaded_workspaces(self):
        return self.model.get_loaded_workspaces()

    def _start_load_worker(self, filenames, xunit):
        """
        Load one to many files into mantid that are tracked by the interface.
        :param filenames: Comma separated list of filenames to load
        """
        self.worker = AsyncTask(
            self.model.load_files, (filenames, xunit),
            error_cb=self._on_worker_error,
            finished_cb=self._emit_enable_load_button_signal,
            success_cb=self._on_worker_success)
        self.worker.start()

    def _on_worker_error(self, _):
        logger.error("Error occurred when loading files.")
        self._emit_enable_load_button_signal()

    def _on_worker_success(self, _):
        if self.view.get_add_to_plot():
            self.plotted.update(self.model.get_last_added())
        self._repopulate_table()

    def _repopulate_table(self):
        """
        Populate the table with the information from the loaded workspaces.
        Will also handle any workspaces that need to be plotted.
        """
        self._remove_all_table_rows()
        self.row_numbers.clear()
        self.all_plots_removed_notifier.notify_subscribers()
        workspaces = self.get_loaded_workspaces()
        for i, name in enumerate(workspaces):
            try:
                run_no = self.model.get_sample_log_from_ws(name, "run_number")
                bank = self.model.get_sample_log_from_ws(name, "bankid")
                if bank == 0:
                    bank = "cropped"
                checked = name in self.plotted
                if name in self.model.get_bg_params():
                    self._add_row_to_table(name, i, run_no, bank, checked,
                                           *self.model.get_bg_params()[name])
                else:
                    self._add_row_to_table(name, i, run_no, bank, checked)
            except RuntimeError:
                self._add_row_to_table(name, i)
            self._handle_table_cell_changed(i, 2)

    def _remove_selected_tracked_workspaces(self):
        row_numbers = self._remove_selected_table_rows()
        self.model.remove_log_rows(row_numbers)
        for row_no in row_numbers:
            ws_name = self.row_numbers.pop(row_no)
            removed = self.get_loaded_workspaces().pop(ws_name)
            self.plot_removed_notifier.notify_subscribers(removed)
            self.plotted.discard(ws_name)
        self._repopulate_table()
        self.model.repopulate_logs()

    def _remove_all_tracked_workspaces(self):
        self.clear_workspaces()
        self.model.clear_logs()
        self._remove_all_table_rows()

    def _plotBG(self):
        # make external figure
        row_numbers = self.view.get_selected_rows()
        for row in row_numbers:
            if self.view.get_item_checked(row, 3):
                # background has been subtracted from workspace
                ws_name = self.row_numbers[row]
                self.model.plot_background_figure(ws_name)

    def _handle_table_cell_changed(self, row, col):
        if row in self.row_numbers:
            ws_name = self.row_numbers[row]
            if col == 2:
                # Plot check box
                ws = self.model.get_loaded_workspaces()[ws_name]
                if self.view.get_item_checked(row, col):  # Plot Box is checked
                    self.plot_added_notifier.notify_subscribers(ws)
                    self.plotted.add(ws_name)
                else:  # Plot box is unchecked
                    self.plot_removed_notifier.notify_subscribers(ws)
                    self.plotted.discard(ws_name)
            elif col == 3:
                # subtract bg
                if self.view.get_item_checked(row, col):
                    # subtract bg box checked
                    bg_params = self.view.read_bg_params_from_table(row)
                    self.model.do_background_subtraction(ws_name, bg_params)
                elif self.model.get_background_workspaces()[ws_name]:
                    # box unchecked and bg exists:
                    self.model.undo_background_subtraction(ws_name)
            elif col > 3:
                if self.view.get_item_checked(row, 3):
                    # bg params changed - revaluate background
                    bg_params = self.view.read_bg_params_from_table(row)
                    self.model.do_background_subtraction(ws_name, bg_params)

    def _handle_selection_changed(self):
        rows = self.view.get_selected_rows()
        enabled = False
        for row in rows:
            if self.view.get_item_checked(row, 3):
                enabled = True
        self._enable_inspect_bg_button(enabled)

    def _enable_load_button(self, enabled):
        self.view.set_load_button_enabled(enabled)

    def _emit_enable_load_button_signal(self):
        self.view.sig_enable_load_button.emit(True)

    def _enable_inspect_bg_button(self, enabled):
        self.view.set_inspect_bg_button_enabled(enabled)

    def _get_filenames(self):
        return self.view.get_filenames_to_load()

    def _is_searching(self):
        return self.view.is_searching()

    def _files_are_valid(self):
        return self.view.get_filenames_valid()

    def _validate(self):
        if self._is_searching():
            create_error_message(
                self.view, "Mantid is searching for files. Please wait.")
            return False
        elif not self._files_are_valid():
            create_error_message(self.view, "Entered files are not valid.")
            return False
        return True

    def _add_row_to_table(self,
                          ws_name,
                          row,
                          run_no=None,
                          bank=None,
                          checked=False,
                          bgsub=False,
                          niter=100,
                          xwindow=None,
                          SG=True):
        words = ws_name.split("_")
        # find xwindow from ws xunit if not specified
        if not xwindow:
            ws = self.model.get_loaded_workspaces()[ws_name]
            if ws.getAxis(0).getUnit().unitID() == "TOF":
                xwindow = 1000
            else:
                xwindow = 0.05
        if run_no is not None and bank is not None:
            self.view.add_table_row(run_no, bank, checked, bgsub, niter,
                                    xwindow, SG)
            self.row_numbers[ws_name] = row
        elif len(words) == 4 and words[2] == "bank":
            logger.notice(
                "No sample logs present, determining information from workspace name."
            )
            self.view.add_table_row(words[1], words[3], checked, bgsub, niter,
                                    xwindow, SG)
            self.row_numbers[ws_name] = row
        else:
            logger.warning(
                "The workspace '{}' was not in the correct naming format. Files should be named in the following way: "
                "INSTRUMENT_RUNNUMBER_bank_BANK. Using workspace name as identifier."
                .format(ws_name))
            self.view.add_table_row(ws_name, "N/A", checked, bgsub, niter,
                                    xwindow, SG)
            self.row_numbers[ws_name] = row

    def _remove_table_row(self, row_no):
        self.view.remove_table_row(row_no)

    def _remove_selected_table_rows(self):
        return self.view.remove_selected()

    def _remove_all_table_rows(self):
        self.view.remove_all()
Esempio n. 28
0
class CalibrationPresenter(object):
    def __init__(self, model, view):
        self.model = model
        self.view = view
        self.worker = None
        self.calibration_notifier = self.CalibrationNotifier(self)

        self.current_calibration = CalibrationInfo()
        self.pending_calibration = CalibrationInfo()

        self.connect_view_signals()

        # Main Window State Variables
        self.instrument = "ENGINX"
        self.rb_num = None

        # Cropping Options
        self.cropping_widget = CroppingPresenter(parent=self.view, view=self.view.get_cropping_widget())
        self.show_cropping(False)

    def connect_view_signals(self):
        self.view.set_on_calibrate_clicked(self.on_calibrate_clicked)
        self.view.set_enable_controls_connection(self.set_calibrate_controls_enabled)
        self.view.set_update_field_connection(self.set_field_value)
        self.view.set_on_radio_new_toggled(self.set_create_new_enabled)
        self.view.set_on_radio_existing_toggled(self.set_load_existing_enabled)
        self.view.set_on_check_cropping_state_changed(self.show_cropping)

    def on_calibrate_clicked(self):
        plot_output = self.view.get_plot_output()
        if self.view.get_new_checked() and self._validate():
            sample_file = self.view.get_sample_filename()
            if self.view.get_crop_checked():
                self.start_cropped_calibration_worker(sample_file,
                                                      plot_output, self.rb_num)
            else:
                self.start_calibration_worker(sample_file,plot_output, self.rb_num)
        elif self.view.get_load_checked():
            if not self.validate_path():
                logger.notice("Invalid calibration path")
                return
            filename = self.view.get_path_filename()
            try:
                instrument, sample_file, grp_ws_name, roi_text, banks = \
                    self.model.load_existing_calibration_files(filename)
            except:
                return
            self.pending_calibration.set_calibration(sample_file, instrument)
            self.pending_calibration.set_roi_info_load(banks, grp_ws_name, roi_text)
            self.set_current_calibration()
            set_setting(output_settings.INTERFACES_SETTINGS_GROUP, output_settings.ENGINEERING_PREFIX,
                        "last_calibration_path", filename)

    def start_calibration_worker(self, sample_path, plot_output, rb_num, bank=None, calfile=None,
                                 spectrum_numbers=None):
        """
        Calibrate the data in a separate thread so as to not freeze the GUI.
        :param sample_path: Path to sample data file.
        :param plot_output: Whether to plot the output.
        :param rb_num: The current RB number set in the GUI.
        :param bank: Optional parameter to crop by bank.
        :param calfile: Custom calibration file the user can supply for the calibration region of interest.
        :param spectrum_numbers: Optional parameter to crop by spectrum number.
        """
        self.worker = AsyncTask(self.model.create_new_calibration, (sample_path,),
                                {
                                "plot_output": plot_output,
                                "instrument": self.instrument,
                                "rb_num": rb_num,
                                "bank": bank,
                                "calfile": calfile,
                                "spectrum_numbers": spectrum_numbers
                                },
                                error_cb=self._on_error,
                                success_cb=self._on_success)
        self.pending_calibration.set_calibration(sample_path, self.instrument)
        self.pending_calibration.set_roi_info(bank, calfile, spectrum_numbers)
        self.set_calibrate_controls_enabled(False)
        self.worker.start()

    def start_cropped_calibration_worker(self, sample_path, plot_output, rb_num):
        if self.cropping_widget.get_custom_calfile_enabled():
            calfile = self.cropping_widget.get_custom_calfile()
            self.start_calibration_worker(sample_path, plot_output, rb_num,
                                          calfile=calfile)
        elif self.cropping_widget.get_custom_spectra_enabled():
            spec_nums = self.cropping_widget.get_custom_spectra()
            self.start_calibration_worker(sample_path, plot_output, rb_num,
                                          spectrum_numbers=spec_nums)
        else:
            bank = str(self.cropping_widget.get_bank())
            self.start_calibration_worker(sample_path, plot_output, rb_num,
                                          bank=bank)

    def set_current_calibration(self, success_info=None):
        if success_info:
            logger.information("Thread executed in " + str(success_info.elapsed_time) + " seconds.")
        self.current_calibration = deepcopy(self.pending_calibration)
        self.calibration_notifier.notify_subscribers(self.current_calibration)
        self.pending_calibration.clear()

    def load_last_calibration(self) -> None:
        """
        Loads the most recently created or loaded calibration into the interface instance. To be used on interface
        startup.
        """
        last_cal_path = get_setting(output_settings.INTERFACES_SETTINGS_GROUP, output_settings.ENGINEERING_PREFIX,
                                    "last_calibration_path")
        if last_cal_path:
            self.view.set_load_checked(True)
            self.view.set_file_text_with_search(last_cal_path)

    def set_field_value(self):
        self.view.set_sample_text(self.current_calibration.get_sample())

    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):
        # Do nothing if run numbers are invalid or view is searching.
        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_sample_valid():
            create_error_message(self.view, "Check run numbers/path is valid.")
            return False
        if self.view.get_crop_checked():
            if self.cropping_widget.get_custom_calfile_enabled() and not self.cropping_widget.is_calfile_valid():
                create_error_message(self.view, "Check custom calfile path is valid.")
                return False
            if self.cropping_widget.get_custom_spectra_enabled() and not self.cropping_widget.is_spectra_valid():
                create_error_message(self.view, "Check custom spectra are valid.")
                return False
        return True

    def validate_path(self):
        return self.view.get_path_valid()

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

    def set_calibrate_controls_enabled(self, enabled):
        self.view.set_calibrate_button_enabled(enabled)

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

    def _on_success(self, success_info):
        self.set_current_calibration(success_info)
        self.emit_enable_button_signal()

    def set_create_new_enabled(self, enabled):
        self.view.set_sample_enabled(enabled)
        if enabled:
            self.set_calibrate_button_text("Calibrate")
            self.view.set_check_plot_output_enabled(True)
            self.view.set_check_cropping_enabled(True)
            self.find_files()

    def set_load_existing_enabled(self, enabled):
        self.view.set_path_enabled(enabled)
        if enabled:
            self.set_calibrate_button_text("Load")
            self.view.set_check_plot_output_enabled(False)
            self.view.set_check_cropping_enabled(False)
            self.view.set_check_cropping_checked(False)

    def set_calibrate_button_text(self, text):
        self.view.set_calibrate_button_text(text)

    def find_files(self):
        self.view.find_sample_files()

    def show_cropping(self, show):
        self.view.set_cropping_widget_visibility(show)

    # -----------------------
    # Observers / Observables
    # -----------------------
    class CalibrationNotifier(Observable):
        def __init__(self, outer):
            Observable.__init__(self)
            self.outer = outer

        def notify_subscribers(self, *args, **kwargs):
            Observable.notify_subscribers(self, *args)
Esempio n. 29
0
class CodeCompleter(object):
    """
    This class generates autocompletions for Workbench's script editor.
    It generates autocompletions from environment globals. These completions
    are updated on every successful script execution.
    """
    def __init__(self, editor, env_globals=None):
        self.simpleapi_in_completions = False
        self.editor = editor
        self.env_globals = env_globals
        self.worker = None

        # A dict gives O(1) lookups and ensures we have no duplicates
        self._completions_dict = dict()
        if re.search("^#{0}import .*numpy( |,|$)", self.editor.text(), re.MULTILINE):
            self._add_to_completions(self._get_module_call_tips('numpy'))
        if re.search("^#{0}import .*pyplot( |,|$)", self.editor.text(), re.MULTILINE):
            with _ignore_matplotlib_deprecation_warnings():
                self._add_to_completions(self._get_module_call_tips('matplotlib.pyplot'))
        self._add_to_completions(python_keywords)

        self.editor.enableAutoCompletion(CodeEditor.AcsAPIs)
        self.editor.updateCompletionAPI(self.completions)

    @property
    def completions(self):
        return list(self._completions_dict.keys())

    def _get_completions_from_globals(self):
        return generate_call_tips(self.env_globals, prepend_module_name=True)

    def _add_to_completions(self, completions):
        for completion in completions:
            self._completions_dict[completion] = True

    def update_completion_api(self):
        with _ignore_matplotlib_deprecation_warnings():
            self._add_to_completions(self._get_completions_from_globals())
        self.editor.updateCompletionAPI(self.completions)

    def add_simpleapi_to_completions_if_required(self):
        """
        If the simpleapi functions haven't been added to the completions, start a separate thread to load them in.
        """
        if not self.simpleapi_in_completions and "from mantid.simpleapi import *" in self.editor.text():
            self.simpleapi_in_completions = True
            self.worker = AsyncTask(self._add_simpleapi_to_completions_if_required)
            self.worker.start()

    def _add_simpleapi_to_completions_if_required(self):
        self._add_to_completions(self._get_module_call_tips('mantid.simpleapi'))
        self.update_completion_api()

    def _get_module_call_tips(self, module):
        """
        Get the call tips for a given module. If the module cannot be
        found in sys.modules return an empty list
        :param str module: The name of the module
        :return list: A list of call tips for the module
        """
        try:
            module = sys.modules[module]
        except KeyError:
            return []
        module_name = get_module_import_alias(module.__name__, self.editor.text())
        return generate_call_tips(module.__dict__, module_name)
Esempio n. 30
0
class FittingDataPresenter(object):
    def __init__(self, model, view):
        self.model = model
        self.view = view
        self.worker = None
        self.iplot = []

        self.row_numbers = TwoWayRowDict(
        )  # {ws_name: table_row} and {table_row: ws_name}
        self.plotted = set()  # List of plotted workspace names

        # Connect view signals to local methods
        self.view.set_on_load_clicked(self.on_load_clicked)
        self.view.set_enable_load_button_connection(self._enable_load_button)
        self.view.set_enable_inspect_bg_button_connection(
            self._enable_inspect_bg_button)
        self.view.set_on_remove_selected_clicked(
            self._remove_selected_tracked_workspaces)
        self.view.set_on_remove_all_clicked(
            self._remove_all_tracked_workspaces)
        self.view.set_on_plotBG_clicked(self._plotBG)
        self.view.set_on_seq_fit_clicked(self._start_seq_fit)
        self.view.set_on_serial_fit_clicked(self._start_serial_fit)
        self.view.set_on_table_cell_changed(self._handle_table_cell_changed)
        self.view.set_on_bank_changed(self._update_file_filter)
        self.view.set_on_xunit_changed(self._update_file_filter)
        self.view.set_table_selection_changed(self._handle_selection_changed)

        # Observable Setup
        self.plot_added_notifier = GenericObservable()
        self.plot_removed_notifier = GenericObservable()
        self.all_plots_removed_notifier = GenericObservable()
        self.fit_all_started_notifier = GenericObservable()
        # Observers
        self.fit_observer = GenericObserverWithArgPassing(self.fit_completed)
        #
        self.fit_enabled_observer = GenericObserverWithArgPassing(
            self.set_fit_enabled)
        self.fit_all_done_observer = GenericObserverWithArgPassing(
            self.fit_completed)
        self.focus_run_observer = GenericObserverWithArgPassing(
            self.view.set_default_files)

    def set_fit_enabled(self, fit_enabled):
        self.view.set_fit_buttons_enabled(fit_enabled)

    def fit_completed(self, fit_props):
        self.model.update_fit(fit_props)

    def _start_seq_fit(self):
        ws_list = self.model.get_ws_sorted_by_primary_log()
        self.fit_all_started_notifier.notify_subscribers(ws_list,
                                                         do_sequential=True)

    def _start_serial_fit(self):
        ws_list = self.model.get_ws_list()
        self.fit_all_started_notifier.notify_subscribers(ws_list,
                                                         do_sequential=False)

    def _update_file_filter(self, bank, xunit):
        self.view.update_file_filter(bank, xunit)

    def on_load_clicked(self):
        if self._validate():
            filenames = self._get_filenames()
            self._start_load_worker(filenames)

    def remove_workspace(self, ws_name):
        if ws_name in self.get_loaded_workspaces():
            removed = self.get_loaded_workspaces().pop(ws_name)
            self.plot_removed_notifier.notify_subscribers(removed)
            self.plotted.discard(ws_name)
            self.model.remove_log_rows([self.row_numbers[ws_name]])
            self.model.update_log_workspace_group()
            self._repopulate_table()
        elif ws_name in self.model.get_log_workspaces_name():
            self.model.update_log_workspace_group()

    def rename_workspace(self, old_name, new_name):
        if old_name in self.get_loaded_workspaces():
            self.model.update_workspace_name(old_name, new_name)
            if old_name in self.plotted:
                self.plotted.remove(old_name)
                self.plotted.add(new_name)
            self._repopulate_table()
            self.model.update_log_workspace_group()  # so matches new table

    def clear_workspaces(self):
        self.get_loaded_workspaces().clear()
        self.get_bgsub_workspaces().clear()
        self.get_bg_params().clear()
        self.model.set_log_workspaces_none()
        self.plotted.clear()
        self.row_numbers.clear()
        self._repopulate_table()

    def replace_workspace(self, name, workspace):
        if name in self.get_loaded_workspaces():
            self.get_loaded_workspaces()[name] = workspace
            if name in self.plotted:
                self.all_plots_removed_notifier.notify_subscribers()
            self._repopulate_table()

    def get_loaded_workspaces(self):
        return self.model.get_loaded_workspaces()

    def get_bgsub_workspaces(self):
        return self.model.get_bgsub_workspaces()

    def get_bg_params(self):
        return self.model.get_bg_params()

    def restore_table(
        self
    ):  # used when the interface is being restored from a save or crash
        self._repopulate_table()

    def _start_load_worker(self, filenames):
        """
        Load one to many files into mantid that are tracked by the interface.
        :param filenames: Comma separated list of filenames to load
        """
        self.worker = AsyncTask(
            self.model.load_files, (filenames, ),
            error_cb=self._on_worker_error,
            finished_cb=self._emit_enable_load_button_signal,
            success_cb=self._on_worker_success)
        self.worker.start()

    def _on_worker_error(self, _):
        logger.error("Error occurred when loading files.")
        self._emit_enable_load_button_signal()

    def _on_worker_success(self, _):
        wsnames = self.model.get_last_added()
        if self.view.get_add_to_plot():
            self.plotted.update(wsnames)
        self._repopulate_table()
        # subtract background - has to be done post repopulation, can't change default in _add_row_to_table
        [
            self.view.set_item_checkstate(self.row_numbers[wsname], 3, True)
            for wsname in wsnames
        ]

    def _repopulate_table(self):
        """
        Populate the table with the information from the loaded workspaces.
        Will also handle any workspaces that need to be plotted.
        """
        self._remove_all_table_rows()
        self.row_numbers.clear()
        self.all_plots_removed_notifier.notify_subscribers()
        workspaces = self.get_loaded_workspaces()
        for i, name in enumerate(workspaces):
            try:
                run_no = self.model.get_sample_log_from_ws(name, "run_number")
                bank = self.model.get_sample_log_from_ws(name, "bankid")
                if bank == 0:
                    bank = "cropped"
                checked = name in self.plotted or name + "_bgsub" in self.plotted
                if name in self.model.get_bg_params():
                    self._add_row_to_table(name, i, run_no, bank, checked,
                                           *self.model.get_bg_params()[name])
                else:
                    self._add_row_to_table(name, i, run_no, bank, checked)
            except RuntimeError:
                self._add_row_to_table(name, i)
            self._handle_table_cell_changed(i, 2)

    def _remove_selected_tracked_workspaces(self):
        row_numbers = self._remove_selected_table_rows()
        self.model.remove_log_rows(row_numbers)
        for row_no in row_numbers:
            ws_name = self.row_numbers.pop(row_no)
            removed = self.get_loaded_workspaces().pop(ws_name)
            self.plot_removed_notifier.notify_subscribers(removed)
            self.plotted.discard(ws_name)
        self._repopulate_table()

    def _remove_all_tracked_workspaces(self):
        self.clear_workspaces()
        self.model.clear_logs()
        self._remove_all_table_rows()

    def _plotBG(self):
        # make external figure
        row_numbers = self.view.get_selected_rows()
        for row in row_numbers:
            ws_name = self.row_numbers[row]
            self.model.plot_background_figure(ws_name)

    def _handle_table_cell_changed(self, row, col):
        if row in self.row_numbers:
            ws_name = self.row_numbers[row]
            is_plotted = self.view.get_item_checked(row, 2)
            is_sub = self.view.get_item_checked(row, 3)
            if col == 2:
                # Plot check box
                if is_sub:
                    ws = self.model.get_bgsub_workspaces()[ws_name]
                    ws_name += "_bgsub"
                else:
                    ws = self.model.get_loaded_workspaces()[ws_name]
                if self.view.get_item_checked(row, col):  # Plot Box is checked
                    self.plot_added_notifier.notify_subscribers(ws)
                    self.plotted.add(ws_name)
                else:  # Plot box is unchecked
                    self.plot_removed_notifier.notify_subscribers(ws)
                    self.plotted.discard(ws_name)
            elif col == 3:
                # subtract bg col
                self.model.update_bgsub_status(ws_name, is_sub)
                if is_sub or is_plotted:  # this ensures the sub ws isn't made on load
                    bg_params = self.view.read_bg_params_from_table(row)
                    self.model.create_or_update_bgsub_ws(ws_name, bg_params)
                    self._update_plotted_ws_with_sub_state(ws_name, is_sub)
            elif col > 3:
                if is_sub:
                    # bg params changed - revaluate background
                    bg_params = self.view.read_bg_params_from_table(row)
                    self.model.create_or_update_bgsub_ws(ws_name, bg_params)

    def _update_plotted_ws_with_sub_state(self, ws_name, is_sub):
        ws = self.model.get_loaded_workspaces()[ws_name]
        ws_bgsub = self.model.get_bgsub_workspaces()[ws_name]
        if ws_name in self.plotted and is_sub:
            self.plot_removed_notifier.notify_subscribers(ws)
            self.plotted.discard(ws_name)
            self.plot_added_notifier.notify_subscribers(ws_bgsub)
            self.plotted.add(ws_name + "_bgsub")
        elif ws_name + "_bgsub" in self.plotted and not is_sub:
            self.plot_removed_notifier.notify_subscribers(ws_bgsub)
            self.plotted.discard(ws_name + "_bgsub")
            self.plot_added_notifier.notify_subscribers(ws)
            self.plotted.add(ws_name)

    def _handle_selection_changed(self):
        enable = True
        if not self.view.get_selected_rows():
            enable = False
        self._enable_inspect_bg_button(enable)

    def _enable_load_button(self, enabled):
        self.view.set_load_button_enabled(enabled)

    def _emit_enable_load_button_signal(self):
        self.view.sig_enable_load_button.emit(True)

    def _enable_inspect_bg_button(self, enabled):
        self.view.set_inspect_bg_button_enabled(enabled)

    def _get_filenames(self):
        return self.view.get_filenames_to_load()

    def _is_searching(self):
        return self.view.is_searching()

    def _files_are_valid(self):
        return self.view.get_filenames_valid()

    def _validate(self):
        if self._is_searching():
            create_error_message(
                self.view, "Mantid is searching for files. Please wait.")
            return False
        elif not self._files_are_valid():
            create_error_message(self.view, "Entered files are not valid.")
            return False
        return True

    def _add_row_to_table(self,
                          ws_name,
                          row,
                          run_no=None,
                          bank=None,
                          checked=False,
                          bgsub=False,
                          niter=100,
                          xwindow=None,
                          SG=True):

        words = ws_name.split("_")
        # find xwindow from ws xunit if not specified
        if not xwindow:
            ws = self.model.get_loaded_workspaces()[ws_name]
            if ws.getAxis(0).getUnit().unitID() == "TOF":
                xwindow = 1000
            else:
                xwindow = 0.05
        if run_no is not None and bank is not None:
            self.view.add_table_row(run_no, bank, checked, bgsub, niter,
                                    xwindow, SG)
        elif len(words) == 4 and words[2] == "bank":
            logger.notice(
                "No sample logs present, determining information from workspace name."
            )
            self.view.add_table_row(words[1], words[3], checked, bgsub, niter,
                                    xwindow, SG)
        else:
            logger.warning(
                "The workspace '{}' was not in the correct naming format. Files should be named in the following way: "
                "INSTRUMENT_RUNNUMBER_bank_BANK. Using workspace name as identifier."
                .format(ws_name))
            self.view.add_table_row(ws_name, "N/A", checked, bgsub, niter,
                                    xwindow, SG)
        self.row_numbers[ws_name] = row

    def _remove_table_row(self, row_no):
        self.view.remove_table_row(row_no)

    def _remove_selected_table_rows(self):
        return self.view.remove_selected()

    def _remove_all_table_rows(self):
        self.view.remove_all()
Esempio n. 31
0
class CalibrationPresenter(object):
    def __init__(self, model, view):
        self.model = model
        self.view = view
        self.view.set_on_calibrate_clicked(self.on_calibrate_clicked)
        self.worker = None
        self.instrument = "ENGINX"
        self.rb_num = None

    def on_calibrate_clicked(self):
        if not self.validate_run_numbers():
            return
        if self.view.is_searching():
            return
        vanadium_no = self.view.get_vanadium_filename()
        calib_no = self.view.get_calib_filename()
        plot_output = self.view.get_plot_output()
        self.start_calibration_worker(vanadium_no, calib_no, plot_output, self.rb_num)

    def start_calibration_worker(self, vanadium_path, calib_path, plot_output, rb_num):
        """
        Calibrate the data in a separate thread so as to not freeze the GUI.
        :param vanadium_path: Path to vanadium data file.
        :param calib_path: Path to calibration data file.
        :param plot_output: Whether to plot the output.
        :param rb_num: The current RB number set in the GUI.
        """
        self.worker = AsyncTask(self.model.create_new_calibration, (vanadium_path, calib_path),
                                {"plot_output": plot_output, "instrument": self.instrument, "rb_num": rb_num},
                                error_cb=self._on_error, finished_cb=self.enable_calibrate_buttons)
        self.disable_calibrate_buttons()
        self.worker.start()

    def set_instrument_override(self, instrument):
        if instrument == 0:
            instrument = "ENGINX"
        elif instrument == 1:
            instrument = "IMAT"
        else:
            raise ValueError("Invalid instrument index")
        self.view.set_instrument_override(instrument)
        self.instrument = instrument

    def set_rb_number(self, rb_number):
        self.rb_num = rb_number

    def validate_run_numbers(self):
        if self.view.get_calib_valid() and self.view.get_vanadium_valid():
            return True
        else:
            return False

    def disable_calibrate_buttons(self):
        self.view.set_calibrate_button_enabled(False)
        self.view.set_check_plot_output_enabled(False)

    def enable_calibrate_buttons(self):
        self.view.set_calibrate_button_enabled(True)
        self.view.set_check_plot_output_enabled(True)

    def _on_error(self, failure_info):
        logger.warning(str(failure_info))
        self.enable_calibrate_buttons()
Esempio n. 32
0
class CalibrationPresenter(object):
    def __init__(self, model, view):
        self.model = model
        self.view = view
        self.worker = None
        self.calibration_notifier = self.CalibrationNotifier(self)

        self.current_calibration = CalibrationInfo()
        self.pending_calibration = CalibrationInfo()

        self.connect_view_signals()

        # Main Window State Variables
        self.instrument = "ENGINX"
        self.rb_num = None

        # Cropping Options
        self.cropping_widget = CroppingWidget(
            self.view, view=self.view.get_cropping_widget())
        self.show_cropping(False)

    def connect_view_signals(self):
        self.view.set_on_calibrate_clicked(self.on_calibrate_clicked)
        self.view.set_enable_controls_connection(
            self.set_calibrate_controls_enabled)
        self.view.set_update_fields_connection(self.set_field_values)
        self.view.set_on_radio_new_toggled(self.set_create_new_enabled)
        self.view.set_on_radio_existing_toggled(self.set_load_existing_enabled)
        self.view.set_on_check_cropping_state_changed(self.show_cropping)

    def on_calibrate_clicked(self):
        plot_output = self.view.get_plot_output()
        if self.view.get_new_checked() and self._validate():
            vanadium_file = self.view.get_vanadium_filename()
            sample_file = self.view.get_sample_filename()
            if self.view.get_crop_checked():
                self.start_cropped_calibration_worker(vanadium_file,
                                                      sample_file, plot_output,
                                                      self.rb_num)
            else:
                self.start_calibration_worker(vanadium_file, sample_file,
                                              plot_output, self.rb_num)
        elif self.view.get_load_checked():
            if not self.validate_path():
                return
            filename = self.view.get_path_filename()
            instrument, vanadium_file, sample_file = self.model.load_existing_gsas_parameters(
                filename)
            self.pending_calibration.set_calibration(vanadium_file,
                                                     sample_file, instrument)
            self.set_current_calibration()

    def start_calibration_worker(self,
                                 vanadium_path,
                                 sample_path,
                                 plot_output,
                                 rb_num,
                                 bank=None,
                                 spectrum_numbers=None):
        """
        Calibrate the data in a separate thread so as to not freeze the GUI.
        :param vanadium_path: Path to vanadium data file.
        :param sample_path: Path to sample data file.
        :param plot_output: Whether to plot the output.
        :param rb_num: The current RB number set in the GUI.
        :param bank: Optional parameter to crop by bank.
        :param spectrum_numbers: Optional parameter to crop by spectrum number.
        """
        self.worker = AsyncTask(self.model.create_new_calibration,
                                (vanadium_path, sample_path), {
                                    "plot_output": plot_output,
                                    "instrument": self.instrument,
                                    "rb_num": rb_num,
                                    "bank": bank,
                                    "spectrum_numbers": spectrum_numbers
                                },
                                error_cb=self._on_error,
                                success_cb=self._on_success)
        self.pending_calibration.set_calibration(vanadium_path, sample_path,
                                                 self.instrument)
        self.set_calibrate_controls_enabled(False)
        self.worker.start()

    def start_cropped_calibration_worker(self, vanadium_path, sample_path,
                                         plot_output, rb_num):
        if self.cropping_widget.is_custom():
            spec_nums = self.cropping_widget.get_custom_spectra()
            self.start_calibration_worker(vanadium_path,
                                          sample_path,
                                          plot_output,
                                          rb_num,
                                          spectrum_numbers=spec_nums)
        else:
            bank = self.cropping_widget.get_bank()
            self.start_calibration_worker(vanadium_path,
                                          sample_path,
                                          plot_output,
                                          rb_num,
                                          bank=bank)

    def set_current_calibration(self, success_info=None):
        if success_info:
            logger.information("Thread executed in " +
                               str(success_info.elapsed_time) + " seconds.")
        self.current_calibration = deepcopy(self.pending_calibration)
        self.calibration_notifier.notify_subscribers(self.current_calibration)
        self.emit_update_fields_signal()
        self.pending_calibration.clear()

    def set_field_values(self):
        self.view.set_sample_text(self.current_calibration.get_sample())
        self.view.set_vanadium_text(self.current_calibration.get_vanadium())

    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):
        # Do nothing if run numbers are invalid or view is searching.
        if self.view.is_searching():
            create_error_message(
                self.view, "Mantid is searching for data files. Please wait.")
            return False
        if not self.validate_run_numbers():
            create_error_message(self.view, "Check run numbers/path is valid.")
            return False
        if self.view.get_crop_checked(
        ) and not self.cropping_widget.is_valid():
            create_error_message(self.view, "Check cropping values are valid.")
            return False
        return True

    def validate_run_numbers(self):
        return self.view.get_sample_valid() and self.view.get_vanadium_valid()

    def validate_path(self):
        return self.view.get_path_valid()

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

    def emit_update_fields_signal(self):
        self.view.sig_update_fields.emit()

    def set_calibrate_controls_enabled(self, enabled):
        self.view.set_calibrate_button_enabled(enabled)
        self.view.set_check_plot_output_enabled(enabled)

    def _on_error(self, _):
        self.emit_enable_button_signal()

    def _on_success(self, success_info):
        self.set_current_calibration(success_info)
        self.emit_enable_button_signal()

    def set_create_new_enabled(self, enabled):
        self.view.set_vanadium_enabled(enabled)
        self.view.set_sample_enabled(enabled)
        if enabled:
            self.set_calibrate_button_text("Calibrate")
            self.view.set_check_plot_output_enabled(True)
            self.view.set_check_cropping_enabled(True)
            self.find_files()

    def set_load_existing_enabled(self, enabled):
        self.view.set_path_enabled(enabled)
        if enabled:
            self.set_calibrate_button_text("Load")
            self.view.set_check_plot_output_enabled(False)
            self.view.set_check_cropping_enabled(False)
            self.view.set_check_cropping_checked(False)

    def set_calibrate_button_text(self, text):
        self.view.set_calibrate_button_text(text)

    def find_files(self):
        self.view.find_sample_files()
        self.view.find_vanadium_files()

    def show_cropping(self, show):
        self.view.set_cropping_widget_visibility(show)

    # -----------------------
    # Observers / Observables
    # -----------------------
    class CalibrationNotifier(Observable):
        def __init__(self, outer):
            Observable.__init__(self)
            self.outer = outer

        def notify_subscribers(self, *args, **kwargs):
            Observable.notify_subscribers(self, *args)
Esempio n. 33
0
class FocusPresenter(object):
    def __init__(self, model, view):
        self.model = model
        self.view = view
        self.worker = None
        self.calibration_observer = self.CalibrationObserver(self)

        # 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)
        self.view.set_on_check_cropping_state_changed(self.show_cropping)

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

        # Cropping Options
        self.cropping_widget = CroppingWidget(self.view, view=self.view.get_cropping_widget())
        self.show_cropping(False)

    def on_focus_clicked(self):
        if not self._validate():
            return
        banks, spectrum_numbers = self._get_banks()
        focus_paths = self.view.get_focus_filenames()
        if self._number_of_files_warning(focus_paths):
            self.start_focus_worker(focus_paths, banks, self.view.get_plot_output(), self.rb_num, spectrum_numbers)

    def start_focus_worker(self, focus_paths, banks, plot_output, rb_num, spectrum_numbers=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 banks: A list of banks that are to be focused.
        :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 spectrum_numbers: Optional parameter to crop to a specific list of spectrum numbers.
        """
        self.worker = AsyncTask(self.model.focus_run,
                                (focus_paths, banks, plot_output, self.instrument, rb_num, spectrum_numbers),
                                error_cb=self._on_worker_error,
                                finished_cb=self.emit_enable_button_signal)
        self.set_focus_controls_enabled(False)
        self.worker.start()

    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 check_workspaces_exist() or 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
        if self.view.get_crop_checked() and not self.cropping_widget.is_valid():
            create_error_message(self.view, "Check cropping values are valid.")
            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, _):
        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 _get_banks(self):
        if self.view.get_crop_checked():
            if self.cropping_widget.is_custom():
                return None, self.cropping_widget.get_custom_spectra()
            else:
                return [self.cropping_widget.get_bank()], None
        else:
            return ["1", "2"], None

    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

    def show_cropping(self, visible):
        self.view.set_cropping_widget_visibility(visible)

    # -----------------------
    # Observers / Observables
    # -----------------------
    class CalibrationObserver(Observer):
        def __init__(self, outer):
            Observer.__init__(self)
            self.outer = outer

        def update(self, observable, calibration):
            self.outer.update_calibration(calibration)
Esempio n. 34
0
class CalibrationPresenter(object):
    def __init__(self, model, view):
        self.model = model
        self.view = view
        self.worker = None
        self.calibration_notifier = self.CalibrationNotifier(self)

        self.current_calibration = CalibrationInfo()

        self.connect_view_signals()

        # Main Window State Variables
        self.instrument = "ENGINX"
        self.rb_num = None

        # Cropping Options
        self.cropping_widget = CroppingPresenter(parent=self.view, view=self.view.get_cropping_widget())
        self.show_cropping(False)

    def connect_view_signals(self):
        self.view.set_on_calibrate_clicked(self.on_calibrate_clicked)
        self.view.set_enable_controls_connection(self.set_calibrate_controls_enabled)
        self.view.set_update_field_connection(self.set_field_value)
        self.view.set_on_radio_new_toggled(self.set_create_new_enabled)
        self.view.set_on_radio_existing_toggled(self.set_load_existing_enabled)
        self.view.set_on_check_cropping_state_changed(self.show_cropping)

    def update_calibration_from_view(self):
        self.current_calibration.clear()
        if self.view.get_load_checked():
            # loading calibration from path to .prm
            self.current_calibration.set_calibration_from_prm_fname(self.view.get_path_filename())
        else:
            # make a new calibration
            sample_file = self.view.get_sample_filename()
            self.current_calibration.set_calibration_paths(self.instrument, sample_file)
            # set group and any additional parameters needed
            if self.view.get_crop_checked():
                self.current_calibration.set_group(self.cropping_widget.get_group())
                if self.current_calibration.group == GROUP.CUSTOM:
                    self.current_calibration.set_cal_file(self.cropping_widget.get_custom_calfile())
                elif self.current_calibration.group == GROUP.CROPPED:
                    self.current_calibration.set_spectra_list(self.cropping_widget.get_custom_spectra())
            else:
                # default if no cropping
                self.current_calibration.set_group(GROUP.BOTH)

    def on_calibrate_clicked(self):
        if self.view.get_new_checked() and self._validate():
            self.update_calibration_from_view()
            self.start_calibration_worker(self.view.get_plot_output())
        elif self.view.get_load_checked() and self.validate_path():
            self.update_calibration_from_view()
            self.model.load_existing_calibration_files(self.current_calibration)
            self._notify_updated_calibration()

    def start_calibration_worker(self, plot_output):
        """
        Calibrate the data in a separate thread so as to not freeze the GUI.
        """
        self.worker = AsyncTask(self.model.create_new_calibration,
                                (self.current_calibration, self.rb_num, plot_output),
                                error_cb=self._on_error,
                                success_cb=self._on_success)
        self.set_calibrate_controls_enabled(False)
        self.worker.start()

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

    def _on_success(self, success_info):
        self._notify_updated_calibration()
        self.emit_enable_button_signal()

    def _notify_updated_calibration(self):
        self.calibration_notifier.notify_subscribers(self.current_calibration)
        set_setting(output_settings.INTERFACES_SETTINGS_GROUP, output_settings.ENGINEERING_PREFIX,
                    "last_calibration_path", self.current_calibration.get_prm_filepath())

    def set_field_value(self):
        self.view.set_sample_text(self.current_calibration.get_sample())

    def load_last_calibration(self) -> None:
        """
        Loads the most recently created or loaded calibration into the interface instance. To be used on interface
        startup.
        """
        last_cal_path = get_setting(output_settings.INTERFACES_SETTINGS_GROUP, output_settings.ENGINEERING_PREFIX,
                                    "last_calibration_path")
        if last_cal_path:
            self.view.set_load_checked(True)
            self.view.set_file_text_with_search(last_cal_path)

    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):
        # Do nothing if run numbers are invalid or view is searching.
        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_sample_valid():
            create_error_message(self.view, "Check run numbers/path is valid.")
            return False
        if self.view.get_crop_checked():
            if self.cropping_widget.get_custom_calfile_enabled() and not self.cropping_widget.is_calfile_valid():
                create_error_message(self.view, "Check custom calfile path is valid.")
                return False
            if self.cropping_widget.get_custom_spectra_enabled() and not self.cropping_widget.is_spectra_valid():
                create_error_message(self.view, "Check custom spectra are valid.")
                return False
        return True

    def validate_path(self):
        return self.view.get_path_valid()

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

    def set_calibrate_controls_enabled(self, enabled):
        self.view.set_calibrate_button_enabled(enabled)

    def set_create_new_enabled(self, enabled):
        self.view.set_sample_enabled(enabled)
        if enabled:
            self.set_calibrate_button_text("Calibrate")
            self.view.set_check_plot_output_enabled(True)
            self.view.set_check_cropping_enabled(True)
            self.find_files()

    def set_load_existing_enabled(self, enabled):
        self.view.set_path_enabled(enabled)
        if enabled:
            self.set_calibrate_button_text("Load")
            self.view.set_check_plot_output_enabled(False)
            self.view.set_check_cropping_enabled(False)
            self.view.set_check_cropping_checked(False)

    def set_calibrate_button_text(self, text):
        self.view.set_calibrate_button_text(text)

    def find_files(self):
        self.view.find_sample_files()

    def show_cropping(self, show):
        self.view.set_cropping_widget_visibility(show)

    # -----------------------
    # Observers / Observables
    # -----------------------
    class CalibrationNotifier(Observable):
        def __init__(self, outer):
            Observable.__init__(self)
            self.outer = outer

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