source: sasview/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py @ 6f7f652

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

interactions between combo boxes on the fitting tab

  • Property mode set to 100755
File size: 16.1 KB
Line 
1import sys
2import json
3import  os
4from collections import defaultdict
5
6from PyQt4 import QtGui
7from PyQt4 import QtCore
8
9from UI.FittingWidgetUI import Ui_FittingWidgetUI
10
11from sasmodels import generate
12from sasmodels import modelinfo
13from sas.sasgui.guiframe.CategoryInstaller import CategoryInstaller
14
15TAB_MAGNETISM = 4
16TAB_POLY = 3
17
18class FittingWidget(QtGui.QWidget, Ui_FittingWidgetUI):
19    """
20    Main widget for selecting form and structure factor models
21    """
22    def __init__(self, manager=None, parent=None, data=None):
23        """
24
25        :param manager:
26        :param parent:
27        :return:
28        """
29        super(FittingWidget, self).__init__()
30
31        self.model_is_loaded = False
32        self._data = data
33        self.is2D = False
34        self.modelHasShells = False
35        self.data_assigned = False
36
37        self.setupUi(self)
38
39        self.setWindowTitle("Fitting")
40
41        # set the main models
42        self._model_model = QtGui.QStandardItemModel()
43        self._poly_model = QtGui.QStandardItemModel()
44        self._magnet_model = QtGui.QStandardItemModel()
45
46        # Param model displayed in param list
47        self.lstParams.setModel(self._model_model)
48        self._readCategoryInfo()
49        self.model_parameters = None
50
51        # Poly model displayed in poly list
52        self.lstPoly.setModel(self._poly_model)
53        self.setPolyModel()
54        self.setTableProperties(self.lstPoly)
55
56        # Magnetism model displayed in magnetism list
57        self.lstMagnetic.setModel(self._magnet_model)
58        self.setMagneticModel()
59        self.setTableProperties(self.lstMagnetic)
60
61        # Defaults for the strcutre factors
62        self.setDefaultStructureCombo()
63
64        # make structure factor and model CBs disabled
65        self.disableModelCombo()
66        self.disableStructureCombo()
67
68        # Generate the category list for display
69        category_list = sorted(self.master_category_dict.keys())
70        self.cbCategory.addItem("Choose category...")
71        self.cbCategory.addItems(category_list)
72        self.cbCategory.addItem("Structure Factor")
73        self.cbCategory.setCurrentIndex(0)
74
75        # Connect GUI element signals
76        self.cbStructureFactor.currentIndexChanged.connect(self.selectStructureFactor)
77        self.cbCategory.currentIndexChanged.connect(self.selectCategory)
78        self.cbModel.currentIndexChanged.connect(self.selectModel)
79        self.chk2DView.toggled.connect(self.toggle2D)
80        self.chkPolydispersity.toggled.connect(self.togglePoly)
81        self.chkMagnetism.toggled.connect(self.toggleMagnetism)
82
83        # Set initial control enablement
84        self.cmdFit.setEnabled(False)
85        self.cmdPlot.setEnabled(True)
86        self.chkPolydispersity.setEnabled(True)
87        self.chkPolydispersity.setCheckState(False)
88        self.chk2DView.setEnabled(True)
89        self.chk2DView.setCheckState(False)
90        self.chkMagnetism.setEnabled(False)
91        self.chkMagnetism.setCheckState(False)
92
93        self.tabFitting.setTabEnabled(TAB_POLY, False)
94        self.tabFitting.setTabEnabled(TAB_MAGNETISM, False)
95
96        # set initial labels
97        self.lblMinRangeDef.setText("---")
98        self.lblMaxRangeDef.setText("---")
99        self.lblChi2Value.setText("---")
100
101    @property
102    def data(self):
103        return self._data
104
105    @data.setter
106    def data(self, value):
107        """ data setter """
108        self._data = value
109        self.data_assigned = True
110        # TODO: update ranges, chi2 etc
111
112    def acceptsData(self):
113        """ Tells the caller this widget can accept new dataset """
114        return not self.data_assigned
115
116    def disableModelCombo(self):
117        self.cbModel.setEnabled(False)
118        self.label_3.setEnabled(False)
119
120    def enableModelCombo(self):
121        self.cbModel.setEnabled(True)
122        self.label_3.setEnabled(True)
123
124    def disableStructureCombo(self):
125        self.cbStructureFactor.setEnabled(False)
126        self.label_4.setEnabled(False)
127
128    def enableStructureCombo(self):
129        self.cbStructureFactor.setEnabled(True)
130        self.label_4.setEnabled(True)
131
132    def setDefaultStructureCombo(self):
133        # Fill in the structure factors combo box with defaults
134        structure_factor_list = self.master_category_dict.pop('Structure Factor')
135        structure_factors = []
136        self.cbStructureFactor.clear()
137        for (structure_factor, _) in structure_factor_list:
138            structure_factors.append(structure_factor)
139        self.cbStructureFactor.addItems(sorted(structure_factors))
140
141    def selectCategory(self):
142        """
143        Select Category from list
144        :return:
145        """
146        category = self.cbCategory.currentText()
147        if category == "Structure Factor":
148            self.disableModelCombo()
149            self.enableStructureCombo()
150            return
151
152        self.cbModel.blockSignals(True)
153        self.cbModel.clear()
154        self.cbModel.blockSignals(False)
155        self.enableModelCombo()
156        self.disableStructureCombo()
157
158        model_list = self.master_category_dict[str(category)]
159        models = []
160        for (model, _) in model_list:
161            models.append(model)
162        self.cbModel.addItems(sorted(models))
163
164    def selectModel(self):
165        """
166        Select Model from list
167        :return:
168        """
169        model = self.cbModel.currentText()
170        self.setModelModel(model)
171
172    def selectStructureFactor(self):
173        """
174        Select Structure Factor from list
175        :param:
176        :return:
177        """
178        model = self.cbStructureFactor.currentText()
179        self.setModelModel(model)
180
181    def _readCategoryInfo(self):
182        """
183        Reads the categories in from file
184        """
185        self.master_category_dict = defaultdict(list)
186        self.by_model_dict = defaultdict(list)
187        self.model_enabled_dict = defaultdict(bool)
188
189        try:
190            categorization_file = CategoryInstaller.get_user_file()
191            if not os.path.isfile(categorization_file):
192                categorization_file = CategoryInstaller.get_default_file()
193            cat_file = open(categorization_file, 'rb')
194            self.master_category_dict = json.load(cat_file)
195            self._regenerate_model_dict()
196            cat_file.close()
197        except IOError:
198            raise
199            print 'Problem reading in category file.'
200            print 'We even looked for it, made sure it was there.'
201            print 'An existential crisis if there ever was one.'
202
203    def _regenerate_model_dict(self):
204        """
205        regenerates self.by_model_dict which has each model name as the
206        key and the list of categories belonging to that model
207        along with the enabled mapping
208        """
209        self.by_model_dict = defaultdict(list)
210        for category in self.master_category_dict:
211            for (model, enabled) in self.master_category_dict[category]:
212                self.by_model_dict[model].append(category)
213                self.model_enabled_dict[model] = enabled
214
215    def getIterParams(self, model):
216        """
217        Returns a list of all multi-shell parameters in 'model'
218        """
219        iter_params = list(filter(lambda par: "[" in par.name, model.iq_parameters))
220
221        return iter_params
222
223    def getMultiplicity(self, model):
224        """
225        Finds out if 'model' has multishell parameters.
226        If so, returns the name of the counter parameter and the number of shells
227        """
228        iter_param = ""
229        iter_length = 0
230
231        iter_params = self.getIterParams(model)
232        # pull out the iterator parameter name and length
233        if iter_params:
234            iter_length = iter_params[0].length
235            iter_param = iter_params[0].length_control
236        return (iter_param, iter_length)
237       
238    def setModelModel(self, model_name):
239        """
240        Setting model parameters into table based on selected
241        :param model_name:
242        :return:
243        """
244        # Crete/overwrite model items
245        self._model_model.clear()
246        model_name = str(model_name)
247        kernel_module = generate.load_kernel_module(model_name)
248        self.model_parameters = modelinfo.make_parameter_table(getattr(kernel_module, 'parameters', []))
249
250        #TODO: scaale and background are implicit in sasmodels and needs to be added
251        item1 = QtGui.QStandardItem('scale')
252        item1.setCheckable(True)
253        item2 = QtGui.QStandardItem('1.0')
254        item3 = QtGui.QStandardItem('0.0')
255        item4 = QtGui.QStandardItem('inf')
256        item5 = QtGui.QStandardItem('')
257        self._model_model.appendRow([item1, item2, item3, item4, item5])
258
259        item1 = QtGui.QStandardItem('background')
260        item1.setCheckable(True)
261        item2 = QtGui.QStandardItem('0.001')
262        item3 = QtGui.QStandardItem('-inf')
263        item4 = QtGui.QStandardItem('inf')
264        item5 = QtGui.QStandardItem('1/cm')
265        self._model_model.appendRow([item1, item2, item3, item4, item5])
266
267        multishell_parameters = self.getIterParams(self.model_parameters)
268        multishell_param_name, _ = self.getMultiplicity(self.model_parameters)
269
270        #TODO: iq_parameters are used here. If orientation paramateres or magnetic are needed
271        # kernel_paramters should be used instead
272        #For orientation and magentic parameters param.type needs to be checked
273        for param in self.model_parameters.iq_parameters:
274            # don't include shell parameters
275            if param.name == multishell_param_name:
276                continue
277            # Modify parameter name from <param>[n] to <param>1
278            item_name = param.name
279            if param in multishell_parameters:
280                item_name = self.replaceShellName(param.name, 1)
281            item1 = QtGui.QStandardItem(item_name)
282            item1.setCheckable(True)
283            item2 = QtGui.QStandardItem(str(param.default))
284            item3 = QtGui.QStandardItem(str(param.limits[0]))
285            item4 = QtGui.QStandardItem(str(param.limits[1]))
286            item5 = QtGui.QStandardItem(param.units)
287            self._model_model.appendRow([item1, item2, item3, item4, item5])
288
289        self._model_model.setHeaderData(0, QtCore.Qt.Horizontal, QtCore.QVariant("Parameter"))
290        self._model_model.setHeaderData(1, QtCore.Qt.Horizontal, QtCore.QVariant("Value"))
291        self._model_model.setHeaderData(2, QtCore.Qt.Horizontal, QtCore.QVariant("Min"))
292        self._model_model.setHeaderData(3, QtCore.Qt.Horizontal, QtCore.QVariant("Max"))
293        self._model_model.setHeaderData(4, QtCore.Qt.Horizontal, QtCore.QVariant("[Units]"))
294
295        self.addExtraShells()
296
297        self.setPolyModel()
298        self.setMagneticModel()
299        self.model_is_loaded = True
300
301    def replaceShellName(self, param_name, value):
302        """
303        Updates parameter name from <param_name>[n_shell] to <param_name>value
304        """
305        assert('[' in param_name)
306        return param_name[:param_name.index('[')]+str(value)
307
308    def setTableProperties(self, table):
309        """
310        Setting table properties
311        :param table:
312        :return:
313        """
314        # Table properties
315        table.verticalHeader().setVisible(False)
316        table.setAlternatingRowColors(True)
317        table.setSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding)
318        table.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
319        table.resizeColumnsToContents()
320
321        # Header
322        header = table.horizontalHeader()
323        header.setResizeMode(QtGui.QHeaderView.ResizeToContents)
324
325        header.ResizeMode(QtGui.QHeaderView.Interactive)
326        header.setResizeMode(0, QtGui.QHeaderView.ResizeToContents)
327        header.setResizeMode(6, QtGui.QHeaderView.ResizeToContents)
328
329    def setPolyModel(self):
330        """
331        Set polydispersity values
332        """
333        if self.model_parameters:
334            for row, param in enumerate(self.model_parameters.form_volume_parameters):
335                item1 = QtGui.QStandardItem("Distribution of "+param.name)
336                item1.setCheckable(True)
337                item2 = QtGui.QStandardItem("0")
338                item3 = QtGui.QStandardItem("")
339                item4 = QtGui.QStandardItem("")
340                item5 = QtGui.QStandardItem("35")
341                item6 = QtGui.QStandardItem("3")
342                item7 = QtGui.QStandardItem("")
343
344                self._poly_model.appendRow([item1, item2, item3, item4, item5, item6, item7])
345
346                #TODO: Need to find cleaner way to input functions
347                func = QtGui.QComboBox()
348                func.addItems(['rectangle','array','lognormal','gaussian','schulz',])
349                func_index = self.lstPoly.model().index(row,6)
350                self.lstPoly.setIndexWidget(func_index,func)
351
352        self._poly_model.setHeaderData(0, QtCore.Qt.Horizontal, QtCore.QVariant("Parameter"))
353        self._poly_model.setHeaderData(1, QtCore.Qt.Horizontal, QtCore.QVariant("PD[ratio]"))
354        self._poly_model.setHeaderData(2, QtCore.Qt.Horizontal, QtCore.QVariant("Min"))
355        self._poly_model.setHeaderData(3, QtCore.Qt.Horizontal, QtCore.QVariant("Max"))
356        self._poly_model.setHeaderData(4, QtCore.Qt.Horizontal, QtCore.QVariant("Npts"))
357        self._poly_model.setHeaderData(5, QtCore.Qt.Horizontal, QtCore.QVariant("Nsigs"))
358        self._poly_model.setHeaderData(6, QtCore.Qt.Horizontal, QtCore.QVariant("Function"))
359
360    def setMagneticModel(self):
361        """
362        Set magnetism values on model
363        """
364        if self.model_parameters:
365            for row, param in enumerate(self.model_parameters.form_volume_parameters):
366                item1 = QtGui.QStandardItem("Distribution of "+param.name)
367                item1.setCheckable(True)
368                item2 = QtGui.QStandardItem("0")
369                item3 = QtGui.QStandardItem("")
370                item4 = QtGui.QStandardItem("")
371                item5 = QtGui.QStandardItem("35")
372                item6 = QtGui.QStandardItem("3")
373                item7 = QtGui.QStandardItem("")
374
375                self._magnet_model.appendRow([item1, item2, item3, item4, item5, item6, item7])
376
377                #TODO: Need to find cleaner way to input functions
378                func = QtGui.QComboBox()
379                func.addItems(['rectangle','array','lognormal','gaussian','schulz',])
380                func_index = self.lstMagnetic.model().index(row,6)
381                self.lstMagnetic.setIndexWidget(func_index,func)
382
383        self._magnet_model.setHeaderData(0, QtCore.Qt.Horizontal, QtCore.QVariant("Parameter"))
384        self._magnet_model.setHeaderData(1, QtCore.Qt.Horizontal, QtCore.QVariant("PD[ratio]"))
385        self._magnet_model.setHeaderData(2, QtCore.Qt.Horizontal, QtCore.QVariant("Min"))
386        self._magnet_model.setHeaderData(3, QtCore.Qt.Horizontal, QtCore.QVariant("Max"))
387        self._magnet_model.setHeaderData(4, QtCore.Qt.Horizontal, QtCore.QVariant("Npts"))
388        self._magnet_model.setHeaderData(5, QtCore.Qt.Horizontal, QtCore.QVariant("Nsigs"))
389        self._magnet_model.setHeaderData(6, QtCore.Qt.Horizontal, QtCore.QVariant("Function"))
390
391    def addExtraShells(self):
392        """
393        Add a combobox for multiple shell display
394        """
395        param_name, param_length = self.getMultiplicity(self.model_parameters)
396
397        if param_length == 0:
398            return
399
400        # cell 1: variable name
401        item1 = QtGui.QStandardItem(param_name)
402
403        func = QtGui.QComboBox()
404        func.addItems([str(i+1) for i in xrange(param_length)])
405
406        # cell 2: combobox
407        item2 = QtGui.QStandardItem()
408        self._model_model.appendRow([item1, item2])
409
410        # Beautify the row:  span columns 2-4
411        shell_row = self._model_model.rowCount()
412        shell_index = self._model_model.index(shell_row-1, 1)
413        self.lstParams.setIndexWidget(shell_index, func)
414        self.lstParams.setSpan(shell_row-1,2,2,4)
415
416    def togglePoly(self, isChecked):
417        """
418        Enable/disable the polydispersity tab
419        """
420        self.tabFitting.setTabEnabled(TAB_POLY, isChecked)
421
422    def toggleMagnetism(self, isChecked):
423        """
424        Enable/disable the magnetism tab
425        """
426        self.tabFitting.setTabEnabled(TAB_MAGNETISM, isChecked)
427
428    def toggle2D(self, isChecked):
429        """
430        Enable/disable the controls dependent on 1D/2D data instance
431        """
432        self.chkMagnetism.setEnabled(isChecked)
433        self.is2D = isChecked
434
Note: See TracBrowser for help on using the repository browser.