From db21c0a2a1c467ed639a0a13f28b0041d40d204c Mon Sep 17 00:00:00 2001
From: Stephen <stephen.smith@stfc.ac.uk>
Date: Wed, 22 Jan 2020 16:22:22 +0000
Subject: [PATCH] Syncing the two function browsers

This commit addresses the issue of having two function browsers present. When the user switches between single and simultaneous fit the fit function should remain the same. In order to implement this two syncing functions were developed, which are called when the fit function structure changes.
---
 .../fitting_tab_presenter.py                  | 110 +++++++++---------
 .../fitting_tab_widget/fitting_tab_view.py    |   5 +-
 .../fitting_tab_presenter_test.py             |  70 ++++++-----
 3 files changed, 98 insertions(+), 87 deletions(-)

diff --git a/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_presenter.py b/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_presenter.py
index c7c737a7017..f14dee91235 100644
--- a/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_presenter.py
+++ b/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_presenter.py
@@ -78,31 +78,19 @@ class FittingTabPresenter(object):
 
     def update_selected_workspace_guess(self):
         if self.view.fit_type == self.view.simultaneous_fit:
-            self.update_fit_selector_list()
+            self.update_fit_specifier_list()
         if self.manual_selection_made:
             guess_selection = self.selected_data
             self.selected_data = guess_selection
-        elif self.context._frequency_context:
-            self.update_selected_frequency_workspace_guess()
         else:
-            self.update_selected_time_workspace_guess()
+            self.selected_data = self.get_workspace_selected_list()
+
+    def get_workspace_selected_list(self):
+        if self.context._frequency_context is not None:
+            freq = self.context._frequency_context.plot_type
+        else:
+            freq = 'None'
 
-    def update_selected_frequency_workspace_guess(self):
-        runs = 'All'
-        groups_and_pairs = self.context.group_pair_context.selected
-        if self.view.fit_type == self.view.simultaneous_fit:
-            if self.view.simultaneous_fit_by == "Run":
-                runs = self.view.simultaneous_fit_by_specifier
-            else:
-                groups_and_pairs = self.view.simultaneous_fit_by_specifier
-        guess_selection = self.context.get_names_of_workspaces_to_fit(
-            runs=runs,
-            group_and_pair=groups_and_pairs,
-            phasequad=True,
-            rebin=not self.view.fit_to_raw, freq=self.context._frequency_context.plot_type)
-        self.selected_data = guess_selection
-
-    def update_selected_time_workspace_guess(self):
         runs = 'All'
         groups_and_pairs = self._get_selected_groups_and_pairs()
         if self.view.fit_type == self.view.simultaneous_fit:
@@ -114,13 +102,13 @@ class FittingTabPresenter(object):
         guess_selection = []
         for grppair in groups_and_pairs:
             guess_selection += self.context.get_names_of_workspaces_to_fit(
-                    runs=runs,
-                    group_and_pair=grppair,
-                    phasequad=False,
-                    rebin=not self.view.fit_to_raw)
+                runs=runs,
+                group_and_pair=grppair,
+                phasequad=False,
+                rebin=not self.view.fit_to_raw, freq=freq)
 
         guess_selection = list(set(self._check_data_exists(guess_selection)))
-        self.selected_data = guess_selection
+        return guess_selection
 
     def handle_display_workspace_changed(self):
         current_index = self.view.get_index_for_start_end_times()
@@ -149,11 +137,12 @@ class FittingTabPresenter(object):
         if self.view.fit_type == self.view.simultaneous_fit:
             self.view.workspace_combo_box_label.setText(
                 'Display parameters for')
-            self.view.simul_fit_by_specifier.setVisible(True)
-            self.update_fit_selector_list()
+            self.view.simul_fit_by_specifier.setEnabled(True)
+            self.update_fit_specifier_list()
         else:
+            self.update_selected_workspace_guess()
             self.view.workspace_combo_box_label.setText('Select Workspace')
-            self.view.simul_fit_by_specifier.setVisible(False)
+            self.view.simul_fit_by_specifier.setEnabled(False)
 
     def handle_plot_guess_changed(self):
         if self.view.fit_type == self.view.simultaneous_fit:
@@ -171,30 +160,31 @@ class FittingTabPresenter(object):
         self.model.change_plot_guess(self.view.plot_guess, parameters)
 
     def fitting_domain_type_changed(self):
+        # If the fit function has been removed we should remove clear both browsers and the fit information
+        # and return.
+        if self._fit_function[0] is None:
+            self.clear_fit_information()
+            self.view.function_browser.clear()
+            self.view.function_browser_multi.clear()
+
         if self.view.fit_type == self.view.simultaneous_fit:
-            multi_domain_function = self.create_multi_domain_function(self._fit_function)
-            self._multi_domain_function = multi_domain_function
-            if multi_domain_function:
-                self.view.function_browser_multi.blockSignals(True)
-                self.view.function_browser_multi.setFunction(str(multi_domain_function))
-                self.view.function_browser_multi.blockSignals(False)
-                self._fit_function = [self.view.fit_object] * len(self.selected_data) if self.selected_data else [
-                    self.view.fit_object]
-            else:
-                self._fit_function = [None] * len(self.selected_data) if self.selected_data else [None]
             self.view.switch_to_simultaneous()
-            self.clear_fit_information()
         else:
-            if self.view.fit_object:
-                function_list = self.view.function_browser_multi.getGlobalFunction().createEquivalentFunctions()
-                self._fit_function = function_list
-                self.view.function_browser.blockSignals(True)
-                self.view.function_browser.setFunction(str(self._fit_function[0]))
-                self.view.function_browser.blockSignals(False)
-            else:
-                self._fit_function = [None] * len(self.selected_data) if self.selected_data else [None]
             self.view.switch_to_single()
-            self.clear_fit_information()
+
+    def sync_single_domain_browser_with_multi_domain_browser(self):
+        multi_domain_function = self.create_multi_domain_function(self._fit_function)
+        if multi_domain_function:
+            self.view.function_browser_multi.blockSignals(True)
+            self.view.function_browser_multi.setFunction(str(multi_domain_function))
+            self.view.function_browser_multi.blockSignals(False)
+
+    def sync_multi_domain_browser_with_single_domain_browser(self):
+        single_domain_function = self._fit_function[0]
+        if single_domain_function:
+            self.view.function_browser.blockSignals(True)
+            self.view.function_browser.setFunction(str(single_domain_function))
+            self.view.function_browser.blockSignals(False)
 
     def handle_fit_clicked(self):
         self.context.fitting_context.number_of_fits = 0
@@ -212,6 +202,7 @@ class FittingTabPresenter(object):
                 self._number_of_fits_cached = 1
                 simultaneous_fit_parameters = self.get_multi_domain_fit_parameters()
                 global_parameters = self.view.get_global_parameters()
+                print(simultaneous_fit_parameters)
                 calculation_function = functools.partial(
                     self.model.do_simultaneous_fit,
                     simultaneous_fit_parameters, global_parameters)
@@ -347,6 +338,12 @@ class FittingTabPresenter(object):
                 self.view.fit_object)
             self.model.function_name = self.view.function_name
 
+        # resync browsers
+        if self.view.fit_type == self.view.simultaneous_fit:
+            self.sync_multi_domain_browser_with_single_domain_browser()
+        else:
+            self.sync_single_domain_browser_with_multi_domain_browser()
+
     def handle_tf_asymmetry_mode_changed(self):
         def calculate_tf_fit_function(original_fit_function):
             tf_asymmetry_parameters = self.get_parameters_for_tf_function_calculation(original_fit_function)
@@ -426,26 +423,27 @@ class FittingTabPresenter(object):
 
     def handle_fit_by_changed(self):
         if self.view.fit_type == self.view.simultaneous_fit:
-            self.update_fit_selector_list()
             self.update_selected_workspace_guess()
             if self.view.simultaneous_fit_by == "Custom":
-                self.view.simul_fit_by_specifier.setVisible(False)
-                self.view.select_workspaces_to_fit_button.setVisible(True)
+                self.view.simul_fit_by_specifier.setEnabled(False)
+                self.view.select_workspaces_to_fit_button.setEnabled(True)
             else:
-                self.view.simul_fit_by_specifier.setVisible(True)
-                self.view.select_workspaces_to_fit_button.setVisible(False)
+                self.view.simul_fit_by_specifier.setEnabled(True)
+                self.view.select_workspaces_to_fit_button.setEnabled(False)
         else:
             return
 
     def handle_fit_specifier_changed(self):
-        self.update_selected_workspace_guess()
+        self.selected_data = self.get_workspace_selected_list()
 
-    def update_fit_selector_list(self):
+    def update_fit_specifier_list(self):
         if self.view.simultaneous_fit_by == "Run":
             flattened_run_list = [str(item) for sublist in self.context.data_context.current_runs for item in sublist]
             simul_choices = flattened_run_list
-        else:
+        elif self.view.simultaneous_fit_by == "Group/Pair":
             simul_choices = self._get_selected_groups_and_pairs()
+        else:
+            simul_choices = self.selected_data
 
         self.view.setup_fit_by_specifier(simul_choices)
 
diff --git a/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_view.py b/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_view.py
index 8558076bb08..458beb77d34 100644
--- a/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_view.py
+++ b/scripts/Muon/GUI/Common/fitting_tab_widget/fitting_tab_view.py
@@ -38,7 +38,7 @@ class FittingTabView(QtWidgets.QWidget, ui_fitting_tab):
         self.increment_parameter_display_button.clicked.connect(self.increment_display_combo_box)
         self.decrement_parameter_display_button.clicked.connect(self.decrement_display_combo_box)
 
-        self.select_workspaces_to_fit_button.setVisible(False)
+        self.select_workspaces_to_fit_button.setEnabled(False)
 
     def update_displayed_data_combo_box(self, data_list):
         self.parameter_display_combo.blockSignals(True)
@@ -77,7 +77,7 @@ class FittingTabView(QtWidgets.QWidget, ui_fitting_tab):
         self.simul_fit_by_combo.addItem("Run")
         self.simul_fit_by_combo.addItem("Group/Pair")
         self.simul_fit_by_combo.addItem("Custom")
-        self.simul_fit_by_specifier.setVisible(False)
+        self.simul_fit_by_specifier.setEnabled(False)
 
     def set_datasets_in_function_browser(self, data_set_name_list):
         number_of_data_sets = self.function_browser.getNumberOfDatasets()
@@ -278,6 +278,7 @@ class FittingTabView(QtWidgets.QWidget, ui_fitting_tab):
         self.simul_fit_by_specifier.clear()
         self.simul_fit_by_specifier.addItems(choices)
         self.simul_fit_by_specifier.blockSignals(False)
+        self.simul_fit_by_specifier.currentIndexChanged.emit(0)
 
     def setup_fit_options_table(self):
         self.fit_options_table.setRowCount(6)
diff --git a/scripts/test/Muon/fitting_tab_widget/fitting_tab_presenter_test.py b/scripts/test/Muon/fitting_tab_widget/fitting_tab_presenter_test.py
index e09b830519a..6a58c69367f 100644
--- a/scripts/test/Muon/fitting_tab_widget/fitting_tab_presenter_test.py
+++ b/scripts/test/Muon/fitting_tab_widget/fitting_tab_presenter_test.py
@@ -18,7 +18,7 @@ EXAMPLE_TF_ASYMMETRY_FUNCTION = '(composite=ProductFunction,NumDeriv=false;name=
                                 '(name=FlatBackground,A0=1,ties=(A0=1);name=ExpDecayOsc,A=0.2,Lambda=0.2,Frequency=0.1,Phi=0))' \
                                 ';name=ExpDecayMuon,A=0,Lambda=-2.19698,ties=(A=0,Lambda=-2.19698)'
 
-SIMUL_FIT_BY_COMBO_MAP = {"Run": 0, "Group/Pair": 1}
+SIMUL_FIT_BY_COMBO_MAP = {"Run": 0, "Group/Pair": 1, "Custom": 2}
 
 
 def retrieve_combobox_info(combo_box):
@@ -116,9 +116,11 @@ class FittingTabPresenterTest(unittest.TestCase):
 
     def test_fit_clicked_with_simultaneous_selected_and_no_globals(self):
         self.presenter.model.get_function_name.return_value = 'GausOsc'
-        self.presenter.selected_data = ['Input Workspace Name_1', 'Input Workspace Name 2']
+        self.presenter.get_workspace_selected_list = mock.MagicMock(
+            return_value=['Input Workspace Name_1', 'Input Workspace Name 2'])
         self.view.function_browser.setFunction('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
         self.view.simul_fit_checkbox.setChecked(True)
+        print("FIT FUNCTION", self.view.function_browser_multi.getGlobalFunction())
         self.presenter.model.do_simultaneous_fit.return_value = (self.view.function_browser_multi.getGlobalFunction(),
                                                                  'Fit Suceeded', 0.5)
 
@@ -138,8 +140,8 @@ class FittingTabPresenterTest(unittest.TestCase):
 
     def test_fit_clicked_with_simultaneous_selected_with_global_parameters(self):
         self.presenter.model.get_function_name.return_value = 'GausOsc'
-
-        self.presenter.selected_data = ['Input Workspace Name_1', 'Input Workspace Name 2']
+        self.presenter.get_workspace_selected_list = mock.MagicMock(
+            return_value=['Input Workspace Name_1', 'Input Workspace Name 2'])
         self.view.function_browser.setFunction('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
         self.view.simul_fit_checkbox.setChecked(True)
         self.view.function_browser_multi.setGlobalParameters(['A'])
@@ -493,6 +495,9 @@ class FittingTabPresenterTest(unittest.TestCase):
         self.presenter.selected_data = ['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
                                         'MUSR22725; Group; fwd; Asymmetry']
         self.view.function_browser.setFunction('name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0')
+        self.presenter.get_workspace_selected_list = mock.MagicMock(
+            return_value=['MUSR22725; Group; top; Asymmetry', 'MUSR22725; Group; bottom; Asymmetry',
+                          'MUSR22725; Group; fwd; Asymmetry'])
 
         self.view.simul_fit_checkbox.setChecked(True)
 
@@ -510,7 +515,7 @@ class FittingTabPresenterTest(unittest.TestCase):
         self.view.simul_fit_checkbox.setChecked(False)
 
         self.assertEqual([str(item) for item in self.presenter._fit_function], [
-            'name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0'] * 3)
+            'name=GausOsc,A=0.2,Sigma=0.2,Frequency=0.1,Phi=0'])
 
     def test_undo_fit_button_disabled_until_a_succesful_fit_is_performed(self):
         self.assertEqual(self.view.undo_fit_button.isEnabled(), False)
@@ -570,16 +575,10 @@ class FittingTabPresenterTest(unittest.TestCase):
 
     def test_update_selected_ws_guess(self):
         self.presenter.manual_selection_made = False
-        self.presenter.update_selected_time_workspace_guess = mock.Mock()
-        self.presenter.update_selected_workspace_guess()
-        self.assertEquals(self.presenter.update_selected_time_workspace_guess.call_count, 1)
-
-    def test_update_selected_ws_guess_freq(self):
-        self.presenter.manual_selection_made = False
-        self.presenter.context._frequency_context = True
-        self.presenter.update_selected_frequency_workspace_guess = mock.Mock()
+        self.presenter.get_workspace_selected_list = mock.Mock()
+        self.presenter.clear_and_reset_gui_state = mock.Mock()
         self.presenter.update_selected_workspace_guess()
-        self.assertEquals(self.presenter.update_selected_frequency_workspace_guess.call_count, 1)
+        self.assertEquals(self.presenter.get_workspace_selected_list.call_count, 1)
 
     def test_update_selected_ws_guess_non(self):
         self.presenter.manual_selection_made = True
@@ -600,14 +599,15 @@ class FittingTabPresenterTest(unittest.TestCase):
         self.presenter._get_selected_groups_and_pairs = mock.Mock(return_value=["fwd", "bwd"])
         self.presenter.clear_and_reset_gui_state = mock.Mock()
         self.presenter._check_data_exists = mock.Mock(return_value=["test"])
-        output = self.presenter.update_selected_time_workspace_guess()
+
+        output = self.presenter.get_workspace_selected_list()
 
         self.context.get_names_of_workspaces_to_fit.assert_any_call(runs="All", group_and_pair="bwd", phasequad=False,
-                                                                    rebin=False)
+                                                                    rebin=False, freq='None')
         self.context.get_names_of_workspaces_to_fit.assert_any_call(runs="All", group_and_pair="fwd", phasequad=False,
-                                                                    rebin=False)
+                                                                    rebin=False, freq='None')
         self.assertEquals(self.presenter.context.get_names_of_workspaces_to_fit.call_count, 2)
-        self.assertEquals(self.presenter.selected_data, ["test"])
+        self.assertEquals(output, ["test"])
 
     def test_handle_plot_guess_changed_calls_correct_function(self):
         self.presenter.get_parameters_for_single_fit = mock.Mock(return_value={})
@@ -622,13 +622,13 @@ class FittingTabPresenterTest(unittest.TestCase):
         self.view.setup_fit_options_table()
         self.assertEqual(-1, self.view.minimizer_combo.findText('FABADA'))
 
-    def test_simul_fit_by_selector_updates_correctly_when_fit_change_to_simultanenous(self):
+    def test_simul_fit_by_specifier_updates_correctly_when_fit_change_to_simultanenous(self):
         # test simul fit by Run
         self.view.simul_fit_by_combo.setCurrentIndex(SIMUL_FIT_BY_COMBO_MAP["Run"])
 
         self.view.simul_fit_checkbox.setChecked(True)
 
-        self.assertEqual(str(self.view.simul_fit_by_selector.itemText(0)), str(self.loaded_run))
+        self.assertEqual(str(self.view.simul_fit_by_specifier.itemText(0)), str(self.loaded_run))
 
         # simul fit by Group/Pair
         self.view.simul_fit_by_combo.setCurrentIndex(SIMUL_FIT_BY_COMBO_MAP["Group/Pair"])
@@ -636,18 +636,18 @@ class FittingTabPresenterTest(unittest.TestCase):
 
         self.presenter.handle_fit_type_changed()
 
-        self.assertEqual(str(self.view.simul_fit_by_selector.itemText(0)), "fwd")
-        self.assertEqual(str(self.view.simul_fit_by_selector.itemText(1)), "bwd")
+        self.assertEqual(str(self.view.simul_fit_by_specifier.itemText(0)), "fwd")
+        self.assertEqual(str(self.view.simul_fit_by_specifier.itemText(1)), "bwd")
 
-    def test_simul_fit_by_selector_updates_when_simul_fit_type_changes(self):
+    def test_simul_fit_by_specifier_updates_when_simul_fit_type_changes(self):
         self.view.simul_fit_checkbox.setChecked(True)
-        self.presenter.update_fit_selector_list = mock.MagicMock()
+        self.presenter.update_fit_specifier_list = mock.MagicMock()
 
         self.view.simul_fit_by_combo.setCurrentIndex(SIMUL_FIT_BY_COMBO_MAP["Group/Pair"])
 
-        self.presenter.update_fit_selector_list.assert_called_once()
+        self.presenter.update_fit_specifier_list.assert_called_once()
 
-    def test_simul_fit_by_selector_does_nothing_when_simul_fit_type_changes_but_not_doing_simul_fit(self):
+    def test_simul_fit_by_specifier_does_nothing_when_simul_fit_type_changes_but_not_doing_simul_fit(self):
         self.view.simul_fit_checkbox.setChecked(False)
         self.presenter.update_fit_selector_list = mock.MagicMock()
 
@@ -655,7 +655,7 @@ class FittingTabPresenterTest(unittest.TestCase):
 
         self.presenter.update_fit_selector_list.assert_not_called()
 
-    def test_simul_fit_by_selector_updates_list_correctly_when_simul_fit_type_changes(self):
+    def test_simul_fit_by_selector_updates_fit_specifier_correctly_when_simul_fit_type_changes(self):
         self.view.simul_fit_checkbox.setChecked(True)
         self.view.simul_fit_by_combo.setCurrentIndex(SIMUL_FIT_BY_COMBO_MAP["Run"])
         self.presenter._get_selected_groups_and_pairs = mock.Mock(return_value=["fwd", "bwd"])
@@ -663,8 +663,20 @@ class FittingTabPresenterTest(unittest.TestCase):
         # now switch to group/pair
         self.view.simul_fit_by_combo.setCurrentIndex(SIMUL_FIT_BY_COMBO_MAP["Group/Pair"])
 
-        self.assertEqual(str(self.view.simul_fit_by_selector.itemText(0)), "fwd")
-        self.assertEqual(str(self.view.simul_fit_by_selector.itemText(1)), "bwd")
+        self.assertEqual(str(self.view.simul_fit_by_specifier.itemText(0)), "fwd")
+        self.assertEqual(str(self.view.simul_fit_by_specifier.itemText(1)), "bwd")
+
+    def test_simul_fit_by_specifier_correctly_updates_display_combobox(self):
+        self.view.simul_fit_checkbox.setChecked(True)
+        self.presenter.get_workspace_selected_list = mock.MagicMock(return_value=['WS1', 'WS2'])
+
+        self.view.simul_fit_by_combo.setCurrentIndex(SIMUL_FIT_BY_COMBO_MAP["Group/Pair"])
+
+        self.assertEqual(str(self.view.parameter_display_combo.itemText(0)), "WS1")
+        self.assertEqual(str(self.view.parameter_display_combo.itemText(1)), "WS2")
+
+    def test_simul_fit_by_specifier_corretcly_selects_workspaces_for_fit(self):
+        pass
 
 
 if __name__ == '__main__':
-- 
GitLab