source: sasview/src/sas/qtgui/Perspectives/Fitting/ConstraintWidget.py @ 6e58f2f

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

Fixed inter-branch contamination

  • Property mode set to 100644
File size: 24.8 KB
RevLine 
[731efec]1import logging
[676f137]2
[116dd4c1]3from twisted.internet import threads
4
[676f137]5import sas.qtgui.Utilities.GuiUtils as GuiUtils
[14ec91c5]6import sas.qtgui.Utilities.LocalConfig as LocalConfig
7
[676f137]8from PyQt5 import QtGui, QtCore, QtWidgets
9
[116dd4c1]10from sas.sascalc.fit.BumpsFitting import BumpsFit as Fit
[676f137]11
[116dd4c1]12import sas.qtgui.Utilities.ObjectLibrary as ObjectLibrary
[676f137]13from sas.qtgui.Perspectives.Fitting.UI.ConstraintWidgetUI import Ui_ConstraintWidgetUI
[be8f4b0]14from sas.qtgui.Perspectives.Fitting.FittingWidget import FittingWidget
[116dd4c1]15from sas.qtgui.Perspectives.Fitting.FitThread import FitThread
16from sas.qtgui.Perspectives.Fitting.ConsoleUpdate import ConsoleUpdate
[2d466e4]17from sas.qtgui.Perspectives.Fitting.ComplexConstraint import ComplexConstraint
[14ec91c5]18from sas.qtgui.Perspectives.Fitting.Constraint import Constraint
[676f137]19
20class ConstraintWidget(QtWidgets.QWidget, Ui_ConstraintWidgetUI):
21    """
[be8f4b0]22    Constraints Dialog to select the desired parameter/model constraints.
[676f137]23    """
24
[14ec91c5]25    def __init__(self, parent=None):
[676f137]26        super(ConstraintWidget, self).__init__()
27        self.parent = parent
28        self.setupUi(self)
29        self.currentType = "FitPage"
[116dd4c1]30        # Page id for fitting
31        # To keep with previous SasView values, use 300 as the start offset
[14ec91c5]32        self.page_id = 301
[676f137]33
[47d7d2d]34        # Remember previous content of modified cell
35        self.current_cell = ""
36
[ba01ad1]37        # Tabs used in simultaneous fitting
38        # tab_name : True/False
39        self.tabs_for_fitting = {}
40
[be8f4b0]41        # Set up the widgets
42        self.initializeWidgets()
43
[676f137]44        # Set up signals/slots
45        self.initializeSignals()
46
47        # Create the list of tabs
[be8f4b0]48        self.initializeFitList()
49
50    def acceptsData(self):
51        """ Tells the caller this widget doesn't accept data """
52        return False
53
54    def initializeWidgets(self):
55        """
56        Set up various widget states
57        """
[47d7d2d]58        labels = ['FitPage', 'Model', 'Data', 'Mnemonic']
[be8f4b0]59        # tab widget - headers
[47d7d2d]60        self.editable_tab_columns = [labels.index('Mnemonic')]
[be8f4b0]61        self.tblTabList.setColumnCount(len(labels))
62        self.tblTabList.setHorizontalHeaderLabels(labels)
63        self.tblTabList.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
64
65        self.tblTabList.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
66        self.tblTabList.customContextMenuRequested.connect(self.showModelContextMenu)
67
68        # disabled constraint
69        labels = ['Constraint']
70        self.tblConstraints.setColumnCount(len(labels))
71        self.tblConstraints.setHorizontalHeaderLabels(labels)
72        self.tblConstraints.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
73        self.tblConstraints.setEnabled(False)
74
75        self.tblConstraints.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
76        self.tblConstraints.customContextMenuRequested.connect(self.showConstrContextMenu)
[676f137]77
78    def initializeSignals(self):
79        """
80        Set up signals/slots for this widget
81        """
[47d7d2d]82        # simple widgets
[676f137]83        self.btnSingle.toggled.connect(self.onFitTypeChange)
84        self.btnBatch.toggled.connect(self.onFitTypeChange)
[be8f4b0]85        self.cbCases.currentIndexChanged.connect(self.onSpecialCaseChange)
[676f137]86        self.cmdFit.clicked.connect(self.onFit)
87        self.cmdHelp.clicked.connect(self.onHelp)
[47d7d2d]88
89        # QTableWidgets
[ba01ad1]90        self.tblTabList.cellChanged.connect(self.onTabCellEdit)
[47d7d2d]91        self.tblTabList.cellDoubleClicked.connect(self.onTabCellEntered)
92        self.tblConstraints.cellChanged.connect(self.onConstraintChange)
93
94        # External signals
[a90c9c5]95        self.parent.tabsModifiedSignal.connect(self.initializeFitList)
[be8f4b0]96
97    def updateSignalsFromTab(self, tab=None):
98        """
99        Intercept update signals from fitting tabs
100        """
[731efec]101        if tab is None:
102            return
103        tab_object = ObjectLibrary.getObject(tab)
104
105        # Disconnect all local slots
106        tab_object.disconnect()
107
108        # Reconnect tab signals to local slots
109        tab_object.constraintAddedSignal.connect(self.initializeFitList)
110        tab_object.newModelSignal.connect(self.initializeFitList)
[676f137]111
112    def onFitTypeChange(self, checked):
113        """
114        Respond to the fit type change
115        single fit/batch fit
116        """
[ba01ad1]117        source = self.sender().objectName()
118        self.currentType = "BatchPage" if source == "btnBatch" else "FitPage"
119        self.initializeFitList()
[676f137]120
121    def onSpecialCaseChange(self, index):
122        """
123        Respond to the combobox change for special case constraint sets
124        """
125        pass
126
[14ec91c5]127    def getTabsForFit(self):
128        """
129        Returns list of tab names selected for fitting
130        """
131        return [tab for tab in self.tabs_for_fitting if self.tabs_for_fitting[tab]]
132
[676f137]133    def onFit(self):
134        """
135        Perform the constrained/simultaneous fit
136        """
[116dd4c1]137        # Find out all tabs to fit
[14ec91c5]138        tabs_to_fit = self.getTabsForFit()
[116dd4c1]139
140        # Single fitter for the simultaneous run
[6ca0da0]141        fitter = Fit()
142        fitter.fitter_id = self.page_id
[116dd4c1]143
[14ec91c5]144        # Notify the parent about fitting started
145        self.parent.fittingStartedSignal.emit(tabs_to_fit)
146
[116dd4c1]147        # prepare fitting problems for each tab
148        #
149        page_ids = []
150        fitter_id = 0
[6ca0da0]151        sim_fitter_list=[fitter]
[c6343a5]152        # Prepare the fitter object
153        try:
154            for tab in tabs_to_fit:
155                tab_object = ObjectLibrary.getObject(tab)
156                if tab_object is None:
157                    # No such tab!
158                    return
[6ca0da0]159                sim_fitter_list, fitter_id = tab_object.prepareFitters(fitter=sim_fitter_list[0], fit_id=fitter_id)
[c6343a5]160                page_ids.append([tab_object.page_id])
[6ca0da0]161        except ValueError:
[c6343a5]162            # No parameters selected in one of the tabs
163            no_params_msg = "Fitting can not be performed.\n" +\
164                            "Not all tabs chosen for fitting have parameters selected for fitting."
[6ca0da0]165            QtWidgets.QMessageBox.question(self,
166                                           'Warning',
167                                            no_params_msg,
168                                            QtWidgets.QMessageBox.Ok)
[c6343a5]169
170            return
[116dd4c1]171
172        # Create the fitting thread, based on the fitter
173        completefn = self.onBatchFitComplete if self.currentType=='BatchPage' else self.onFitComplete
174
[14ec91c5]175        if LocalConfig.USING_TWISTED:
176            handler = None
177            updater = None
178        else:
179            handler = ConsoleUpdate(parent=self.parent,
180                                    manager=self,
181                                    improvement_delta=0.1)
182            updater = handler.update_fit
[116dd4c1]183
184        batch_inputs = {}
185        batch_outputs = {}
186
187        # new fit thread object
188        calc_fit = FitThread(handler=handler,
[6ca0da0]189                             fn=sim_fitter_list,
[116dd4c1]190                             batch_inputs=batch_inputs,
191                             batch_outputs=batch_outputs,
192                             page_id=page_ids,
193                             updatefn=updater,
194                             completefn=completefn)
195
[14ec91c5]196        if LocalConfig.USING_TWISTED:
197            # start the trhrhread with twisted
198            calc_thread = threads.deferToThread(calc_fit.compute)
199            calc_thread.addCallback(completefn)
200            calc_thread.addErrback(self.onFitFailed)
201        else:
202            # Use the old python threads + Queue
203            calc_fit.queue()
204            calc_fit.ready(2.5)
[116dd4c1]205
206
207        #disable the Fit button
208        self.cmdFit.setText('Running...')
209        self.parent.communicate.statusBarUpdateSignal.emit('Fitting started...')
210        self.cmdFit.setEnabled(False)
[676f137]211
212    def onHelp(self):
213        """
[c6343a5]214        Show the "Fitting" section of help
[676f137]215        """
[c6343a5]216        tree_location = "/user/sasgui/perspectives/fitting/"
217
218        helpfile = "fitting_help.html#simultaneous-fit-mode"
219        help_location = tree_location + helpfile
220
221        # OMG, really? Crawling up the object hierarchy...
222        self.parent.parent.showHelp(help_location)
[676f137]223
[ba01ad1]224    def onTabCellEdit(self, row, column):
[47d7d2d]225        """
[ba01ad1]226        Respond to check/uncheck and to modify the model moniker actions
[47d7d2d]227        """
[ba01ad1]228        item = self.tblTabList.item(row, column)
229        if column == 0:
230            # Update the tabs for fitting list
231            tab_name = item.text()
232            self.tabs_for_fitting[tab_name] = (item.checkState() == QtCore.Qt.Checked)
233            # Enable fitting only when there are models to fit
234            self.cmdFit.setEnabled(any(self.tabs_for_fitting.values()))
235
[47d7d2d]236        if column not in self.editable_tab_columns:
237            return
238        new_moniker = item.data(0)
239
240        # The new name should be validated on the fly, with QValidator
241        # but let's just assure it post-factum
242        is_good_moniker = self.validateMoniker(new_moniker)
243        if not is_good_moniker:
[eb1a386]244            self.tblTabList.blockSignals(True)
[47d7d2d]245            item.setBackground(QtCore.Qt.red)
[eb1a386]246            self.tblTabList.blockSignals(False)
[47d7d2d]247            self.cmdFit.setEnabled(False)
[ba01ad1]248            return
249        self.tblTabList.blockSignals(True)
250        item.setBackground(QtCore.Qt.white)
251        self.tblTabList.blockSignals(False)
252        self.cmdFit.setEnabled(True)
253        if not self.current_cell:
254            return
255        # Remember the value
256        if self.current_cell not in self.available_tabs:
257            return
258        temp_tab = self.available_tabs[self.current_cell]
259        # Remove the key from the dictionaries
260        self.available_tabs.pop(self.current_cell, None)
261        # Change the model name
262        model = temp_tab.kernel_module
263        model.name = new_moniker
264        # Replace constraint name
265        temp_tab.replaceConstraintName(self.current_cell, new_moniker)
[0764593]266        # Replace constraint name in the remaining tabs
267        for tab in self.available_tabs.values():
268            tab.replaceConstraintName(self.current_cell, new_moniker)
[ba01ad1]269        # Reinitialize the display
270        self.initializeFitList()
[47d7d2d]271
272    def onConstraintChange(self, row, column):
273        """
[116dd4c1]274        Modify the constraint's "active" instance variable.
[47d7d2d]275        """
[ba01ad1]276        item = self.tblConstraints.item(row, column)
277        if column == 0:
278            # Update the tabs for fitting list
279            constraint = self.available_constraints[row]
280            constraint.active = (item.checkState() == QtCore.Qt.Checked)
[47d7d2d]281
282    def onTabCellEntered(self, row, column):
283        """
284        Remember the original tab list cell data.
285        Needed for reverting back on bad validation
286        """
287        if column != 3:
288            return
289        self.current_cell = self.tblTabList.item(row, column).data(0)
290
[116dd4c1]291    def onFitComplete(self, result):
292        """
293        Respond to the successful fit complete signal
294        """
[17968c3]295        #re-enable the Fit button
296        self.cmdFit.setText("Fit")
297        self.cmdFit.setEnabled(True)
298
[14ec91c5]299        # Notify the parent about completed fitting
300        self.parent.fittingStoppedSignal.emit(self.getTabsForFit())
301
[235d766]302        # Assure the fitting succeeded
303        if result is None or not result:
304            msg = "Fitting failed. Please ensure correctness of chosen constraints."
305            self.parent.communicate.statusBarUpdateSignal.emit(msg)
306            return
307
[c6343a5]308        # get the elapsed time
309        elapsed = result[1]
310
311        # result list
312        results = result[0][0]
313
314        # Find out all tabs to fit
315        tabs_to_fit = [tab for tab in self.tabs_for_fitting if self.tabs_for_fitting[tab]]
316
317        # update all involved tabs
318        for i, tab in enumerate(tabs_to_fit):
319            tab_object = ObjectLibrary.getObject(tab)
320            if tab_object is None:
321                # No such tab. removed while job was running
322                return
323            # Make sure result and target objects are the same (same model moniker)
324            if tab_object.kernel_module.name == results[i].model.name:
325                tab_object.fitComplete(([[results[i]]], elapsed))
[116dd4c1]326
[17968c3]327        msg = "Fitting completed successfully in: %s s.\n" % GuiUtils.formatNumber(elapsed)
328        self.parent.communicate.statusBarUpdateSignal.emit(msg)
329
[116dd4c1]330    def onBatchFitComplete(self, result):
331        """
332        Respond to the successful batch fit complete signal
333        """
[17968c3]334        #re-enable the Fit button
335        self.cmdFit.setText("Fit")
336        self.cmdFit.setEnabled(True)
337
[14ec91c5]338        # Notify the parent about completed fitting
339        self.parent.fittingStoppedSignal.emit(self.getTabsForFit())
340
[17968c3]341        # get the elapsed time
342        elapsed = result[1]
343
344        # ADD THE BATCH FIT VIEW HERE
345        #
346
347        msg = "Fitting completed successfully in: %s s.\n" % GuiUtils.formatNumber(elapsed)
348        self.parent.communicate.statusBarUpdateSignal.emit(msg)
349
[116dd4c1]350    def onFitFailed(self, reason):
351        """
[17968c3]352        Respond to fitting failure.
[116dd4c1]353        """
[17968c3]354        #re-enable the Fit button
355        self.cmdFit.setText("Fit")
356        self.cmdFit.setEnabled(True)
357
[14ec91c5]358        # Notify the parent about completed fitting
359        self.parent.fittingStoppedSignal.emit(self.getTabsForFit())
360
[17968c3]361        msg = "Fitting failed: %s s.\n" % reason
362        self.parent.communicate.statusBarUpdateSignal.emit(msg)
[116dd4c1]363 
[be8f4b0]364    def isTabImportable(self, tab):
[676f137]365        """
[be8f4b0]366        Determines if the tab can be imported and included in the widget
[676f137]367        """
[da9a0722]368        if not isinstance(tab, str): return False
[be8f4b0]369        if not self.currentType in tab: return False
370        object = ObjectLibrary.getObject(tab)
371        if not isinstance(object, FittingWidget): return False
372        if object.data is None: return False
373        return True
374
375    def showModelContextMenu(self, position):
376        """
377        Show context specific menu in the tab table widget.
378        """
379        menu = QtWidgets.QMenu()
380        rows = [s.row() for s in self.tblTabList.selectionModel().selectedRows()]
381        num_rows = len(rows)
382        if num_rows <= 0:
[676f137]383            return
[be8f4b0]384        # Select for fitting
385        param_string = "Fit Page " if num_rows==1 else "Fit Pages "
[676f137]386
[be8f4b0]387        self.actionSelect = QtWidgets.QAction(self)
388        self.actionSelect.setObjectName("actionSelect")
389        self.actionSelect.setText(QtCore.QCoreApplication.translate("self", "Select "+param_string+" for fitting"))
390        # Unselect from fitting
391        self.actionDeselect = QtWidgets.QAction(self)
392        self.actionDeselect.setObjectName("actionDeselect")
393        self.actionDeselect.setText(QtCore.QCoreApplication.translate("self", "De-select "+param_string+" from fitting"))
394
395        self.actionRemoveConstraint = QtWidgets.QAction(self)
396        self.actionRemoveConstraint.setObjectName("actionRemoveConstrain")
397        self.actionRemoveConstraint.setText(QtCore.QCoreApplication.translate("self", "Remove all constraints on selected models"))
398
399        self.actionMutualMultiConstrain = QtWidgets.QAction(self)
400        self.actionMutualMultiConstrain.setObjectName("actionMutualMultiConstrain")
401        self.actionMutualMultiConstrain.setText(QtCore.QCoreApplication.translate("self", "Mutual constrain of parameters in selected models..."))
402
403        menu.addAction(self.actionSelect)
404        menu.addAction(self.actionDeselect)
405        menu.addSeparator()
406
407        if num_rows >= 2:
408            menu.addAction(self.actionMutualMultiConstrain)
[676f137]409
[be8f4b0]410        # Define the callbacks
[47d7d2d]411        self.actionMutualMultiConstrain.triggered.connect(self.showMultiConstraint)
[be8f4b0]412        self.actionSelect.triggered.connect(self.selectModels)
413        self.actionDeselect.triggered.connect(self.deselectModels)
414        try:
415            menu.exec_(self.tblTabList.viewport().mapToGlobal(position))
416        except AttributeError as ex:
417            logging.error("Error generating context menu: %s" % ex)
418        return
419
420    def showConstrContextMenu(self, position):
[676f137]421        """
[be8f4b0]422        Show context specific menu in the tab table widget.
[676f137]423        """
[be8f4b0]424        menu = QtWidgets.QMenu()
425        rows = [s.row() for s in self.tblConstraints.selectionModel().selectedRows()]
426        num_rows = len(rows)
427        if num_rows <= 0:
428            return
429        # Select for fitting
430        param_string = "constraint " if num_rows==1 else "constraints "
431
432        self.actionSelect = QtWidgets.QAction(self)
433        self.actionSelect.setObjectName("actionSelect")
434        self.actionSelect.setText(QtCore.QCoreApplication.translate("self", "Select "+param_string+" for fitting"))
435        # Unselect from fitting
436        self.actionDeselect = QtWidgets.QAction(self)
437        self.actionDeselect.setObjectName("actionDeselect")
438        self.actionDeselect.setText(QtCore.QCoreApplication.translate("self", "De-select "+param_string+" from fitting"))
[676f137]439
[be8f4b0]440        self.actionRemoveConstraint = QtWidgets.QAction(self)
441        self.actionRemoveConstraint.setObjectName("actionRemoveConstrain")
442        self.actionRemoveConstraint.setText(QtCore.QCoreApplication.translate("self", "Remove "+param_string))
443
444        menu.addAction(self.actionSelect)
445        menu.addAction(self.actionDeselect)
446        menu.addSeparator()
447        menu.addAction(self.actionRemoveConstraint)
448
449        # Define the callbacks
450        self.actionRemoveConstraint.triggered.connect(self.deleteConstraint)
451        self.actionSelect.triggered.connect(self.selectConstraints)
452        self.actionDeselect.triggered.connect(self.deselectConstraints)
453        try:
454            menu.exec_(self.tblConstraints.viewport().mapToGlobal(position))
455        except AttributeError as ex:
456            logging.error("Error generating context menu: %s" % ex)
457        return
458
459    def selectConstraints(self):
460        """
461        Selected constraints are chosen for fitting
462        """
463        status = QtCore.Qt.Checked
464        self.setRowSelection(self.tblConstraints, status)
465
466    def deselectConstraints(self):
467        """
468        Selected constraints are removed for fitting
469        """
470        status = QtCore.Qt.Unchecked
471        self.setRowSelection(self.tblConstraints, status)
472
473    def selectModels(self):
474        """
475        Selected models are chosen for fitting
476        """
477        status = QtCore.Qt.Checked
478        self.setRowSelection(self.tblTabList, status)
479
480    def deselectModels(self):
481        """
482        Selected models are removed for fitting
483        """
484        status = QtCore.Qt.Unchecked
485        self.setRowSelection(self.tblTabList, status)
486
487    def selectedParameters(self, widget):
488        """ Returns list of selected (highlighted) parameters """
489        return [s.row() for s in widget.selectionModel().selectedRows()]
490
491    def setRowSelection(self, widget, status=QtCore.Qt.Unchecked):
492        """
493        Selected models are chosen for fitting
494        """
495        # Convert to proper indices and set requested enablement
496        for row in self.selectedParameters(widget):
497            widget.item(row, 0).setCheckState(status)
498
499    def deleteConstraint(self):#, row):
500        """
501        Delete all selected constraints.
502        """
[47d7d2d]503        # Removing rows from the table we're iterating over,
504        # so prepare a list of data first
[be8f4b0]505        constraints_to_delete = []
506        for row in self.selectedParameters(self.tblConstraints):
507            constraints_to_delete.append(self.tblConstraints.item(row, 0).data(0))
508        for constraint in constraints_to_delete:
509            moniker = constraint[:constraint.index(':')]
510            param = constraint[constraint.index(':')+1:constraint.index('=')].strip()
511            tab = self.available_tabs[moniker]
512            tab.deleteConstraintOnParameter(param)
513        # Constraints removed - refresh the table widget
514        self.initializeFitList()
515
[47d7d2d]516    def uneditableItem(self, data=""):
517        """
518        Returns an uneditable Table Widget Item
519        """
520        item = QtWidgets.QTableWidgetItem(data)
521        item.setFlags( QtCore.Qt.ItemIsSelectable |  QtCore.Qt.ItemIsEnabled )
522        return item
523
[be8f4b0]524    def updateFitLine(self, tab):
525        """
526        Update a single line of the table widget with tab info
527        """
[ba01ad1]528        fit_page = ObjectLibrary.getObject(tab)
529        model = fit_page.kernel_module
[be8f4b0]530        if model is None:
531            return
532        tab_name = tab
533        model_name = model.id
534        moniker = model.name
[ba01ad1]535        model_data = fit_page.data
[be8f4b0]536        model_filename = model_data.filename
[ba01ad1]537        self.available_tabs[moniker] = fit_page
[be8f4b0]538
539        # Update the model table widget
540        pos = self.tblTabList.rowCount()
541        self.tblTabList.insertRow(pos)
[47d7d2d]542        item = self.uneditableItem(tab_name)
543        item.setFlags(item.flags() ^ QtCore.Qt.ItemIsUserCheckable)
[ba01ad1]544        if tab_name in self.tabs_for_fitting:
545            state = QtCore.Qt.Checked if self.tabs_for_fitting[tab_name] else QtCore.Qt.Unchecked
546            item.setCheckState(state)
547        else:
548            item.setCheckState(QtCore.Qt.Checked)
549            self.tabs_for_fitting[tab_name] = True
550
[731efec]551        # Disable signals so we don't get infinite call recursion
552        self.tblTabList.blockSignals(True)
[be8f4b0]553        self.tblTabList.setItem(pos, 0, item)
[47d7d2d]554        self.tblTabList.setItem(pos, 1, self.uneditableItem(model_name))
555        self.tblTabList.setItem(pos, 2, self.uneditableItem(model_filename))
556        # Moniker is editable, so no option change
557        item = QtWidgets.QTableWidgetItem(moniker)
558        self.tblTabList.setItem(pos, 3, item)
559        self.tblTabList.blockSignals(False)
[be8f4b0]560
561        # Check if any constraints present in tab
[235d766]562        constraint_names = fit_page.getComplexConstraintsForModel()
[ba01ad1]563        constraints = fit_page.getConstraintObjectsForModel()
[be8f4b0]564        if not constraints: 
565            return
566        self.tblConstraints.setEnabled(True)
[731efec]567        self.tblConstraints.blockSignals(True)
[ba01ad1]568        for constraint, constraint_name in zip(constraints, constraint_names):
[be8f4b0]569            # Create the text for widget item
[ba01ad1]570            label = moniker + ":"+ constraint_name[0] + " = " + constraint_name[1]
571            pos = self.tblConstraints.rowCount()
572            self.available_constraints[pos] = constraint
[be8f4b0]573
574            # Show the text in the constraint table
[ba01ad1]575            item = self.uneditableItem(label)
576            item.setFlags(item.flags() ^ QtCore.Qt.ItemIsUserCheckable)
[be8f4b0]577            item.setCheckState(QtCore.Qt.Checked)
578            self.tblConstraints.insertRow(pos)
579            self.tblConstraints.setItem(pos, 0, item)
[731efec]580        self.tblConstraints.blockSignals(False)
[be8f4b0]581
582    def initializeFitList(self):
583        """
584        Fill the list of model/data sets for fitting/constraining
585        """
586        # look at the object library to find all fit tabs
587        # Show the content of the current "model"
588        objects = ObjectLibrary.listObjects()
589
590        # Tab dict
591        # moniker -> (kernel_module, data)
592        self.available_tabs = {}
593        # Constraint dict
594        # moniker -> [constraints]
595        self.available_constraints = {}
596
597        # Reset the table widgets
598        self.tblTabList.setRowCount(0)
599        self.tblConstraints.setRowCount(0)
600
601        # Fit disabled
602        self.cmdFit.setEnabled(False)
603
604        if not objects:
605            return
[676f137]606
[be8f4b0]607        tabs = [tab for tab in ObjectLibrary.listObjects() if self.isTabImportable(tab)]
608        for tab in tabs:
609            self.updateFitLine(tab)
610            self.updateSignalsFromTab(tab)
611            # We have at least 1 fit page, allow fitting
612            self.cmdFit.setEnabled(True)
[47d7d2d]613
614    def validateMoniker(self, new_moniker=None):
615        """
616        Check new_moniker for correctness.
617        It must be non-empty.
618        It must not be the same as other monikers.
619        """
620        if not new_moniker:
621            return False
622
623        for existing_moniker in self.available_tabs:
624            if existing_moniker == new_moniker and existing_moniker != self.current_cell:
625                return False
626
627        return True
628
[c5a2722f]629    def getObjectByName(self, name):
[731efec]630        """
631        Given name of the fit, returns associated fit object
632        """
[c5a2722f]633        for object_name in ObjectLibrary.listObjects():
634            object = ObjectLibrary.getObject(object_name)
635            if isinstance(object, FittingWidget):
636                try:
637                    if object.kernel_module.name == name:
638                        return object
639                except AttributeError:
640                    # Disregard atribute errors - empty fit widgets
641                    continue
642        return None
643
[47d7d2d]644    def showMultiConstraint(self):
645        """
646        Invoke the complex constraint editor
647        """
[2d466e4]648        selected_rows = self.selectedParameters(self.tblTabList)
649        assert(len(selected_rows)==2)
650
651        tab_list = [ObjectLibrary.getObject(self.tblTabList.item(s, 0).data(0)) for s in selected_rows]
652        # Create and display the widget for param1 and param2
653        cc_widget = ComplexConstraint(self, tabs=tab_list)
654        if cc_widget.exec_() != QtWidgets.QDialog.Accepted:
655            return
656
[c5a2722f]657        constraint = Constraint()
658        model1, param1, operator, constraint_text = cc_widget.constraint()
659
660        constraint.func = constraint_text
[ba01ad1]661        constraint.param = param1
[c5a2722f]662        # Find the right tab
663        constrained_tab = self.getObjectByName(model1)
664        if constrained_tab is None:
665            return
666
667        # Find the constrained parameter row
668        constrained_row = constrained_tab.getRowFromName(param1)
669
670        # Update the tab
671        constrained_tab.addConstraintToRow(constraint, constrained_row)
Note: See TracBrowser for help on using the repository browser.