source: sasview/src/sas/qtgui/Perspectives/Fitting/MultiConstraint.py @ 09e0c32

ESS_GUIESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since 09e0c32 was 09e0c32, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 5 years ago

Added tooltip on COnstraints table.
Added "validate" parameter to Constraint, allowing for looser validation
of complex, multi-fitpage setups.

  • Property mode set to 100644
File size: 5.8 KB
RevLine 
[7fd20fc]1"""
2Widget for parameter constraints.
3"""
[e147ce2]4import os
[5e66738]5import webbrowser
6
7# numpy methods required for the validator! Don't remove.
8# pylint: disable=unused-import,unused-wildcard-import,redefined-builtin
[0595bb7]9from numpy import *
10
[7fd20fc]11from PyQt5 import QtWidgets
12
13import sas.qtgui.Utilities.GuiUtils as GuiUtils
14
15# Local UI
16from sas.qtgui.Perspectives.Fitting.UI.MultiConstraintUI import Ui_MultiConstraintUI
17
18class MultiConstraint(QtWidgets.QDialog, Ui_MultiConstraintUI):
[5e66738]19    """
20    Logic class for interacting with MultiConstrainedUI view
21    """
[baeac95]22    def __init__(self, parent=None, params=None, constraint=None):
[5e66738]23        """
24        parent: ConstraintWidget object
25        params: tuple of strings describing model parameters
26        """
[7fd20fc]27        super(MultiConstraint, self).__init__()
28
29        self.setupUi(self)
30        self.setModal(True)
31        self.params = params
[5e66738]32        self.parent = parent
[09e0c32]33        # Text of the constraint
[baeac95]34        self.function = None
[09e0c32]35        # Should this constraint be validated?
36        self.validate = True
[baeac95]37
38        self.input_constraint = constraint
39        if self.input_constraint is not None:
40            variable = constraint.value
41            self.function = constraint.func
42            self.params.append(variable)
43            self.model_name = constraint.value_ex
[09e0c32]44            # Passed constraint may be too complex for simple validation
45            self.validate = constraint.validate
[baeac95]46        else:
47            self.model_name = self.params[1]
[7fd20fc]48
49        self.setupLabels()
50        self.setupTooltip()
51
[0595bb7]52        # Set param text control to the second parameter passed
[baeac95]53        if self.input_constraint is None:
54            self.txtConstraint.setText(self.params[1])
55        else:
56            self.txtConstraint.setText(self.function)
[7fd20fc]57        self.cmdOK.clicked.connect(self.accept)
[d3c0b95]58        self.cmdHelp.clicked.connect(self.onHelp)
[7fd20fc]59        self.cmdRevert.clicked.connect(self.revert)
[0595bb7]60        self.txtConstraint.editingFinished.connect(self.validateFormula)
[7fd20fc]61
[14ec91c5]62        # Default focus is on OK
63        self.cmdOK.setFocus()
64
[7fd20fc]65    def revert(self):
66        """
[5e66738]67        Swap parameters in the view
[7fd20fc]68        """
[eae226b]69        # Switch parameters
[7fd20fc]70        self.params[1], self.params[0] = self.params[0], self.params[1]
[baeac95]71        # change fully qualified param name (e.g. M1.sld -> M1.sld_solvent)
72        self.model_name = self.model_name.replace(self.params[0], self.params[1])
[eae226b]73        # Try to swap parameter names in the line edit
74        current_text = self.txtConstraint.text()
75        new_text = current_text.replace(self.params[0], self.params[1])
76        self.txtConstraint.setText(new_text)
77        # Update labels and tooltips
[7fd20fc]78        self.setupLabels()
79        self.setupTooltip()
80
81    def setupLabels(self):
82        """
83        Setup labels based on current parameters
84        """
[baeac95]85        l1 = str(self.params[0])
86        l2 = str(self.params[1])
[7fd20fc]87        self.txtParam1.setText(l1)
88        self.txtParam1_2.setText(l1)
89        self.txtParam2.setText(l2)
90
91    def setupTooltip(self):
92        """
93        Tooltip for txtConstraint
94        """
95        tooltip = "E.g.\n%s = 2.0 * (%s)\n" %(self.params[0], self.params[1])
96        tooltip += "%s = sqrt(%s) + 5"%(self.params[0], self.params[1])
97        self.txtConstraint.setToolTip(tooltip)
98
[0595bb7]99    def validateFormula(self):
100        """
101        Add visual cues when formula is incorrect
102        """
[09e0c32]103        # Don't validate if requested
104        if not self.validate: return
105
[0595bb7]106        formula_is_valid = False
107        formula_is_valid = self.validateConstraint(self.txtConstraint.text())
108        if not formula_is_valid:
109            self.cmdOK.setEnabled(False)
110            self.txtConstraint.setStyleSheet("QLineEdit {background-color: red;}")
111        else:
112            self.cmdOK.setEnabled(True)
113            self.txtConstraint.setStyleSheet("QLineEdit {background-color: white;}")
114
115    def validateConstraint(self, constraint_text):
116        """
117        Ensure the constraint has proper form
118        """
119        # 0. none or empty
120        if not constraint_text or not isinstance(constraint_text, str):
121            return False
122
[baeac95]123        param_str = self.model_name
[0595bb7]124
125        # 1. just the parameter
126        if param_str == constraint_text:
127            return True
128
129        # 2. ensure the text contains parameter name
130        parameter_string_start = constraint_text.find(param_str)
[eae226b]131        if parameter_string_start < 0:
[0595bb7]132            return False
133        parameter_string_end = parameter_string_start + len(param_str)
134
[eae226b]135        # 3. parameter name should be a separate word, but can have "()[]*+-/ " around
[3b3b40b]136        #valid_neighbours = "()[]*+-/ "
137        #start_loc = parameter_string_start -1
138        #end_loc = parameter_string_end
139        #if not any([constraint_text[start_loc] == ch for ch in valid_neighbours]):
140        #    return False
141        #if end_loc < len(constraint_text):
142        #    if not any([constraint_text[end_loc] == ch for ch in valid_neighbours]):
143        #        return False
[0595bb7]144
145        # 4. replace parameter name with "1" and try to evaluate the expression
146        try:
147            expression_to_evaluate = constraint_text.replace(param_str, "1.0")
148            eval(expression_to_evaluate)
149        except Exception:
150            # Too many cases to cover individually, just a blanket
151            # Exception should be sufficient
152            # Note that in current numpy things like sqrt(-1) don't
153            # raise but just return warnings
154            return False
155
156        return True
157
[d3c0b95]158    def onHelp(self):
159        """
160        Display related help section
161        """
162        try:
[e147ce2]163            help_location = GuiUtils.HELP_DIRECTORY_LOCATION + \
[aed0532]164            "/user/qtgui/Perspectives/Fitting/fitting_help.html#simultaneous-fits-with-constraints"
[e147ce2]165            webbrowser.open('file://' + os.path.realpath(help_location))
[d3c0b95]166        except AttributeError:
167            # No manager defined - testing and standalone runs
168            pass
[0595bb7]169
[7fd20fc]170
Note: See TracBrowser for help on using the repository browser.