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

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 5e66738 was 5e66738, checked in by Piotr Rozyczko <rozyczko@…>, 6 years ago

Code review cleanup

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