source: sasview/src/sas/qtgui/Perspectives/Fitting/FittingOptions.py @ ff8cb73

Last change on this file since ff8cb73 was 377ade1, checked in by Piotr Rozyczko <rozyczko@…>, 7 years ago

Fixing unit tests + removal of unnecessary files

  • Property mode set to 100755
File size: 7.7 KB
Line 
1# global
2import sys
3import os
4import types
5
6from PyQt4 import QtCore
7from PyQt4 import QtGui
8from PyQt4 import QtWebKit
9
10from sas.qtgui.UI import images_rc
11from sas.qtgui.UI import main_resources_rc
12import sas.qtgui.Utilities.GuiUtils as GuiUtils
13
14from bumps import fitters
15import bumps.options
16
17from sas.qtgui.Perspectives.Fitting.UI.FittingOptionsUI import Ui_FittingOptions
18
19# Set the default optimizer
20fitters.FIT_DEFAULT_ID = 'lm'
21
22
23class FittingOptions(QtGui.QDialog, Ui_FittingOptions):
24    """
25    Hard-coded version of the fit options dialog available from BUMPS.
26    This should be make more "dynamic".
27    bumps.options.FIT_FIELDS gives mapping between parameter names, parameter strings and field type
28     (double line edit, integer line edit, combo box etc.), e.g.
29        FIT_FIELDS = dict(
30            samples = ("Samples", parse_int),
31            xtol = ("x tolerance", float))
32
33    bumps.fitters.<algorithm>.settings gives mapping between algorithm, parameter name and default value:
34        e.g.
35        settings = [('steps', 1000), ('starts', 1), ('radius', 0.15), ('xtol', 1e-6), ('ftol', 1e-8)]
36    """
37    fit_option_changed = QtCore.pyqtSignal(str)
38
39    def __init__(self, parent=None, config=None):
40        super(FittingOptions, self).__init__(parent)
41        self.setupUi(self)
42
43        self.config = config
44
45        # no reason to have this widget resizable
46        self.setFixedSize(self.minimumSizeHint())
47
48        self.setWindowTitle("Fit Algorithms")
49
50        # Fill up the algorithm combo, based on what BUMPS says is available
51        self.cbAlgorithm.addItems([n.name for n in fitters.FITTERS if n.id in fitters.FIT_ACTIVE_IDS])
52
53        # Handle the Apply button click
54        self.buttonBox.button(QtGui.QDialogButtonBox.Ok).clicked.connect(self.onApply)
55        # handle the Help button click
56        self.buttonBox.button(QtGui.QDialogButtonBox.Help).clicked.connect(self.onHelp)
57
58        # Handle the combo box changes
59        self.cbAlgorithm.currentIndexChanged.connect(self.onAlgorithmChange)
60
61        # Set the default index
62        default_name = [n.name for n in fitters.FITTERS if n.id == fitters.FIT_DEFAULT_ID][0]
63        default_index = self.cbAlgorithm.findText(default_name)
64        self.cbAlgorithm.setCurrentIndex(default_index)
65
66        # Assign appropriate validators
67        self.assignValidators()
68
69        # Set defaults
70        self.current_fitter_id = fitters.FIT_DEFAULT_ID
71
72        # OK has to be initialized to True, after initial validator setup
73        self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(True)
74
75        # Display HTML content
76        self.helpView = QtWebKit.QWebView()
77
78    def assignValidators(self):
79        """
80        Use options.FIT_FIELDS to assert which line edit gets what validator
81        """
82        for option in bumps.options.FIT_FIELDS.iterkeys():
83            (f_name, f_type) = bumps.options.FIT_FIELDS[option]
84            validator = None
85            if type(f_type) == types.FunctionType:
86                validator = QtGui.QIntValidator()
87                validator.setBottom(0)
88            elif f_type == types.FloatType:
89                validator = QtGui.QDoubleValidator()
90                validator.setBottom(0)
91            else:
92                continue
93            for fitter_id in fitters.FIT_ACTIVE_IDS:
94                line_edit = self.widgetFromOption(str(option), current_fitter=str(fitter_id))
95                if hasattr(line_edit, 'setValidator') and validator is not None:
96                    line_edit.setValidator(validator)
97                    line_edit.textChanged.connect(self.check_state)
98                    line_edit.textChanged.emit(line_edit.text())
99
100    def check_state(self, *args, **kwargs):
101        sender = self.sender()
102        validator = sender.validator()
103        state = validator.validate(sender.text(), 0)[0]
104        if state == QtGui.QValidator.Acceptable:
105            color = '' # default
106            self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(True)
107        else:
108            color = '#fff79a' # yellow
109            self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(False)
110
111        sender.setStyleSheet('QLineEdit { background-color: %s }' % color)
112
113    def onAlgorithmChange(self, index):
114        """
115        Change the page in response to combo box index
116        """
117        # Find the algorithm ID from name
118        self.current_fitter_id = \
119            [n.id for n in fitters.FITTERS if n.name == str(self.cbAlgorithm.currentText())][0]
120
121        # find the right stacked widget
122        widget_name = "self.page_"+str(self.current_fitter_id)
123
124        # Convert the name into widget instance
125        widget_to_activate = eval(widget_name)
126        index_for_this_id = self.stackedWidget.indexOf(widget_to_activate)
127
128        # Select the requested widget
129        self.stackedWidget.setCurrentIndex(index_for_this_id)
130
131        self.updateWidgetFromBumps(self.current_fitter_id)
132
133        self.assignValidators()
134
135        # OK has to be reinitialized to True
136        self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(True)
137
138    def onApply(self):
139        """
140        Update the fitter object
141        """
142        # Notify the perspective, so the window title is updated
143        self.fit_option_changed.emit(self.cbAlgorithm.currentText())
144
145        def bumpsUpdate(option):
146            """
147            Utility method for bumps state update
148            """
149            widget = self.widgetFromOption(option)
150            new_value = widget.currentText() if isinstance(widget, QtGui.QComboBox) \
151                else float(widget.text())
152            self.config.values[self.current_fitter_id][option] = new_value
153
154        # Update the BUMPS singleton
155        [bumpsUpdate(o) for o in self.config.values[self.current_fitter_id].iterkeys()]
156
157    def onHelp(self):
158        """
159        Show the "Fitting options" section of help
160        """
161        tree_location = GuiUtils.HELP_DIRECTORY_LOCATION + "/user/sasgui/perspectives/fitting/"
162
163        # Actual file anchor will depend on the combo box index
164        # Note that we can be clusmy here, since bad current_fitter_id
165        # will just make the page displayed from the top
166        helpfile = "optimizer.html#fit-" + self.current_fitter_id
167        help_location = tree_location + helpfile
168        self.helpView.load(QtCore.QUrl(help_location))
169        self.helpView.show()
170
171    def widgetFromOption(self, option_id, current_fitter=None):
172        """
173        returns widget's element linked to the given option_id
174        """
175        if current_fitter is None:
176            current_fitter = self.current_fitter_id
177        if option_id not in bumps.options.FIT_FIELDS.keys(): return None
178        option = option_id + '_' + current_fitter
179        if not hasattr(self, option): return None
180        return eval('self.' + option)
181
182    def getResults(self):
183        """
184        Sends back the current choice of parameters
185        """
186        algorithm = self.cbAlgorithm.currentText()
187        return algorithm
188
189    def updateWidgetFromBumps(self, fitter_id):
190        """
191        Given the ID of the current optimizer, fetch the values
192        and update the widget
193        """
194        options = self.config.values[fitter_id]
195        for option in options.iterkeys():
196            # Find the widget name of the option
197            # e.g. 'samples' for 'dream' is 'self.samples_dream'
198            widget_name = 'self.'+option+'_'+fitter_id
199            if option not in bumps.options.FIT_FIELDS:
200                return
201            if isinstance(bumps.options.FIT_FIELDS[option][1], bumps.options.ChoiceList):
202                control = eval(widget_name)
203                control.setCurrentIndex(control.findText(str(options[option])))
204            else:
205                eval(widget_name).setText(str(options[option]))
206
207        pass
Note: See TracBrowser for help on using the repository browser.