Unverified Commit 7ca16e8f authored by Gigg, Martyn Anthony's avatar Gigg, Martyn Anthony Committed by GitHub
Browse files

Merge pull request #25717 from mantidproject/25585_create_framework_for_Muon_analysis_2

Add fitting tab outline to Muon Analysis 2
parents 25997f63 85d6861c
......@@ -26,4 +26,5 @@ Improvements
Bug Fixes
#########
* Muon Analysis no longer crashes when `TF Asymmetry` mode is activated.
* Muon Analysis no longer crashes when `TF Asymmetry` mode is activated.
* Frequency Domain Analysis GUI added to workbench.
......@@ -14,7 +14,7 @@ from Muon.GUI.Common.contexts.muon_data_context import MuonDataContext
from Muon.GUI.Common.contexts.muon_group_pair_context import MuonGroupPairContext
from Muon.GUI.Common.contexts.muon_gui_context import MuonGuiContext
from Muon.GUI.Common.contexts.phase_table_context import PhaseTableContext
from Muon.GUI.Common.utilities.run_string_utils import run_list_to_string
from Muon.GUI.Common.utilities.run_string_utils import run_list_to_string, run_string_to_list
import Muon.GUI.Common.ADSHandler.workspace_naming as wsName
from Muon.GUI.Common.contexts.muon_data_context import get_default_grouping
......@@ -28,7 +28,7 @@ class MuonContext(object):
self._phase_context = muon_phase_context
self.base_directory = base_directory
self.gui_context.update({'DeadTimeSource': 'None', 'LastGoodDataFromFile': True})
self.gui_context.update({'DeadTimeSource': 'None', 'LastGoodDataFromFile': True, 'selected_group_pair': ''})
@property
def data_context(self):
......@@ -214,3 +214,46 @@ class MuonContext(object):
return self.data_context.get_loaded_data_for_run(run)["DataDeadTimeTable"]
elif self.gui_context['DeadTimeSource'] == 'None':
return None
def get_names_of_workspaces_to_fit(self, runs='', group_and_pair='', phasequad=False, rebin=False):
if group_and_pair == 'All':
group = self.group_pair_context.group_names
pair = self.group_pair_context.pair_names
else:
group_pair_list = group_and_pair.replace(' ', '').split(',')
group = [group for group in group_pair_list if group in self.group_pair_context.group_names]
pair = [pair for pair in group_pair_list if pair in self.group_pair_context.pair_names]
if runs == 'All':
run_list = self.data_context.current_runs
else:
run_list = [run_string_to_list(item) for item in runs.replace(' ', '').split(',')]
flat_list = []
for sublist in run_list:
flat_list += [[run] for run in sublist if len(sublist) > 1]
run_list += flat_list
run_list = [run for run in run_list if run in self.data_context.current_runs]
group_names = self.group_pair_context.get_group_workspace_names(run_list, group, rebin)
pair_names = self.group_pair_context.get_pair_workspace_names(run_list, pair, rebin)
phasequad_names = []
if phasequad:
for run in run_list:
run_string = run_list_to_string(run)
phasequad_names += self.phase_context.get_phase_quad(self.data_context.instrument, run_string)
return group_names + pair_names + phasequad_names
def get_list_of_binned_or_unbinned_workspaces_from_equivalents(self, input_list):
equivalent_list = []
for item in input_list:
if 'PhaseQuad' in item:
equivalent_list.append(item)
equivalent_group_pair = self.group_pair_context.get_equivalent_group_pair(item)
if equivalent_group_pair:
equivalent_list.append(equivalent_group_pair)
return equivalent_list
# Mantid Repository : https://github.com/mantidproject/mantid
#
# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
# NScD Oak Ridge National Laboratory, European Spallation Source
# & Institut Laue - Langevin
# SPDX - License - Identifier: GPL - 3.0 +
from __future__ import (absolute_import, division, print_function)
import Muon.GUI.Common.utilities.xml_utils as xml_utils
......@@ -146,3 +152,39 @@ class MuonGroupPairContext(object):
if item.name == name:
return False
return True
def get_group_workspace_names(self, runs, groups, rebin):
workspace_list = []
for group_name in groups:
group = self[group_name]
if rebin:
sub_list = group.get_asymmetry_workspace_names_rebinned(runs)
else:
sub_list = group.get_asymmetry_workspace_names(runs)
workspace_list += sub_list
return workspace_list
def get_pair_workspace_names(self, runs, pairs, rebin):
workspace_list = []
for pair_name in pairs:
pair = self[pair_name]
if rebin:
sub_list = pair.get_asymmetry_workspace_names_rebinned(runs)
else:
sub_list = pair.get_asymmetry_workspace_names(runs)
workspace_list += sub_list
return workspace_list
def get_equivalent_group_pair(self, workspace_name):
for item in self._groups + self._pairs:
equivalent_name = item.get_rebined_or_unbinned_version_of_workspace_if_it_exists(workspace_name)
if equivalent_name:
return equivalent_name
return None
......@@ -20,6 +20,7 @@ class MuonGuiContext(dict):
def __init__(self, *args, **kwargs):
super(MuonGuiContext, self).__init__(*args, **kwargs)
self.gui_variables_notifier = GuiVariablesNotifier(self)
self.gui_variable_non_calulation_notifier = GuiVariablesNotifier(self)
def update_and_send_signal(self, *args, **kwargs):
updated_items = {k: kwargs[k] for k in kwargs if k in self and kwargs[k] != self[k] or k not in self}
......@@ -29,9 +30,20 @@ class MuonGuiContext(dict):
super(MuonGuiContext, self).update(*args, **kwargs)
self.gui_variables_notifier.notify_subscribers()
def update_and_send_non_calculation_signal(self, *args, **kwargs):
updated_items = {k: kwargs[k] for k in kwargs if k in self and kwargs[k] != self[k] or k not in self}
if not updated_items and kwargs:
return
super(MuonGuiContext, self).update(*args, **kwargs)
self.gui_variable_non_calulation_notifier.notify_subscribers()
def add_subscriber(self, observer):
self.gui_variables_notifier.add_subscriber(observer)
def add_non_calc_subscriber(self, observer):
self.gui_variable_non_calulation_notifier.add_subscriber(observer)
def period_string(self, run=None):
summed_periods = self["SummedPeriods"] if 'SummedPeriods' in self else [1]
subtracted_periods = self["SubtractedPeriods"] if 'SubtractedPeriods' in self else []
......
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>fitting_tab</class>
<widget class="QWidget" name="fitting_tab">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>526</width>
<height>454</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Maximum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Maximum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item row="6" column="0">
<widget class="QTableWidget" name="fit_options_table">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="workspace_combo_box_label">
<property name="text">
<string>Select Workspace</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="decrement_parameter_display_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>27</width>
<height>27</height>
</size>
</property>
<property name="text">
<string>&lt;&lt;</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="parameter_display_combo">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="increment_parameter_display_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>27</width>
<height>27</height>
</size>
</property>
<property name="text">
<string>&gt;&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Maximum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="0">
<layout class="QGridLayout" name="function_browser_layout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
</layout>
</item>
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QPushButton" name="fit_button">
<property name="text">
<string>Fit</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QComboBox" name="fit_type_combo">
<item>
<property name="text">
<string>Single Fit</string>
</property>
</item>
<item>
<property name="text">
<string>Sequential Fit</string>
</property>
</item>
<item>
<property name="text">
<string>Simultaneous Fit</string>
</property>
</item>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="select_workspaces_to_fit_button">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Select data to fit</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="undo_fit_button">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Undo Fit</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
<zorder>fit_options_table</zorder>
<zorder>verticalSpacer</zorder>
<zorder>verticalSpacer_2</zorder>
<zorder>verticalSpacer_3</zorder>
<zorder></zorder>
</widget>
<resources/>
<connections/>
</ui>
from Muon.GUI.Common.fitting_tab_widget.workspace_selector_view import WorkspaceSelectorView
from Muon.GUI.Common.observer_pattern import GenericObserver
class FittingTabPresenter(object):
def __init__(self, view, context):
self.view = view
self.context = context
self._selected_data = []
self.manual_selection_made = False
self.update_selected_workspace_guess()
self.gui_context_observer = GenericObserver(self.update_selected_workspace_guess)
self.run_changed_observer = GenericObserver(self.update_selected_workspace_guess)
def handle_select_fit_data_clicked(self):
selected_data, dialog_return = WorkspaceSelectorView.get_selected_data(self.context.data_context.current_runs,
self.context.data_context.instrument,
self.selected_data,
self.view.fit_to_raw,
self.context,
self.view)
if dialog_return:
self.selected_data = selected_data
self.manual_selection_made = True
def update_selected_workspace_guess(self):
if not self.manual_selection_made:
guess_selection = self.context.get_names_of_workspaces_to_fit(runs='All',
group_and_pair=self.context.gui_context[
'selected_group_pair'], phasequad=False,
rebin=not self.view.fit_to_raw)
self.selected_data = guess_selection
def handle_display_workspace_changed(self):
pass
def handle_use_rebin_changed(self):
if not self.view.fit_to_raw and not self.context._do_rebin():
self.view.fit_to_raw = True
self.view.warning_popup('No rebin options specified')
return
if not self.manual_selection_made:
self.update_selected_workspace_guess()
else:
self.selected_data = self.context.get_list_of_binned_or_unbinned_workspaces_from_equivalents(self.selected_data)
@property
def selected_data(self):
return self._selected_data
@selected_data.setter
def selected_data(self, value):
self._selected_data = value
self.view.update_displayed_data_combo_box(value)
# Mantid Repository : https://github.com/mantidproject/mantid
#
# Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
# NScD Oak Ridge National Laboratory, European Spallation Source
# & Institut Laue - Langevin
# SPDX - License - Identifier: GPL - 3.0 +
from __future__ import (absolute_import, division, print_function)
from qtpy import QtWidgets, QtCore
from Muon.GUI.Common.utilities import table_utils
from Muon.GUI.Common.message_box import warning
from mantidqt.utils.qt import load_ui
from mantidqt.widgets.functionbrowser import FunctionBrowser
ui_fitting_tab, _ = load_ui(__file__, "fitting_tab.ui")
class FittingTabView(QtWidgets.QWidget, ui_fitting_tab):
def __init__(self, parent=None):
super(FittingTabView, self).__init__(parent)
self.setupUi(self)
self.setup_fit_options_table()
self.function_browser = FunctionBrowser(self, True)
self.function_browser_layout.addWidget(self.function_browser)
self.increment_parameter_display_button.clicked.connect(self.increment_display_combo_box)
self.decrement_parameter_display_button.clicked.connect(self.decrement_display_combo_box)
def update_displayed_data_combo_box(self, data_list):
name = self.parameter_display_combo.currentText()
self.parameter_display_combo.clear()
self.parameter_display_combo.addItems(data_list)
index = self.parameter_display_combo.findText(name)
if index != -1:
self.parameter_display_combo.setCurrentIndex(index)
else:
self.parameter_display_combo.setCurrentIndex(0)
def increment_display_combo_box(self):
index = self.parameter_display_combo.currentIndex()
count = self.parameter_display_combo.count()
if index < count - 1:
self.parameter_display_combo.setCurrentIndex(index + 1)
else:
self.parameter_display_combo.setCurrentIndex(0)
def decrement_display_combo_box(self):
index = self.parameter_display_combo.currentIndex()
count = self.parameter_display_combo.count()
if index != 0:
self.parameter_display_combo.setCurrentIndex(index - 1)
else:
self.parameter_display_combo.setCurrentIndex(count - 1)
def set_slot_for_select_workspaces_to_fit(self, slot):
self.select_workspaces_to_fit_button.clicked.connect(slot)
def set_slot_for_display_workspace_changed(self, slot):
self.parameter_display_combo.currentIndexChanged.connect(slot)
def set_slot_for_use_raw_changed(self, slot):
self.fit_to_raw_data_checkbox.stateChanged.connect(slot)
@property
def display_workspace(self):
return str(self.parameter_display_combo.currentText())
@property
def fit_to_raw(self):
return self.fit_to_raw_data_checkbox.isChecked()
@fit_to_raw.setter
def fit_to_raw(self, value):
state = QtCore.Qt.Checked if value else QtCore.Qt.Unchecked
self.fit_to_raw_data_checkbox.setCheckState(state)
def warning_popup(self, message):
warning(message, parent=self)
def setup_fit_options_table(self):
self.fit_options_table.setRowCount(5)
self.fit_options_table.setColumnCount(2)
self.fit_options_table.setColumnWidth(0, 300)
self.fit_options_table.setColumnWidth(1, 300)
self.fit_options_table.verticalHeader().setVisible(False)
self.fit_options_table.horizontalHeader().setStretchLastSection(True)
self.fit_options_table.setHorizontalHeaderLabels(
("Property;Value").split(";"))
table_utils.setRowName(self.fit_options_table, 0, "Time Start")
self.minimizer_combo = table_utils.addDoubleToTable(self.fit_options_table, 0.0, 0, 1)
table_utils.setRowName(self.fit_options_table, 1, "Time End")
self.minimizer_combo = table_utils.addDoubleToTable(self.fit_options_table, 15.0, 1, 1)
table_utils.setRowName(self.fit_options_table, 2, "Minimizer")
self.minimizer_combo = table_utils.addComboToTable(self.fit_options_table, 2, [])
# table_utils.setRowName(self.fit_options_table, 3, "TF Asymmetry Mode")
# self.tf_asymmetry_mode_checkbox = table_utils.addCheckBoxWidgetToTable(
# self.fit_options_table, False, 3)
#
# table_utils.setRowName(self.fit_options_table, 4, "Plot Difference")
# self.plot_differences_checkbox = table_utils.addCheckBoxWidgetToTable(
# self.fit_options_table, True, 4)
table_utils.setRowName(self.fit_options_table, 3, "Fit To Raw Data")
self.fit_to_raw_data_checkbox = table_utils.addCheckBoxWidgetToTable(
self.fit_options_table, True, 3)
# table_utils.setRowName(self.fit_options_table, 6, "Show Parameter Errors")
# self.show_parameter_errors_checkbox = table_utils.addCheckBoxWidgetToTable(
# self.fit_options_table, True, 6)
table_utils.setRowName(self.fit_options_table, 4, "Evaluate Function As")
self.minimizer_combo = table_utils.addComboToTable(self.fit_options_table, 4, ['CentrePoint', 'Histogram'])
# Mantid Repository : https://github.com/mantidproject/mantid
#
# Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
# NScD Oak Ridge National Laboratory, European Spallation Source
# & Institut Laue - Langevin
# SPDX - License - Identifier: GPL - 3.0 +
from Muon.GUI.Common.fitting_tab_widget.fitting_tab_view import FittingTabView
from Muon.GUI.Common.fitting_tab_widget.fitting_tab_presenter import FittingTabPresenter
class FittingTabWidget(object):
def __init__(self, context, parent):
self.fitting_tab_view = FittingTabView(parent)
self.fitting_tab_presenter = FittingTabPresenter(self.fitting_tab_view, context)
self.fitting_tab_view.set_slot_for_select_workspaces_to_fit(self.fitting_tab_presenter.handle_select_fit_data_clicked)
self.fitting_tab_view.set_slot_for_display_workspace_changed(self.fitting_tab_presenter.handle_display_workspace_changed)
self.fitting_tab_view.set_slot_for_use_raw_changed(self.fitting_tab_presenter.handle_use_rebin_changed)
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>dialog</class>
<widget class="QDialog" name="dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>691</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Data Selection</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="">
<layout class="QGridLayout" name="list_selector_layout"/>
</widget>
<widget class="QWidget" name="">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLineEdit" name="run_line_edit">