def __init__(self, facility, view=None): super(RunTabPresenter, self).__init__() self._facility = facility # Logger self.sans_logger = Logger("SANS") # Name of grpah to output to self.output_graph = 'SANS-Latest' self.progress = 0 # Models that are being used by the presenter self._state_model = None self._table_model = TableModel() # Presenter needs to have a handle on the view since it delegates it self._view = None self.set_view(view) self._processing = False self.work_handler = WorkHandler() self.batch_process_runner = BatchProcessRunner(self.notify_progress, self.on_processing_finished, self.on_processing_error) # File information for the first input self._file_information = None self._clipboard = [] # Settings diagnostic tab presenter self._settings_diagnostic_tab_presenter = SettingsDiagnosticPresenter(self) # Masking table presenter self._masking_table_presenter = MaskingTablePresenter(self) # Beam centre presenter self._beam_centre_presenter = BeamCentrePresenter(self, WorkHandler, BeamCentreModel, SANSCentreFinder) # Workspace Diagnostic page presenter self._workspace_diagnostic_presenter = DiagnosticsPagePresenter(self, WorkHandler, run_integral, create_state, self._facility)
def setUp(self): self.parent_presenter = create_run_tab_presenter_mock(use_fake_state = False) self.view = create_mock_diagnostics_tab() self.state = mock.MagicMock() self.create_state = mock.MagicMock(return_value = self.state) self.WorkHandler = mock.MagicMock() self.run_integral = mock.MagicMock() self.presenter = DiagnosticsPagePresenter(self.parent_presenter, self.WorkHandler, self.run_integral, self.create_state, SANSFacility.ISIS) self.presenter.set_view(self.view, SANSInstrument.LARMOR)
def setUp(self): self.parent_presenter = create_run_tab_presenter_mock(use_fake_state = False) self.view = create_mock_diagnostics_tab() self.presenter = DiagnosticsPagePresenter(self.parent_presenter, SANSFacility.ISIS) self.presenter.set_view(self.view, SANSInstrument.LARMOR) # Inject mocks to things tested elsewhere self.presenter._worker = mock.create_autospec(self.presenter._worker) self.presenter._model = mock.create_autospec(self.presenter._model) self.mock_state = self.presenter._model.create_state.return_value
class DiagnosticsPagePresenterTest(unittest.TestCase): def setUp(self): self.parent_presenter = create_run_tab_presenter_mock( use_fake_state=False) self.view = create_mock_diagnostics_tab() self.state = mock.MagicMock() self.create_state = mock.MagicMock(return_value=self.state) self.WorkHandler = mock.MagicMock() self.run_integral = mock.MagicMock() self.presenter = DiagnosticsPagePresenter(self.parent_presenter, self.WorkHandler, self.run_integral, self.create_state, SANSFacility.ISIS) self.presenter.set_view(self.view, SANSInstrument.LARMOR) def test_that_on_horizontal_clicked_calls_work_handler_with_correct_parameters( self): self.presenter.on_horizontal_clicked() self.assertEqual(self.presenter._work_handler.process.call_count, 1) self.assertEqual(self.presenter._work_handler.process.call_args[0][1], self.run_integral) self.assertEqual(self.presenter._work_handler.process.call_args[0][3], self.presenter._view.horizontal_range) self.assertEqual(self.presenter._work_handler.process.call_args[0][4], self.presenter._view.horizontal_mask) self.assertEqual(self.presenter._work_handler.process.call_args[0][5], IntegralEnum.Horizontal) self.assertEqual(self.presenter._work_handler.process.call_args[0][6], DetectorType.LAB) self.assertEqual(self.presenter._work_handler.process.call_args[0][7], self.state) def test_that_on_vertical_clicked_calls_work_handler_with_correct_parameters( self): self.presenter.on_vertical_clicked() self.assertEqual(self.presenter._work_handler.process.call_count, 1) self.assertEqual(self.presenter._work_handler.process.call_args[0][1], self.run_integral) self.assertEqual(self.presenter._work_handler.process.call_args[0][3], self.presenter._view.vertical_range) self.assertEqual(self.presenter._work_handler.process.call_args[0][4], self.presenter._view.vertical_mask) self.assertEqual(self.presenter._work_handler.process.call_args[0][5], IntegralEnum.Vertical) self.assertEqual(self.presenter._work_handler.process.call_args[0][6], DetectorType.LAB) self.assertEqual(self.presenter._work_handler.process.call_args[0][7], self.state) def test_that_on_time_clicked_calls_work_handler_with_correct_parameters( self): self.presenter.on_time_clicked() self.assertEqual(self.presenter._work_handler.process.call_count, 1) self.assertEqual(self.presenter._work_handler.process.call_args[0][1], self.run_integral) self.assertEqual(self.presenter._work_handler.process.call_args[0][3], self.presenter._view.time_range) self.assertEqual(self.presenter._work_handler.process.call_args[0][4], self.presenter._view.time_mask) self.assertEqual(self.presenter._work_handler.process.call_args[0][5], IntegralEnum.Time) self.assertEqual(self.presenter._work_handler.process.call_args[0][6], DetectorType.LAB) self.assertEqual(self.presenter._work_handler.process.call_args[0][7], self.state) def test_that_on_user_file_load_sets_user_file_name_on_view(self): user_file_name = 'user_file_name' self.presenter.on_user_file_load(user_file_name) self.assertEqual(self.presenter._view.user_file_name, user_file_name) def test_that_set_instrument_settings_calls_set_detectors_on_view(self): self.presenter.set_instrument_settings(SANSInstrument.SANS2D) self.presenter._view.set_detectors.assert_called_with( ['rear', 'front']) self.assertEqual(self.presenter._view.set_detectors.call_count, 2)
class DiagnosticsPagePresenterTest(unittest.TestCase): def setUp(self): self.parent_presenter = create_run_tab_presenter_mock(use_fake_state = False) self.view = create_mock_diagnostics_tab() self.state = mock.MagicMock() self.create_state = mock.MagicMock(return_value = self.state) self.WorkHandler = mock.MagicMock() self.run_integral = mock.MagicMock() self.presenter = DiagnosticsPagePresenter(self.parent_presenter, self.WorkHandler, self.run_integral, self.create_state, SANSFacility.ISIS) self.presenter.set_view(self.view, SANSInstrument.LARMOR) def test_that_on_horizontal_clicked_calls_work_handler_with_correct_parameters(self): self.presenter.on_horizontal_clicked() self.assertEqual(self.presenter._work_handler.process.call_count, 1) self.assertEqual(self.presenter._work_handler.process.call_args[0][1], self.run_integral) self.assertEqual(self.presenter._work_handler.process.call_args[0][3], self.presenter._view.horizontal_range) self.assertEqual(self.presenter._work_handler.process.call_args[0][4], self.presenter._view.horizontal_mask) self.assertEqual(self.presenter._work_handler.process.call_args[0][5], IntegralEnum.Horizontal) self.assertEqual(self.presenter._work_handler.process.call_args[0][6], DetectorType.LAB) self.assertEqual(self.presenter._work_handler.process.call_args[0][7], self.state) def test_that_on_vertical_clicked_calls_work_handler_with_correct_parameters(self): self.presenter.on_vertical_clicked() self.assertEqual(self.presenter._work_handler.process.call_count, 1) self.assertEqual(self.presenter._work_handler.process.call_args[0][1], self.run_integral) self.assertEqual(self.presenter._work_handler.process.call_args[0][3], self.presenter._view.vertical_range) self.assertEqual(self.presenter._work_handler.process.call_args[0][4], self.presenter._view.vertical_mask) self.assertEqual(self.presenter._work_handler.process.call_args[0][5], IntegralEnum.Vertical) self.assertEqual(self.presenter._work_handler.process.call_args[0][6], DetectorType.LAB) self.assertEqual(self.presenter._work_handler.process.call_args[0][7], self.state) def test_that_on_time_clicked_calls_work_handler_with_correct_parameters(self): self.presenter.on_time_clicked() self.assertEqual(self.presenter._work_handler.process.call_count, 1) self.assertEqual(self.presenter._work_handler.process.call_args[0][1], self.run_integral) self.assertEqual(self.presenter._work_handler.process.call_args[0][3], self.presenter._view.time_range) self.assertEqual(self.presenter._work_handler.process.call_args[0][4], self.presenter._view.time_mask) self.assertEqual(self.presenter._work_handler.process.call_args[0][5], IntegralEnum.Time) self.assertEqual(self.presenter._work_handler.process.call_args[0][6], DetectorType.LAB) self.assertEqual(self.presenter._work_handler.process.call_args[0][7], self.state) def test_that_on_user_file_load_sets_user_file_name_on_view(self): user_file_name = 'user_file_name' self.presenter.on_user_file_load(user_file_name) self.assertEqual(self.presenter._view.user_file_name, user_file_name) def test_that_set_instrument_settings_calls_set_detectors_on_view(self): self.presenter.set_instrument_settings(SANSInstrument.SANS2D) self.presenter._view.set_detectors.assert_called_with(['rear', 'front']) self.assertEqual(self.presenter._view.set_detectors.call_count, 2)
class RunTabPresenter(object): class ConcreteRunTabListener(SANSDataProcessorGui.RunTabListener): def __init__(self, presenter): super(RunTabPresenter.ConcreteRunTabListener, self).__init__() self._presenter = presenter def on_user_file_load(self): self._presenter.on_user_file_load() def on_mask_file_add(self): self._presenter.on_mask_file_add() def on_batch_file_load(self): self._presenter.on_batch_file_load() def on_processed_clicked(self): self._presenter.on_processed_clicked() def on_multi_period_selection(self, show_periods): self._presenter.on_multiperiod_changed(show_periods) def on_data_changed(self, row, column, new_value, old_value): self._presenter.on_data_changed(row, column, new_value, old_value) def on_manage_directories(self): self._presenter.on_manage_directories() def on_instrument_changed(self): self._presenter.on_instrument_changed() def on_row_inserted(self, index, row): self._presenter.on_row_inserted(index, row) def on_rows_removed(self, rows): self._presenter.on_rows_removed(rows) def on_copy_rows_requested(self): self._presenter.on_copy_rows_requested() def on_paste_rows_requested(self): self._presenter.on_paste_rows_requested() def on_insert_row(self): self._presenter.on_insert_row() def on_erase_rows(self): self._presenter.on_erase_rows() def on_cut_rows(self): self._presenter.on_cut_rows_requested() class ProcessListener(WorkHandler.WorkListener): def __init__(self, presenter): super(RunTabPresenter.ProcessListener, self).__init__() self._presenter = presenter def on_processing_finished(self, result): self._presenter.on_processing_finished(result) def on_processing_error(self, error): self._presenter.on_processing_error(error) def __init__(self, facility, view=None): super(RunTabPresenter, self).__init__() self._facility = facility # Logger self.sans_logger = Logger("SANS") # Name of grpah to output to self.output_graph = 'SANS-Latest' self.progress = 0 # Models that are being used by the presenter self._state_model = None self._table_model = TableModel() # Presenter needs to have a handle on the view since it delegates it self._view = None self.set_view(view) self._processing = False self.work_handler = WorkHandler() self.batch_process_runner = BatchProcessRunner( self.notify_progress, self.on_processing_finished, self.on_processing_error) # File information for the first input self._file_information = None self._clipboard = [] # Settings diagnostic tab presenter self._settings_diagnostic_tab_presenter = SettingsDiagnosticPresenter( self) # Masking table presenter self._masking_table_presenter = MaskingTablePresenter(self) # Beam centre presenter self._beam_centre_presenter = BeamCentrePresenter( self, WorkHandler, BeamCentreModel, SANSCentreFinder) # Workspace Diagnostic page presenter self._workspace_diagnostic_presenter = DiagnosticsPagePresenter( self, WorkHandler, run_integral, create_state, self._facility) def _default_gui_setup(self): """ Provides a default setup of the GUI. This is important for the initial start up, when the view is being set. """ # Set the possible reduction modes reduction_mode_list = get_reduction_mode_strings_for_gui() self._view.set_reduction_modes(reduction_mode_list) # Set the possible instruments instrument_list = get_instrument_strings_for_gui() self._view.set_instruments(instrument_list) # Set the step type options for wavelength range_step_types = [ RangeStepType.to_string(RangeStepType.Lin), RangeStepType.to_string(RangeStepType.Log), RangeStepType.to_string(RangeStepType.RangeLog), RangeStepType.to_string(RangeStepType.RangeLin) ] self._view.wavelength_step_type = range_step_types # Set the geometry options. This needs to include the option to read the sample shape from file. sample_shape = [ "Read from file", SampleShape.Cylinder, SampleShape.FlatPlate, SampleShape.Disc ] self._view.sample_shape = sample_shape # Set the q range self._view.q_1d_step_type = [ RangeStepType.to_string(RangeStepType.Lin), RangeStepType.to_string(RangeStepType.Log) ] self._view.q_xy_step_type = [ RangeStepType.to_string(RangeStepType.Lin) ] # Set the fit options fit_types = [ FitType.to_string(FitType.Linear), FitType.to_string(FitType.Logarithmic), FitType.to_string(FitType.Polynomial) ] self._view.transmission_sample_fit_type = fit_types self._view.transmission_can_fit_type = fit_types # ------------------------------------------------------------------------------------------------------------------ # Table + Actions # ------------------------------------------------------------------------------------------------------------------ def set_view(self, view): """ Sets the view :param view: the view is the SANSDataProcessorGui. The presenter needs to access some of the API """ if view is not None: self._view = view # Add a listener to the view listener = RunTabPresenter.ConcreteRunTabListener(self) self._view.add_listener(listener) # Default gui setup self._default_gui_setup() # Set appropriate view for the state diagnostic tab presenter self._settings_diagnostic_tab_presenter.set_view( self._view.settings_diagnostic_tab) # Set appropriate view for the masking table presenter self._masking_table_presenter.set_view(self._view.masking_table) # Set the appropriate view for the beam centre presenter self._beam_centre_presenter.set_view(self._view.beam_centre) # Set the appropriate view for the diagnostic page self._workspace_diagnostic_presenter.set_view( self._view.diagnostic_page, self._view.instrument) self._view.setup_layout() self._view.set_hinting_line_edit_for_column( 15, self._table_model.get_options_hint_strategy()) def on_user_file_load(self): """ Loads the user file. Populates the models and the view. """ try: # 1. Get the user file path from the view user_file_path = self._view.get_user_file_path() if not user_file_path: return # 2. Get the full file path user_file_path = FileFinder.getFullPath(user_file_path) if not os.path.exists(user_file_path): raise RuntimeError( "The user path {} does not exist. Make sure a valid user file path" " has been specified.".format(user_file_path)) self._table_model.user_file = user_file_path # Clear out the current view self._view.reset_all_fields_to_default() # 3. Read and parse the user file user_file_reader = UserFileReader(user_file_path) user_file_items = user_file_reader.read_user_file() # 4. Populate the model self._state_model = StateGuiModel(user_file_items) # 5. Update the views. self._update_view_from_state_model() self._beam_centre_presenter.update_centre_positions( self._state_model) self._beam_centre_presenter.on_update_rows() self._masking_table_presenter.on_update_rows() self._workspace_diagnostic_presenter.on_user_file_load( user_file_path) except Exception as e: self.sans_logger.error( "Loading of the user file failed. {}".format(str(e))) self.display_warning_box('Warning', 'Loading of the user file failed.', str(e)) def on_batch_file_load(self): """ Loads a batch file and populates the batch table based on that. """ try: # 1. Get the batch file from the view batch_file_path = self._view.get_batch_file_path() if not batch_file_path: return if not os.path.exists(batch_file_path): raise RuntimeError( "The batch file path {} does not exist. Make sure a valid batch file path" " has been specified.".format(batch_file_path)) self._table_model.batch_file = batch_file_path # 2. Read the batch file batch_file_parser = BatchCsvParser(batch_file_path) parsed_rows = batch_file_parser.parse_batch_file() # 3. Populate the table self._table_model.clear_table_entries() for index, row in enumerate(parsed_rows): self._add_row_to_table_model(row, index) self._table_model.remove_table_entries([len(parsed_rows)]) self.update_view_from_table_model() self._beam_centre_presenter.on_update_rows() self._masking_table_presenter.on_update_rows() except RuntimeError as e: self.sans_logger.error( "Loading of the batch file failed. {}".format(str(e))) self.display_warning_box('Warning', 'Loading of the batch file failed', str(e)) def _add_row_to_table_model(self, row, index): """ Adds a row to the table """ def get_string_entry(_tag, _row): _element = "" if _tag in _row: _element = _row[_tag] return _element def get_string_period(_tag): return "" if _tag == ALL_PERIODS else str(_tag) # 1. Pull out the entries sample_scatter = get_string_entry(BatchReductionEntry.SampleScatter, row) sample_scatter_period = get_string_period( get_string_entry(BatchReductionEntry.SampleScatterPeriod, row)) sample_transmission = get_string_entry( BatchReductionEntry.SampleTransmission, row) sample_transmission_period = \ get_string_period(get_string_entry(BatchReductionEntry.SampleTransmissionPeriod, row)) sample_direct = get_string_entry(BatchReductionEntry.SampleDirect, row) sample_direct_period = get_string_period( get_string_entry(BatchReductionEntry.SampleDirectPeriod, row)) can_scatter = get_string_entry(BatchReductionEntry.CanScatter, row) can_scatter_period = get_string_period( get_string_entry(BatchReductionEntry.CanScatterPeriod, row)) can_transmission = get_string_entry( BatchReductionEntry.CanTransmission, row) can_transmission_period = get_string_period( get_string_entry(BatchReductionEntry.CanScatterPeriod, row)) can_direct = get_string_entry(BatchReductionEntry.CanDirect, row) can_direct_period = get_string_period( get_string_entry(BatchReductionEntry.CanDirectPeriod, row)) output_name = get_string_entry(BatchReductionEntry.Output, row) file_information_factory = SANSFileInformationFactory() file_information = file_information_factory.create_sans_file_information( sample_scatter) sample_thickness = file_information._thickness user_file = get_string_entry(BatchReductionEntry.UserFile, row) row_entry = [ sample_scatter, sample_scatter_period, sample_transmission, sample_transmission_period, sample_direct, sample_direct_period, can_scatter, can_scatter_period, can_transmission, can_transmission_period, can_direct, can_direct_period, output_name, user_file, sample_thickness, '' ] table_index_model = TableIndexModel(*row_entry) self._table_model.add_table_entry(index, table_index_model) def update_view_from_table_model(self): self._view.clear_table() self._view.hide_period_columns() for row_index, row in enumerate(self._table_model._table_entries): row_entry = [str(x) for x in row.to_list()] self._view.add_row(row_entry) self._view.change_row_color( row_state_to_colour_mapping[row.row_state], row_index + 1) self._view.set_row_tooltip(row.tool_tip, row_index + 1) if row.isMultiPeriod(): self._view.show_period_columns() self._view.remove_rows([0]) self._view.clear_selection() def on_data_changed(self, row, column, new_value, old_value): self._table_model.update_table_entry(row, column, new_value) self._view.change_row_color( row_state_to_colour_mapping[RowState.Unprocessed], row) self._view.set_row_tooltip('', row) self._beam_centre_presenter.on_update_rows() self._masking_table_presenter.on_update_rows() def on_instrument_changed(self): self._setup_instrument_specific_settings() def on_processed_clicked(self): """ Prepares the batch reduction. 0. Validate rows and create dummy workspace if it does not exist 1. Sets up the states 2. Adds a dummy input workspace 3. Adds row index information """ try: self._view.disable_buttons() self._processing = True self.sans_logger.information("Starting processing of batch table.") # 1. Set up the states and convert them into property managers selected_rows = self._view.get_selected_rows() selected_rows = selected_rows if selected_rows else range( self._table_model.get_number_of_rows()) for row in selected_rows: self._table_model.reset_row_state(row) self.update_view_from_table_model() states, errors = self.get_states(row_index=selected_rows) for row, error in errors.items(): self.on_processing_error(row, error) if not states: self.on_processing_finished(None) return # 4. Create the graph if continuous output is specified if mantidplot: if self._view.plot_results and not mantidplot.graph( self.output_graph): mantidplot.newGraph(self.output_graph) # Check if optimizations should be used use_optimizations = self._view.use_optimizations # Get the output mode output_mode = self._view.output_mode # Check if results should be plotted plot_results = self._view.plot_results # Get the name of the graph to output to output_graph = self.output_graph self.progress = 0 setattr(self._view, 'progress_bar_value', self.progress) setattr(self._view, 'progress_bar_maximum', len(states)) self.batch_process_runner.process_states(states, use_optimizations, output_mode, plot_results, output_graph) except Exception as e: self._view.enable_buttons() self.sans_logger.error("Process halted due to: {}".format(str(e))) self.display_warning_box('Warning', 'Process halted', str(e)) def on_multiperiod_changed(self, show_periods): if show_periods: self._view.show_period_columns() else: self._view.hide_period_columns() def display_warning_box(self, title, text, detailed_text): self._view.display_message_box(title, text, detailed_text) def notify_progress(self, row): self.increment_progress() message = '' self._table_model.set_row_to_processed(row, message) self.update_view_from_table_model() def on_processing_finished(self, result): self._view.enable_buttons() self._processing = False def on_processing_error(self, row, error_msg): self.increment_progress() self._table_model.set_row_to_error(row, error_msg) self.update_view_from_table_model() def increment_progress(self): self.progress = self.progress + 1 setattr(self._view, 'progress_bar_value', self.progress) def on_row_inserted(self, index, row): row_table_index = TableIndexModel(*row) self._table_model.add_table_entry(index, row_table_index) def on_insert_row(self): selected_rows = self._view.get_selected_rows() selected_row = selected_rows[ 0] + 1 if selected_rows else self._table_model.get_number_of_rows( ) table_entry_row = self._table_model.create_empty_row() self._table_model.add_table_entry(selected_row, table_entry_row) self.update_view_from_table_model() def on_erase_rows(self): selected_rows = self._view.get_selected_rows() empty_row = TableModel.create_empty_row() for row in selected_rows: self._table_model.replace_table_entries([row], [empty_row]) self.update_view_from_table_model() def on_rows_removed(self, rows): self._table_model.remove_table_entries(rows) self.update_view_from_table_model() def on_copy_rows_requested(self): selected_rows = self._view.get_selected_rows() self._clipboard = [] for row in selected_rows: data_from_table_model = self._table_model.get_table_entry( row).to_list() self._clipboard.append(data_from_table_model) def on_cut_rows_requested(self): self.on_copy_rows_requested() rows = self._view.get_selected_rows() self.on_rows_removed(rows) def on_paste_rows_requested(self): if self._clipboard: selected_rows = self._view.get_selected_rows() selected_rows = selected_rows if selected_rows else [ self._table_model.get_number_of_rows() ] replacement_table_index_models = [ TableIndexModel(*x) for x in self._clipboard ] self._table_model.replace_table_entries( selected_rows, replacement_table_index_models) self.update_view_from_table_model() def on_manage_directories(self): self._view.show_directory_manager() def get_row_indices(self): """ Gets the indices of row which are not empty. :return: a list of row indices. """ row_indices_which_are_not_empty = [] number_of_rows = self._table_model.get_number_of_rows() for row in range(number_of_rows): if not self.is_empty_row(row): row_indices_which_are_not_empty.append(row) return row_indices_which_are_not_empty def on_mask_file_add(self): """ We get the added mask file name and add it to the list of masks """ new_mask_file = self._view.get_mask_file() if not new_mask_file: return new_mask_file_full_path = FileFinder.getFullPath(new_mask_file) if not new_mask_file_full_path: return # Add the new mask file to state model mask_files = self._state_model.mask_files mask_files.append(new_mask_file) self._state_model.mask_files = mask_files # Make sure that the sub-presenters are up to date with this change self._masking_table_presenter.on_update_rows() self._settings_diagnostic_tab_presenter.on_update_rows() self._beam_centre_presenter.on_update_rows() def is_empty_row(self, row): """ Checks if a row has no entries. These rows will be ignored. :param row: the row index :return: True if the row is empty. """ return self._table_model.is_empty_row(row) # def _validate_rows(self): # """ # Validation of the rows. A minimal setup requires that ScatterSample is set. # """ # # If SampleScatter is empty, then don't run the reduction. # # We allow empty rows for now, since we cannot remove them from Python. # number_of_rows = self._table_model.get_number_of_rows() # for row in range(number_of_rows): # if not self.is_empty_row(row): # sample_scatter = self._view.get_cell(row, 0) # if not sample_scatter: # raise RuntimeError("Row {} has not SampleScatter specified. Please correct this.".format(row)) # ------------------------------------------------------------------------------------------------------------------ # Controls # ------------------------------------------------------------------------------------------------------------------ def disable_controls(self): """ Disable all input fields and buttons during the execution of the reduction. """ # TODO: think about enabling and disable some controls during reduction pass def enable_controls(self): """ Enable all input fields and buttons after the execution has completed. """ # TODO: think about enabling and disable some controls during reduction pass # ------------------------------------------------------------------------------------------------------------------ # Table Model and state population # ------------------------------------------------------------------------------------------------------------------ def get_states(self, row_index=None, file_lookup=True): """ Gathers the state information for all rows. :param row_index: if a single row is selected, then only this row is returned, else all the state for all rows is returned :return: a list of states """ start_time_state_generation = time.time() # 1. Update the state model state_model_with_view_update = self._get_state_model_with_view_update() # 2. Update the table model table_model = self._table_model # 3. Go through each row and construct a state object if table_model and state_model_with_view_update: states, errors = create_states(state_model_with_view_update, table_model, self._view.instrument, self._facility, row_index=row_index, file_lookup=file_lookup) else: states = None errors = None stop_time_state_generation = time.time() time_taken = stop_time_state_generation - start_time_state_generation self.sans_logger.information( "The generation of all states took {}s".format(time_taken)) return states, errors def get_state_for_row(self, row_index, file_lookup=True): """ Creates the state for a particular row. :param row_index: the row index :return: a state if the index is valid and there is a state else None """ states, errors = self.get_states(row_index=[row_index], file_lookup=file_lookup) if states is None: self.sans_logger.warning( "There does not seem to be data for a row {}.".format( row_index)) return None if row_index in list(states.keys()): if states: return states[row_index] return None def _update_view_from_state_model(self): # Front tab view self._set_on_view("zero_error_free") self._set_on_view("save_types") self._set_on_view("compatibility_mode") self._set_on_view("merge_scale") self._set_on_view("merge_shift") self._set_on_view("merge_scale_fit") self._set_on_view("merge_shift_fit") self._set_on_view("merge_q_range_start") self._set_on_view("merge_q_range_stop") self._set_on_view("merge_max") self._set_on_view("merge_min") # Settings tab view self._set_on_view("reduction_dimensionality") self._set_on_view("reduction_mode") self._set_on_view("event_slices") self._set_on_view("event_binning") self._set_on_view("merge_mask") self._set_on_view("wavelength_step_type") self._set_on_view("wavelength_min") self._set_on_view("wavelength_max") self._set_on_view("wavelength_step") self._set_on_view("absolute_scale") self._set_on_view("sample_shape") self._set_on_view("sample_height") self._set_on_view("sample_width") self._set_on_view("sample_thickness") self._set_on_view("z_offset") # Adjustment tab self._set_on_view("normalization_incident_monitor") self._set_on_view("normalization_interpolate") self._set_on_view("transmission_incident_monitor") self._set_on_view("transmission_interpolate") self._set_on_view("transmission_roi_files") self._set_on_view("transmission_mask_files") self._set_on_view("transmission_radius") self._set_on_view("transmission_monitor") self._set_on_view("transmission_mn_shift") self._set_on_view("show_transmission") self._set_on_view_transmission_fit() self._set_on_view("pixel_adjustment_det_1") self._set_on_view("pixel_adjustment_det_2") self._set_on_view("wavelength_adjustment_det_1") self._set_on_view("wavelength_adjustment_det_2") # Q tab self._set_on_view_q_rebin_string() self._set_on_view("q_xy_max") self._set_on_view("q_xy_step") self._set_on_view("q_xy_step_type") self._set_on_view("gravity_on_off") self._set_on_view("gravity_extra_length") self._set_on_view("use_q_resolution") self._set_on_view_q_resolution_aperture() self._set_on_view("q_resolution_delta_r") self._set_on_view("q_resolution_collimation_length") self._set_on_view("q_resolution_moderator_file") self._set_on_view("r_cut") self._set_on_view("w_cut") # Mask self._set_on_view("phi_limit_min") self._set_on_view("phi_limit_max") self._set_on_view("phi_limit_use_mirror") self._set_on_view("radius_limit_min") self._set_on_view("radius_limit_max") def _set_on_view_transmission_fit_sample_settings(self): # Set transmission_sample_use_fit fit_type = self._state_model.transmission_sample_fit_type use_fit = fit_type is not FitType.NoFit self._view.transmission_sample_use_fit = use_fit # Set the polynomial order for sample polynomial_order = self._state_model.transmission_sample_polynomial_order if fit_type is FitType.Polynomial else 2 # noqa self._view.transmission_sample_polynomial_order = polynomial_order # Set the fit type for the sample fit_type = fit_type if fit_type is not FitType.NoFit else FitType.Linear self._view.transmission_sample_fit_type = fit_type # Set the wavelength wavelength_min = self._state_model.transmission_sample_wavelength_min wavelength_max = self._state_model.transmission_sample_wavelength_max if wavelength_min and wavelength_max: self._view.transmission_sample_use_wavelength = True self._view.transmission_sample_wavelength_min = wavelength_min self._view.transmission_sample_wavelength_max = wavelength_max def _set_on_view_transmission_fit(self): # Steps for adding the transmission fit to the view # 1. Check if individual settings exist. If so then set the view to separate, else set them to both # 2. Apply the settings separate_settings = self._state_model.has_transmission_fit_got_separate_settings_for_sample_and_can( ) self._view.set_fit_selection(use_separate=separate_settings) if separate_settings: self._set_on_view_transmission_fit_sample_settings() # Set transmission_sample_can_fit fit_type_can = self._state_model.transmission_can_fit_type() use_can_fit = fit_type_can is FitType.NoFit self._view.transmission_can_use_fit = use_can_fit # Set the polynomial order for can polynomial_order_can = self._state_model.transmission_can_polynomial_order if fit_type_can is FitType.Polynomial else 2 # noqa self._view.transmission_can_polynomial_order = polynomial_order_can # Set the fit type for the can fit_type_can = fit_type_can if fit_type_can is not FitType.NoFit else FitType.Linear self.transmission_can_fit_type = fit_type_can # Set the wavelength wavelength_min = self._state_model.transmission_can_wavelength_min wavelength_max = self._state_model.transmission_can_wavelength_max if wavelength_min and wavelength_max: self._view.transmission_can_use_wavelength = True self._view.transmission_can_wavelength_min = wavelength_min self._view.transmission_can_wavelength_max = wavelength_max else: self._set_on_view_transmission_fit_sample_settings() def _set_on_view_q_resolution_aperture(self): self._set_on_view("q_resolution_source_a") self._set_on_view("q_resolution_sample_a") self._set_on_view("q_resolution_source_h") self._set_on_view("q_resolution_sample_h") self._set_on_view("q_resolution_source_w") self._set_on_view("q_resolution_sample_w") # If we have h1, h2, w1, and w2 selected then we want to select the rectangular aperture. is_rectangular = self._state_model.q_resolution_source_h and self._state_model.q_resolution_sample_h and \ self._state_model.q_resolution_source_w and self._state_model.q_resolution_sample_w # noqa self._view.set_q_resolution_shape_to_rectangular(is_rectangular) def _set_on_view_q_rebin_string(self): """ Maps the q_1d_rebin_string of the model to the q_1d_step and q_1d_step_type property of the view. """ rebin_string = self._state_model.q_1d_rebin_string # Extract the min, max and step and step type from the rebin string elements = rebin_string.split(",") # If we have three elements then we want to set only the if len(elements) == 3: step_element = float(elements[1]) step = abs(step_element) step_type = RangeStepType.Lin if step_element >= 0 else RangeStepType.Log # Set on the view self._view.q_1d_min_or_rebin_string = float(elements[0]) self._view.q_1d_max = float(elements[2]) self._view.q_1d_step = step self._view.q_1d_step_type = step_type else: # Set the rebin string self._view.q_1d_min_or_rebin_string = rebin_string self._view.q_1d_step_type = self._view.VARIABLE def _set_on_view(self, attribute_name): attribute = getattr(self._state_model, attribute_name) if attribute or isinstance( attribute, bool ): # We need to be careful here. We don't want to set empty strings, or None, but we want to set boolean values. # noqa setattr(self._view, attribute_name, attribute) def _set_on_view_with_view(self, attribute_name, view): attribute = getattr(self._state_model, attribute_name) if attribute or isinstance( attribute, bool ): # We need to be careful here. We don't want to set empty strings, or None, but we want to set boolean values. # noqa setattr(view, attribute_name, attribute) def _get_state_model_with_view_update(self): """ Goes through all sub presenters and update the state model based on the views. Note that at the moment we have set up the view and the model such that the name of a property must be the same in the view and the model. This can be easily changed, but it also provides a good cohesion. """ state_model = copy.deepcopy(self._state_model) # If we don't have a state model then return None if state_model is None: return state_model # Run tab view self._set_on_state_model("zero_error_free", state_model) self._set_on_state_model("save_types", state_model) self._set_on_state_model("compatibility_mode", state_model) self._set_on_state_model("merge_scale", state_model) self._set_on_state_model("merge_shift", state_model) self._set_on_state_model("merge_scale_fit", state_model) self._set_on_state_model("merge_shift_fit", state_model) self._set_on_state_model("merge_q_range_start", state_model) self._set_on_state_model("merge_q_range_stop", state_model) self._set_on_state_model("merge_mask", state_model) self._set_on_state_model("merge_max", state_model) self._set_on_state_model("merge_min", state_model) # Settings tab self._set_on_state_model("reduction_dimensionality", state_model) self._set_on_state_model("reduction_mode", state_model) self._set_on_state_model("event_slices", state_model) self._set_on_state_model("event_binning", state_model) self._set_on_state_model("wavelength_step_type", state_model) self._set_on_state_model("wavelength_min", state_model) self._set_on_state_model("wavelength_max", state_model) self._set_on_state_model("wavelength_step", state_model) self._set_on_state_model("wavelength_range", state_model) self._set_on_state_model("absolute_scale", state_model) self._set_on_state_model("sample_shape", state_model) self._set_on_state_model("sample_height", state_model) self._set_on_state_model("sample_width", state_model) self._set_on_state_model("sample_thickness", state_model) self._set_on_state_model("z_offset", state_model) # Adjustment tab self._set_on_state_model("normalization_incident_monitor", state_model) self._set_on_state_model("normalization_interpolate", state_model) self._set_on_state_model("transmission_incident_monitor", state_model) self._set_on_state_model("transmission_interpolate", state_model) self._set_on_state_model("transmission_roi_files", state_model) self._set_on_state_model("transmission_mask_files", state_model) self._set_on_state_model("transmission_radius", state_model) self._set_on_state_model("transmission_monitor", state_model) self._set_on_state_model("transmission_mn_shift", state_model) self._set_on_state_model("show_transmission", state_model) self._set_on_state_model_transmission_fit(state_model) self._set_on_state_model("pixel_adjustment_det_1", state_model) self._set_on_state_model("pixel_adjustment_det_2", state_model) self._set_on_state_model("wavelength_adjustment_det_1", state_model) self._set_on_state_model("wavelength_adjustment_det_2", state_model) # Q tab self._set_on_state_model_q_1d_rebin_string(state_model) self._set_on_state_model("q_xy_max", state_model) self._set_on_state_model("q_xy_step", state_model) self._set_on_state_model("q_xy_step_type", state_model) self._set_on_state_model("gravity_on_off", state_model) self._set_on_state_model("gravity_extra_length", state_model) self._set_on_state_model("use_q_resolution", state_model) self._set_on_state_model("q_resolution_source_a", state_model) self._set_on_state_model("q_resolution_sample_a", state_model) self._set_on_state_model("q_resolution_source_h", state_model) self._set_on_state_model("q_resolution_sample_h", state_model) self._set_on_state_model("q_resolution_source_w", state_model) self._set_on_state_model("q_resolution_sample_w", state_model) self._set_on_state_model("q_resolution_delta_r", state_model) self._set_on_state_model("q_resolution_collimation_length", state_model) self._set_on_state_model("q_resolution_moderator_file", state_model) self._set_on_state_model("r_cut", state_model) self._set_on_state_model("w_cut", state_model) # Mask self._set_on_state_model("phi_limit_min", state_model) self._set_on_state_model("phi_limit_max", state_model) self._set_on_state_model("phi_limit_use_mirror", state_model) self._set_on_state_model("radius_limit_min", state_model) self._set_on_state_model("radius_limit_max", state_model) # Beam Centre self._beam_centre_presenter.set_on_state_model("lab_pos_1", state_model) self._beam_centre_presenter.set_on_state_model("lab_pos_2", state_model) return state_model def _set_on_state_model_transmission_fit(self, state_model): # Behaviour depends on the selection of the fit if self._view.use_same_transmission_fit_setting_for_sample_and_can(): use_fit = self._view.transmission_sample_use_fit fit_type = self._view.transmission_sample_fit_type polynomial_order = self._view.transmission_sample_polynomial_order state_model.transmission_sample_fit_type = fit_type if use_fit else FitType.NoFit state_model.transmission_can_fit_type = fit_type if use_fit else FitType.NoFit state_model.transmission_sample_polynomial_order = polynomial_order state_model.transmission_can_polynomial_order = polynomial_order # Wavelength settings if self._view.transmission_sample_use_wavelength: wavelength_min = self._view.transmission_sample_wavelength_min wavelength_max = self._view.transmission_sample_wavelength_max state_model.transmission_sample_wavelength_min = wavelength_min state_model.transmission_sample_wavelength_max = wavelength_max state_model.transmission_can_wavelength_min = wavelength_min state_model.transmission_can_wavelength_max = wavelength_max else: # Sample use_fit_sample = self._view.transmission_sample_use_fit fit_type_sample = self._view.transmission_sample_fit_type polynomial_order_sample = self._view.transmission_sample_polynomial_order state_model.transmission_sample_fit_type = fit_type_sample if use_fit_sample else FitType.NoFit state_model.transmission_sample_polynomial_order = polynomial_order_sample # Wavelength settings if self._view.transmission_sample_use_wavelength: wavelength_min = self._view.transmission_sample_wavelength_min wavelength_max = self._view.transmission_sample_wavelength_max state_model.transmission_sample_wavelength_min = wavelength_min state_model.transmission_sample_wavelength_max = wavelength_max # Can use_fit_can = self._view.transmission_can_use_fit fit_type_can = self._view.transmission_can_fit_type polynomial_order_can = self._view.transmission_can_polynomial_order state_model.transmission_can_fit_type = fit_type_can if use_fit_can else FitType.NoFit state_model.transmission_can_polynomial_order = polynomial_order_can # Wavelength settings if self._view.transmission_can_use_wavelength: wavelength_min = self._view.transmission_can_wavelength_min wavelength_max = self._view.transmission_can_wavelength_max state_model.transmission_can_wavelength_min = wavelength_min state_model.transmission_can_wavelength_max = wavelength_max def _set_on_state_model_q_1d_rebin_string(self, state_model): q_1d_step_type = self._view.q_1d_step_type # If we are dealing with a simple rebin string then the step type is None if self._view.q_1d_step_type is None: state_model.q_1d_rebin_string = self._view.q_1d_min_or_rebin_string else: q_1d_min = self._view.q_1d_min_or_rebin_string q_1d_max = self._view.q_1d_max q_1d_step = self._view.q_1d_step if q_1d_min and q_1d_max and q_1d_step and q_1d_step_type: q_1d_rebin_string = str(q_1d_min) + "," q_1d_step_type_factor = -1. if q_1d_step_type is RangeStepType.Log else 1. q_1d_rebin_string += str( q_1d_step_type_factor * q_1d_step) + "," q_1d_rebin_string += str(q_1d_max) state_model.q_1d_rebin_string = q_1d_rebin_string def _set_on_state_model(self, attribute_name, state_model): attribute = getattr(self._view, attribute_name) if attribute is not None and attribute != '': setattr(state_model, attribute_name, attribute) def get_cell_value(self, row, column): return self._view.get_cell(row=row, column=self.table_index[column], convert_to=str) # ------------------------------------------------------------------------------------------------------------------ # Settings # ------------------------------------------------------------------------------------------------------------------ def _setup_instrument_specific_settings(self, instrument=None): if not instrument: instrument = self._view.instrument self._view.set_instrument_settings(instrument) self._beam_centre_presenter.on_update_instrument(instrument) self._workspace_diagnostic_presenter.set_instrument_settings( instrument)
class DiagnosticsPagePresenterTest(unittest.TestCase): def setUp(self): self.parent_presenter = create_run_tab_presenter_mock(use_fake_state = False) self.view = create_mock_diagnostics_tab() self.presenter = DiagnosticsPagePresenter(self.parent_presenter, SANSFacility.ISIS) self.presenter.set_view(self.view, SANSInstrument.LARMOR) # Inject mocks to things tested elsewhere self.presenter._worker = mock.create_autospec(self.presenter._worker) self.presenter._model = mock.create_autospec(self.presenter._model) self.mock_state = self.presenter._model.create_state.return_value def test_that_on_horizontal_clicked_calls_worker_with_correct_parameters(self): self.presenter.on_horizontal_clicked() self.presenter._worker.run_integral.assert_called_once_with( self.view.horizontal_range, self.view.horizontal_mask, IntegralEnum.Horizontal, DetectorType.LAB, self.mock_state) def test_that_on_vertical_clicked_calls_worker_with_correct_parameters(self): self.presenter.on_vertical_clicked() self.presenter._worker.run_integral.assert_called_once_with( self.view.vertical_range, self.view.vertical_mask, IntegralEnum.Vertical, DetectorType.LAB, self.mock_state) def test_that_on_time_clicked_calls_worker_with_correct_parameters(self): self.presenter.on_time_clicked() self.presenter._worker.run_integral.assert_called_once_with( self.view.time_range, self.view.time_mask, IntegralEnum.Time, DetectorType.LAB, self.mock_state) def test_that_on_user_file_load_sets_user_file_name_on_view(self): user_file_name = 'user_file_name' self.presenter.on_user_file_load(user_file_name) self.assertEqual(self.presenter._view.user_file_name, user_file_name) def test_that_set_instrument_settings_calls_set_detectors_on_view(self): self.presenter.set_instrument_settings(SANSInstrument.SANS2D) self.presenter._view.set_detectors.assert_called_with(['rear', 'front']) self.assertEqual(self.presenter._view.set_detectors.call_count, 2)