Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from sas.qtgui.Perspectives.Fitting.FitPage import FitPage
from sas.qtgui.Perspectives.Fitting.FitThread import FitThread
from sas.qtgui.Perspectives.Fitting.FittingLogic import FittingLogic
from sas.qtgui.Perspectives.Fitting.InViewWidget import InViewWidget
from sas.qtgui.Perspectives.Fitting.MagnetismWidget import MagnetismWidget
from sas.qtgui.Perspectives.Fitting.ModelThread import Calc1D, Calc2D
from sas.qtgui.Perspectives.Fitting.MultiConstraint import MultiConstraint
Expand Down Expand Up @@ -108,6 +109,8 @@ def __init__(self, parent: QtWidgets.QWidget | None = None, data: Any | None = N
# Logics.data contains a single Data1D/Data2D object
self._logic = [FittingLogic()]

self._in_view_widget = None

# Main GUI setup up
self.setupUi(self)
self.setWindowTitle("Fitting")
Expand Down Expand Up @@ -457,6 +460,7 @@ def setEnablementOnDataLoad(self) -> None:
self.chkChainFit.setVisible(True)
# This panel is not designed to view individual fits, so disable plotting
self.cmdPlot.setVisible(False)
self.cmdInView.setEnabled(False)
# Similarly on other tabs
self.options_widget.setEnablementOnDataLoad()
self.onSelectModel()
Expand Down Expand Up @@ -536,6 +540,16 @@ def toggle2D(self, isChecked: bool) -> None:
""" Enable/disable the controls dependent on 1D/2D data instance """
self.chkMagnetism.setEnabled(isChecked)
self.is2D = isChecked

if isChecked:
# 2D view is toggled on, therefore disable InView button,
# and close InView widget if opened
self.cmdInView.setEnabled(False)
if getattr(self, '_in_view_widget', None):
self._in_view_widget.close()
else:
# 2D view is toggled off, therefore enable InView button
self.cmdInView.setEnabled(True)
# Reload the current model
if self.logic.kernel_module:
self.onSelectModel()
Expand Down Expand Up @@ -591,6 +605,7 @@ def initializeSignals(self) -> None:
self.cmdFit.clicked.connect(self.onFit)
self.cmdPlot.clicked.connect(self.onPlot)
self.cmdHelp.clicked.connect(self.onHelp)
self.cmdInView.clicked.connect(self.onInView)

# Respond to change in parameters from the UI
self._model_model.dataChanged.connect(self.onMainParamsChange)
Expand Down Expand Up @@ -1658,6 +1673,72 @@ def onSelectCategory(self) -> None:
self.cbModel.addItems(sorted(models_to_show))
self.cbModel.blockSignals(False)

def onInView(self):
"""
Trigers the display of 'InView'
"""
if self._in_view_widget is None:
self._in_view_widget = InViewWidget(parent=self)
self._in_view_widget.destroyed.connect(lambda: setattr(self, '_in_view_widget', None))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The connection on line 1694 already does this, but in a cleaner way.


# When InView is closed, re-enable editing and clear reference
def _on_inview_closed():
self._in_view_widget = None
# Enabling paramters edits
self.lstParams.setEnabled(True)
self.cbCategory.setEnabled(True)
self.cmdFit.setEnabled(True)
self.cmdPlot.setEnabled(True)
Comment on lines +1685 to +1691
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create the following class-level method and you can replace the last four lines of this function here and in at least 2 other places.

        def toggle_inview_window():
            toggle = self._in_view_widget == None
            # Enabling paramters edits
            self.lstParams.setEnabled(toggle)
            self.cbCategory.setEnabled(toggle)
            self.cmdFit.setEnabled(toggle)
            self.cmdPlot.setEnabled(toggle)



self._in_view_widget.destroyed.connect(_on_inview_closed)

self.lstParams.setEnabled(False)
self.cbCategory.setEnabled(False)
self.cmdFit.setEnabled(False)
self.cmdPlot.setEnabled(False)

###
# Validate parameter bounds for InView (no +/-inf allowed)
params = list(self.main_params_to_fit)
if self.chkPolydispersity.isChecked():
params += list(self.polydispersity_widget.poly_params_to_fit)
has_infinite_bounds = False
for p in params:
try:
details = self.logic.kernel_module.details.get(p)
if details is None or len(details) < 3:
continue
pmin, pmax = details[1], details[2]
# Disallow non-finite bounds
if not np.isfinite(pmin) or not np.isfinite(pmax):
has_infinite_bounds = True
break
except Exception:
# If anything odd, be conservative and flag
has_infinite_bounds = True
break
if has_infinite_bounds:
QtWidgets.QMessageBox.warning(self, "InView",
"Some parameters have infinite bounds. Please set appropriate min/max ranges.")
# Re-enable controls since we are not opening InView
self.lstParams.setEnabled(True)
self.cbCategory.setEnabled(True)
self.cmdFit.setEnabled(True)
self.cmdPlot.setEnabled(True)
return
Comment on lines +1714 to +1729
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplification:

Suggested change
if not np.isfinite(pmin) or not np.isfinite(pmax):
has_infinite_bounds = True
break
except Exception:
# If anything odd, be conservative and flag
has_infinite_bounds = True
break
if has_infinite_bounds:
QtWidgets.QMessageBox.warning(self, "InView",
"Some parameters have infinite bounds. Please set appropriate min/max ranges.")
# Re-enable controls since we are not opening InView
self.lstParams.setEnabled(True)
self.cbCategory.setEnabled(True)
self.cmdFit.setEnabled(True)
self.cmdPlot.setEnabled(True)
return
if not np.isfinite(pmin) or not np.isfinite(pmax):
# Fall through to exception handling, which mimics behavior required here.
raise Exception()
except Exception:
# If anything odd, be conservative and flag
QtWidgets.QMessageBox.warning(self, "InView",
"Some parameters have infinite bounds. Please set appropriate min/max ranges.")
# Re-enable controls since we are not opening InView
self._on_inview_closed()
return

###

# Pushing the 1D data set to InView
self._in_view_widget.setData(self.logic.data)

# Construct sliders from the Fit Page
self._in_view_widget.initFromFitPage(self)
Comment on lines +1733 to +1736
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are creating a new window in this method and the fitpage is already passed to the InViewWidget. Instead, pass the data to __init__() and call both setData() and initFromFitPage() as part of the instantiation, which allows you to remove the arguments from both methods.


self._in_view_widget.show()
self._in_view_widget.raise_()
self._in_view_widget.activateWindow()

def onHelp(self):
"""
Show the "Fitting" section of help
Expand Down Expand Up @@ -3362,6 +3443,10 @@ def enableInteractiveElements(self) -> None:
self.fit_started = False
self.setInteractiveElements(True)

# Re-enable InView button for 1D data
if not getattr(self, 'is2D', False):
self.cmdInView.setEnabled(True)

def disableInteractiveElements(self) -> None:
"""
Set button caption on fitting/calculate start
Expand All @@ -3373,6 +3458,15 @@ def disableInteractiveElements(self) -> None:
self.cmdFit.setText('Stop fit')
self.setInteractiveElements(False)

# Disable InView button and close the widget if opened
self.cmdInView.setEnabled(False)
isInView = getattr(self, '_in_inview_widget', None)
if isInView is not None:
try:
isInView.close()
except Exception:
pass

def disableInteractiveElementsOnCalculate(self) -> None:
"""
Set button caption on fitting/calculate start
Expand All @@ -3384,6 +3478,15 @@ def disableInteractiveElementsOnCalculate(self) -> None:
self.cmdFit.setText('Running...')
self.setInteractiveElements(False)

# Disable InView button and close the widget if opened
self.cmdInView.setEnabled(False)
isInView = getattr(self, '_in_inview_widget', None)
if isInView is not None:
try:
isInView.close()
except Exception:
pass

def readFitPage(self, fp: FitPage) -> None:
"""
Read in state from a fitpage object and update GUI
Expand Down
Loading