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

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

Context menus for tab and constraint list widgets.
Dynamic bindings of add/remove for FPs and the C&S widget

  • Property mode set to 100755
File size: 12.6 KB
Line 
1import os
2import sys
3
4import sas.qtgui.Utilities.GuiUtils as GuiUtils
5from PyQt5 import QtGui, QtCore, QtWidgets
6
7import sas.qtgui.Utilities.ObjectLibrary as ObjectLibrary
8
9from sas.qtgui.Perspectives.Fitting.UI.ConstraintWidgetUI import Ui_ConstraintWidgetUI
10from sas.qtgui.Perspectives.Fitting.FittingWidget import FittingWidget
11
12class ConstraintWidget(QtWidgets.QWidget, Ui_ConstraintWidgetUI):
13    """
14    Constraints Dialog to select the desired parameter/model constraints.
15    """
16
17    def __init__(self, parent=None):
18        super(ConstraintWidget, self).__init__()
19        self.parent = parent
20        self.setupUi(self)
21        self.currentType = "FitPage"
22
23        # Set up the widgets
24        self.initializeWidgets()
25
26        # Set up signals/slots
27        self.initializeSignals()
28
29        # Create the list of tabs
30        self.initializeFitList()
31
32    def acceptsData(self):
33        """ Tells the caller this widget doesn't accept data """
34        return False
35
36    def initializeWidgets(self):
37        """
38        Set up various widget states
39        """
40        labels = ['FitPage', 'Model', 'Data', 'Mnemonics']
41        # tab widget - headers
42        self.tblTabList.setColumnCount(len(labels))
43        self.tblTabList.setHorizontalHeaderLabels(labels)
44        self.tblTabList.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
45
46        self.tblTabList.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
47        self.tblTabList.customContextMenuRequested.connect(self.showModelContextMenu)
48
49        # disabled constraint
50        labels = ['Constraint']
51        self.tblConstraints.setColumnCount(len(labels))
52        self.tblConstraints.setHorizontalHeaderLabels(labels)
53        self.tblConstraints.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
54        self.tblConstraints.setEnabled(False)
55
56        self.tblConstraints.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
57        self.tblConstraints.customContextMenuRequested.connect(self.showConstrContextMenu)
58
59    def initializeSignals(self):
60        """
61        Set up signals/slots for this widget
62        """
63        self.btnSingle.toggled.connect(self.onFitTypeChange)
64        self.btnBatch.toggled.connect(self.onFitTypeChange)
65        self.cbCases.currentIndexChanged.connect(self.onSpecialCaseChange)
66        self.cmdFit.clicked.connect(self.onFit)
67        self.cmdHelp.clicked.connect(self.onHelp)
68        self.parent.tabsModifiedSignal.connect(self.initializeFitList)
69
70    def updateSignalsFromTab(self, tab=None):
71        """
72        Intercept update signals from fitting tabs
73        """
74        if tab is not None:
75            ObjectLibrary.getObject(tab).constraintAddedSignal.connect(self.initializeFitList)
76            ObjectLibrary.getObject(tab).newModelSignal.connect(self.initializeFitList)
77
78    def onFitTypeChange(self, checked):
79        """
80        Respond to the fit type change
81        single fit/batch fit
82        """
83        pass
84
85    def onSpecialCaseChange(self, index):
86        """
87        Respond to the combobox change for special case constraint sets
88        """
89        pass
90
91    def onFit(self):
92        """
93        Perform the constrained/simultaneous fit
94        """
95        pass
96
97    def onHelp(self):
98        """
99        Display the help page
100        """
101        pass
102
103    def isTabImportable(self, tab):
104        """
105        Determines if the tab can be imported and included in the widget
106        """
107        if not self.currentType in tab: return False
108        object = ObjectLibrary.getObject(tab)
109        if not isinstance(object, FittingWidget): return False
110        if object.data is None: return False
111        return True
112
113    def showModelContextMenu(self, position):
114        """
115        Show context specific menu in the tab table widget.
116        """
117        menu = QtWidgets.QMenu()
118        rows = [s.row() for s in self.tblTabList.selectionModel().selectedRows()]
119        num_rows = len(rows)
120        if num_rows <= 0:
121            return
122        # Select for fitting
123        param_string = "Fit Page " if num_rows==1 else "Fit Pages "
124        to_string = "to its current value" if num_rows==1 else "to their current values"
125
126        self.actionSelect = QtWidgets.QAction(self)
127        self.actionSelect.setObjectName("actionSelect")
128        self.actionSelect.setText(QtCore.QCoreApplication.translate("self", "Select "+param_string+" for fitting"))
129        # Unselect from fitting
130        self.actionDeselect = QtWidgets.QAction(self)
131        self.actionDeselect.setObjectName("actionDeselect")
132        self.actionDeselect.setText(QtCore.QCoreApplication.translate("self", "De-select "+param_string+" from fitting"))
133
134        self.actionRemoveConstraint = QtWidgets.QAction(self)
135        self.actionRemoveConstraint.setObjectName("actionRemoveConstrain")
136        self.actionRemoveConstraint.setText(QtCore.QCoreApplication.translate("self", "Remove all constraints on selected models"))
137
138        self.actionMutualMultiConstrain = QtWidgets.QAction(self)
139        self.actionMutualMultiConstrain.setObjectName("actionMutualMultiConstrain")
140        self.actionMutualMultiConstrain.setText(QtCore.QCoreApplication.translate("self", "Mutual constrain of parameters in selected models..."))
141
142        menu.addAction(self.actionSelect)
143        menu.addAction(self.actionDeselect)
144        menu.addSeparator()
145
146        #menu.addAction(self.actionRemoveConstraint)
147        if num_rows >= 2:
148            menu.addAction(self.actionMutualMultiConstrain)
149
150        # Define the callbacks
151        #self.actionConstrain.triggered.connect(self.addSimpleConstraint)
152        #self.actionRemoveConstraint.triggered.connect(self.deleteConstraint)
153        #self.actionMutualMultiConstrain.triggered.connect(self.showMultiConstraint)
154        self.actionSelect.triggered.connect(self.selectModels)
155        self.actionDeselect.triggered.connect(self.deselectModels)
156        try:
157            menu.exec_(self.tblTabList.viewport().mapToGlobal(position))
158        except AttributeError as ex:
159            logging.error("Error generating context menu: %s" % ex)
160        return
161
162    def showConstrContextMenu(self, position):
163        """
164        Show context specific menu in the tab table widget.
165        """
166        menu = QtWidgets.QMenu()
167        rows = [s.row() for s in self.tblConstraints.selectionModel().selectedRows()]
168        num_rows = len(rows)
169        if num_rows <= 0:
170            return
171        # Select for fitting
172        param_string = "constraint " if num_rows==1 else "constraints "
173        to_string = "to its current value" if num_rows==1 else "to their current values"
174
175        self.actionSelect = QtWidgets.QAction(self)
176        self.actionSelect.setObjectName("actionSelect")
177        self.actionSelect.setText(QtCore.QCoreApplication.translate("self", "Select "+param_string+" for fitting"))
178        # Unselect from fitting
179        self.actionDeselect = QtWidgets.QAction(self)
180        self.actionDeselect.setObjectName("actionDeselect")
181        self.actionDeselect.setText(QtCore.QCoreApplication.translate("self", "De-select "+param_string+" from fitting"))
182
183        self.actionRemoveConstraint = QtWidgets.QAction(self)
184        self.actionRemoveConstraint.setObjectName("actionRemoveConstrain")
185        self.actionRemoveConstraint.setText(QtCore.QCoreApplication.translate("self", "Remove "+param_string))
186
187        menu.addAction(self.actionSelect)
188        menu.addAction(self.actionDeselect)
189        menu.addSeparator()
190        menu.addAction(self.actionRemoveConstraint)
191
192        # Define the callbacks
193        #self.actionConstrain.triggered.connect(self.addSimpleConstraint)
194        self.actionRemoveConstraint.triggered.connect(self.deleteConstraint)
195        #self.actionMutualMultiConstrain.triggered.connect(self.showMultiConstraint)
196        self.actionSelect.triggered.connect(self.selectConstraints)
197        self.actionDeselect.triggered.connect(self.deselectConstraints)
198        try:
199            menu.exec_(self.tblConstraints.viewport().mapToGlobal(position))
200        except AttributeError as ex:
201            logging.error("Error generating context menu: %s" % ex)
202        return
203
204    def selectConstraints(self):
205        """
206        Selected constraints are chosen for fitting
207        """
208        status = QtCore.Qt.Checked
209        self.setRowSelection(self.tblConstraints, status)
210
211    def deselectConstraints(self):
212        """
213        Selected constraints are removed for fitting
214        """
215        status = QtCore.Qt.Unchecked
216        self.setRowSelection(self.tblConstraints, status)
217
218    def selectModels(self):
219        """
220        Selected models are chosen for fitting
221        """
222        status = QtCore.Qt.Checked
223        self.setRowSelection(self.tblTabList, status)
224
225    def deselectModels(self):
226        """
227        Selected models are removed for fitting
228        """
229        status = QtCore.Qt.Unchecked
230        self.setRowSelection(self.tblTabList, status)
231
232    def selectedParameters(self, widget):
233        """ Returns list of selected (highlighted) parameters """
234        return [s.row() for s in widget.selectionModel().selectedRows()]
235
236    def setRowSelection(self, widget, status=QtCore.Qt.Unchecked):
237        """
238        Selected models are chosen for fitting
239        """
240        # Convert to proper indices and set requested enablement
241        for row in self.selectedParameters(widget):
242            widget.item(row, 0).setCheckState(status)
243
244    def deleteConstraint(self):#, row):
245        """
246        Delete all selected constraints.
247        """
248        constraints_to_delete = []
249        for row in self.selectedParameters(self.tblConstraints):
250            constraints_to_delete.append(self.tblConstraints.item(row, 0).data(0))
251        for constraint in constraints_to_delete:
252            moniker = constraint[:constraint.index(':')]
253            param = constraint[constraint.index(':')+1:constraint.index('=')].strip()
254            tab = self.available_tabs[moniker]
255            tab.deleteConstraintOnParameter(param)
256        # Constraints removed - refresh the table widget
257        self.initializeFitList()
258
259    def updateFitLine(self, tab):
260        """
261        Update a single line of the table widget with tab info
262        """
263        model = ObjectLibrary.getObject(tab).kernel_module
264        if model is None:
265            return
266        tab_name = tab
267        model_name = model.id
268        moniker = model.name
269        model_data = ObjectLibrary.getObject(tab).data
270        model_filename = model_data.filename
271        self.available_tabs[moniker] = ObjectLibrary.getObject(tab)
272
273        # Update the model table widget
274        item = QtWidgets.QTableWidgetItem(tab_name)
275        item.setCheckState(QtCore.Qt.Checked)
276        pos = self.tblTabList.rowCount()
277        self.tblTabList.insertRow(pos)
278        self.tblTabList.setItem(pos, 0, item)
279        self.tblTabList.setItem(pos, 1, QtWidgets.QTableWidgetItem(model_name))
280        self.tblTabList.setItem(pos, 2, QtWidgets.QTableWidgetItem(model_filename))
281        self.tblTabList.setItem(pos, 3, QtWidgets.QTableWidgetItem(moniker))
282
283        #self.available_tabs[pos] = (model, model_data)
284        #self.available_tabs[moniker] = tab
285
286        # Check if any constraints present in tab
287        constraints = ObjectLibrary.getObject(tab).getConstraintsForModel()
288        if not constraints: 
289            return
290        self.tblConstraints.setEnabled(True)
291        for constraint in constraints:
292            # Create the text for widget item
293            label = moniker + ":"+ constraint[0] + " = " + constraint[1]
294
295            # Show the text in the constraint table
296            item = QtWidgets.QTableWidgetItem(label)
297            item.setCheckState(QtCore.Qt.Checked)
298            pos = self.tblConstraints.rowCount()
299            self.tblConstraints.insertRow(pos)
300            self.tblConstraints.setItem(pos, 0, item)
301            self.available_constraints[pos] = constraints
302
303    def initializeFitList(self):
304        """
305        Fill the list of model/data sets for fitting/constraining
306        """
307        # look at the object library to find all fit tabs
308        # Show the content of the current "model"
309        objects = ObjectLibrary.listObjects()
310
311        # Tab dict
312        # moniker -> (kernel_module, data)
313        self.available_tabs = {}
314        # Constraint dict
315        # moniker -> [constraints]
316        self.available_constraints = {}
317
318        # Reset the table widgets
319        self.tblTabList.setRowCount(0)
320        self.tblConstraints.setRowCount(0)
321
322        # Fit disabled
323        self.cmdFit.setEnabled(False)
324
325        if not objects:
326            return
327
328        tabs = [tab for tab in ObjectLibrary.listObjects() if self.isTabImportable(tab)]
329        for tab in tabs:
330            self.updateFitLine(tab)
331            self.updateSignalsFromTab(tab)
332            # We have at least 1 fit page, allow fitting
333            self.cmdFit.setEnabled(True)
Note: See TracBrowser for help on using the repository browser.