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

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

Added a warning label to simple and complex constraint widgets,
shown whenever polydisperse parameters are constrained SASVIEW-1042

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