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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since d3c0b95 was d3c0b95, checked in by Piotr Rozyczko <rozyczko@…>, 6 years ago

Added constraints to the fitter

  • Property mode set to 100644
File size: 4.6 KB
Line 
1"""
2Widget for parameter constraints.
3"""
4from numpy import *
5
6from PyQt5 import QtCore
7from PyQt5 import QtGui
8from PyQt5 import QtWidgets
9
10import sas.qtgui.Utilities.GuiUtils as GuiUtils
11
12# Local UI
13from sas.qtgui.Perspectives.Fitting.UI.MultiConstraintUI import Ui_MultiConstraintUI
14
15class MultiConstraint(QtWidgets.QDialog, Ui_MultiConstraintUI):
16    def __init__(self, parent=None, params=None):
17        super(MultiConstraint, self).__init__()
18
19        self.setupUi(self)
20        self.setFixedSize(self.minimumSizeHint())
21        self.setModal(True)
22        self.params = params
23
24        self.setupLabels()
25        self.setupTooltip()
26
27        # Set param text control to the second parameter passed
28        self.txtConstraint.setText(self.params[1])
29
30        self.cmdOK.clicked.connect(self.accept)
31        self.cmdHelp.clicked.connect(self.onHelp)
32        self.cmdRevert.clicked.connect(self.revert)
33        self.txtConstraint.editingFinished.connect(self.validateFormula)
34
35    def revert(self):
36        """
37        switch M1 <-> M2
38        """
39        # Switch parameters
40        self.params[1], self.params[0] = self.params[0], self.params[1]
41        # Try to swap parameter names in the line edit
42        current_text = self.txtConstraint.text()
43        new_text = current_text.replace(self.params[0], self.params[1])
44        self.txtConstraint.setText(new_text)
45        # Update labels and tooltips
46        self.setupLabels()
47        self.setupTooltip()
48
49    def setupLabels(self):
50        """
51        Setup labels based on current parameters
52        """
53        l1 = self.params[0]
54        l2 = self.params[1]
55        self.txtParam1.setText(l1)
56        self.txtParam1_2.setText(l1)
57        self.txtParam2.setText(l2)
58
59    def setupTooltip(self):
60        """
61        Tooltip for txtConstraint
62        """
63        tooltip = "E.g.\n%s = 2.0 * (%s)\n" %(self.params[0], self.params[1])
64        tooltip += "%s = sqrt(%s) + 5"%(self.params[0], self.params[1])
65        self.txtConstraint.setToolTip(tooltip)
66
67    def validateFormula(self):
68        """
69        Add visual cues when formula is incorrect
70        """
71        formula_is_valid = False
72        formula_is_valid = self.validateConstraint(self.txtConstraint.text())
73        if not formula_is_valid:
74            self.cmdOK.setEnabled(False)
75            self.txtConstraint.setStyleSheet("QLineEdit {background-color: red;}")
76        else:
77            self.cmdOK.setEnabled(True)
78            self.txtConstraint.setStyleSheet("QLineEdit {background-color: white;}")
79
80    def validateConstraint(self, constraint_text):
81        """
82        Ensure the constraint has proper form
83        """
84        # 0. none or empty
85        if not constraint_text or not isinstance(constraint_text, str):
86            return False
87
88        param_str = str(self.params[1])
89        constraint_text = constraint_text.strip()
90
91        # 1. just the parameter
92        if param_str == constraint_text:
93            return True
94
95        # 2. ensure the text contains parameter name
96        parameter_string_start = constraint_text.find(param_str)
97        if parameter_string_start < 0:
98            return False
99        parameter_string_end = parameter_string_start + len(param_str)
100
101        # 3. parameter name should be a separate word, but can have "()[]*+-/ " around
102        valid_neighbours = "()[]*+-/ "
103        has_only_parameter = False
104        start_loc = parameter_string_start -1
105        end_loc = parameter_string_end
106        if not any([constraint_text[start_loc] == char for char in valid_neighbours]):
107            return False
108        if end_loc < len(constraint_text):
109            if not any([constraint_text[end_loc] == char for char in valid_neighbours]):
110                return False
111
112        # 4. replace parameter name with "1" and try to evaluate the expression
113        try:
114            expression_to_evaluate = constraint_text.replace(param_str, "1.0")
115            eval(expression_to_evaluate)
116        except Exception:
117            # Too many cases to cover individually, just a blanket
118            # Exception should be sufficient
119            # Note that in current numpy things like sqrt(-1) don't
120            # raise but just return warnings
121            return False
122
123        return True
124
125    def onHelp(self):
126        """
127        Display related help section
128        """
129        try:
130            location = GuiUtils.HELP_DIRECTORY_LOCATION + \
131            "/user/sasgui/perspectives/fitting/fitting_help.html#simultaneous-fits-with-constraints"
132
133            self.manager._helpView.load(QtCore.QUrl(location))
134            self.manager._helpView.show()
135        except AttributeError:
136            # No manager defined - testing and standalone runs
137            pass
138
139
Note: See TracBrowser for help on using the repository browser.