source: sasview/src/sas/qtgui/Calculators/DataOperationUtilityPanel.py @ 0c468bf

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 0c468bf was 0c468bf, checked in by celinedurniak <celine.durniak@…>, 7 years ago

Corrected Data Operation Panel: disabling compute button when incompatible data

  • Property mode set to 100644
File size: 16.5 KB
Line 
1import time
2import logging
3import re
4import copy
5
6from PyQt4 import QtGui
7from PyQt4 import QtCore
8
9from sas.qtgui.Plotting.PlotterData import Data1D
10from sas.qtgui.Plotting.Plotter import PlotterWidget
11from sas.qtgui.Plotting.PlotterData import Data2D
12from sas.qtgui.Plotting.Plotter2D import Plotter2DWidget
13import sas.qtgui.Utilities.GuiUtils as GuiUtils
14
15from UI.DataOperationUtilityUI import Ui_DataOperationUtility
16
17BG_WHITE = "background-color: rgb(255, 255, 255);"
18BG_RED = "background-color: rgb(244, 170, 164);"
19
20
21class DataOperationUtilityPanel(QtGui.QDialog, Ui_DataOperationUtility):
22    def __init__(self, parent=None):
23        super(DataOperationUtilityPanel, self).__init__()
24        self.setupUi(self)
25        self.manager = parent
26        self.communicator = self.manager.communicator()
27
28        # To store input datafiles
29        self.filenames = None
30        self.list_data_items = []
31        self.data1 = None
32        self.data2 = None
33        # To store the result
34        self.output = None
35
36        # To update content of comboboxes with files loaded in DataExplorer
37        self.communicator.sendDataToPanelSignal.connect(self.updateCombobox)
38
39        # change index of comboboxes
40        self.cbData1.currentIndexChanged.connect(self.onSelectData1)
41        self.cbData2.currentIndexChanged.connect(self.onSelectData2)
42        self.cbOperator.currentIndexChanged.connect(self.onSelectOperator)
43
44        # edit Coefficient text edit
45        self.txtNumber.textChanged.connect(self.onInputCoefficient)
46        self.txtOutputData.textChanged.connect(self.onCheckOutputName)
47
48        # push buttons
49        self.cmdClose.clicked.connect(self.onClose)
50        self.cmdHelp.clicked.connect(self.onHelp)
51        self.cmdCompute.clicked.connect(self.onCompute)
52        self.cmdReset.clicked.connect(self.onReset)
53
54        self.cmdCompute.setEnabled(False)
55
56        # validator for coefficient
57        self.txtNumber.setValidator(QtGui.QDoubleValidator())
58
59        self.layoutOutput = QtGui.QHBoxLayout()
60        self.layoutData1 = QtGui.QHBoxLayout()
61        self.layoutData2 = QtGui.QHBoxLayout()
62
63        # Create default layout for initial graphs (when they are still empty)
64        self.newPlot(self.graphOutput, self.layoutOutput)
65        self.newPlot(self.graphData1, self.layoutData1)
66        self.newPlot(self.graphData2, self.layoutData2)
67
68        # Flag to enable Compute pushbutton
69        self.data2OK = False
70        self.data1OK = False
71
72    def updateCombobox(self, filenames):
73        """ Function to fill comboboxes with names of datafiles loaded in
74         DataExplorer. For Data2, there is the additional option of choosing
75         a number to apply to data1 """
76        self.filenames = filenames
77
78        if filenames.keys():
79            # clear contents of comboboxes
80            self.cbData1.clear()
81            self.cbData1.addItems(['Select Data'])
82            self.cbData2.clear()
83            self.cbData2.addItems(['Select Data', 'Number'])
84
85            list_datafiles = []
86
87            for key_id in filenames.keys():
88                if filenames[key_id].get_data().title:
89                    # filenames with titles
90                    new_title = filenames[key_id].get_data().title
91                    list_datafiles.append(new_title)
92                    self.list_data_items.append(new_title)
93
94                else:
95                    # filenames without titles by removing time.time()
96                    new_title = re.sub('\d{10}\.\d{2}', '', str(key_id))
97                    self.list_data_items.append(new_title)
98                    list_datafiles.append(new_title)
99
100            # update contents of comboboxes
101            self.cbData1.addItems(list_datafiles)
102            self.cbData2.addItems(list_datafiles)
103
104    def onHelp(self):
105        """
106        Bring up the Data Operation Utility Documentation whenever
107        the HELP button is clicked.
108        Calls Documentation Window with the path of the location within the
109        documentation tree (after /doc/ ....".
110        """
111        try:
112            location = GuiUtils.HELP_DIRECTORY_LOCATION + \
113                       "/user/sasgui/perspectives/calculator/data_operator_help.html"
114            self.manager._helpView.load(QtCore.QUrl(location))
115            self.manager._helpView.show()
116
117        except AttributeError:
118            # No manager defined - testing and standalone runs
119            pass
120
121    def onClose(self):
122        """ Close dialog """
123        self.onReset()
124        self.close()
125
126    def onCompute(self):
127        """ perform calculation """
128        # set operator to be applied
129        operator = self.cbOperator.currentText()
130        # calculate and send data to DataExplorer
131        output = None
132        try:
133            data1 = self.data1
134            data2 = self.data2
135            exec "output = data1 %s data2" % operator
136        except:
137            raise
138
139        self.output = output
140
141        # if outputname was unused, write output result to it
142        # and display plot
143        if self.onCheckOutputName():
144            # add outputname to self.filenames
145            self.list_data_items.append(str(self.txtOutputData.text()))
146            # send result to DataExplorer
147            self.onPrepareOutputData()
148            # plot result
149            self.updatePlot(self.graphOutput, self.layoutOutput, self.output)
150
151    def onPrepareOutputData(self):
152        """ Prepare datasets to be added to DataExplorer and DataManager """
153        new_item = GuiUtils.createModelItemWithPlot(
154            QtCore.QVariant(self.output),
155            name=self.txtOutputData.text())
156
157        new_datalist_item = {str(self.txtOutputData.text()) + str(time.time()):
158                                 self.output}
159        self.communicator. \
160            updateModelFromDataOperationPanelSignal.emit(new_item, new_datalist_item)
161
162    def onSelectOperator(self):
163        """ Change GUI when operator changed """
164        self.lblOperatorApplied.setText(self.cbOperator.currentText())
165        self.newPlot(self.graphOutput, self.layoutOutput)
166
167    def onReset(self):
168        """
169        Reset Panel to its initial state (default values) keeping
170        the names of loaded data
171        """
172        self.txtNumber.setText('1.0')
173        self.txtOutputData.setText('MyNewDataName')
174
175        self.txtNumber.setEnabled(False)
176        self.cmdCompute.setEnabled(False)
177
178        self.cbData1.setCurrentIndex(0)
179        self.cbData2.setCurrentIndex(0)
180        self.cbOperator.setCurrentIndex(0)
181
182        self.output = None
183        self.data1 = None
184        self.data2 = None
185        self.filenames = None
186        self.list_data_items = []
187
188        self.data1OK = False
189        self.data2OK = False
190
191        # Empty graphs
192        self.newPlot(self.graphOutput, self.layoutOutput)
193        self.newPlot(self.graphData1, self.layoutData1)
194        self.newPlot(self.graphData2, self.layoutData2)
195
196    def onSelectData1(self):
197        """ Plot for selection of Data1 """
198        choice_data1 = str(self.cbData1.currentText())
199
200        wrong_choices = ['No Data Available', 'Select Data', '']
201
202        if choice_data1 in wrong_choices:
203            # check validity of choice: input = filename
204            self.newPlot(self.graphData1, self.layoutData1)
205            self.data1 = None
206            self.data1OK = False
207            self.cmdCompute.setEnabled(False) # self.onCheckChosenData())
208            return
209
210        else:
211            self.data1OK = True
212            # get Data1
213            key_id1 = self._findId(choice_data1)
214            self.data1 = self._extractData(key_id1)
215            # plot Data1
216            self.updatePlot(self.graphData1, self.layoutData1, self.data1)
217            # plot default for output graph
218            self.newPlot(self.graphOutput, self.layoutOutput)
219            # Enable Compute button only if Data2 is defined and data compatible
220            self.cmdCompute.setEnabled(self.onCheckChosenData())
221
222    def onSelectData2(self):
223        """ Plot for selection of Data2 """
224        choice_data2 = str(self.cbData2.currentText())
225        wrong_choices = ['No Data Available', 'Select Data', '']
226
227        if choice_data2 in wrong_choices:
228            self.newPlot(self.graphData2, self.layoutData2)
229            self.txtNumber.setEnabled(False)
230            self.data2OK = False
231            self.onCheckChosenData()
232            self.cmdCompute.setEnabled(False)
233            return
234
235        elif choice_data2 == 'Number':
236            self.data2OK = True
237            self.txtNumber.setEnabled(True)
238            self.data2 = float(self.txtNumber.text())
239
240            # Enable Compute button only if Data1 defined and compatible data
241            self.cmdCompute.setEnabled(self.onCheckChosenData())
242            # Display value of coefficient in graphData2
243            self.updatePlot(self.graphData2, self.layoutData2, self.data2)
244            # plot default for output graph
245            self.newPlot(self.graphOutput, self.layoutOutput)
246            self.onCheckChosenData()
247
248        else:
249            self.txtNumber.setEnabled(False)
250            self.data2OK = True
251            key_id2 = self._findId(choice_data2)
252            self.data2 = self._extractData(key_id2)
253            self.cmdCompute.setEnabled(self.onCheckChosenData())
254
255            # plot Data2
256            self.updatePlot(self.graphData2, self.layoutData2, self.data2)
257            # plot default for output graph
258            self.newPlot(self.graphOutput, self.layoutOutput)
259
260    def onInputCoefficient(self):
261        """ Check input of number when a coefficient is required
262        for operation """
263        if self.txtNumber.isModified():
264            input_to_check = str(self.txtNumber.text())
265
266            if input_to_check is None or input_to_check is '':
267                msg = 'DataOperation: Number requires a float number'
268                logging.info(msg)
269                self.txtNumber.setStyleSheet(QtCore.QString(BG_RED))
270
271            elif float(self.txtNumber.text()) == 0.:
272                # should be check that 0 is not chosen
273                msg = 'DataOperation: Number requires a non zero number'
274                logging.info(msg)
275                self.txtNumber.setStyleSheet(QtCore.QString(BG_RED))
276
277            else:
278                self.txtNumber.setStyleSheet(QtCore.QString(BG_WHITE))
279                self.data2 = float(self.txtNumber.text())
280                self.updatePlot(self.graphData2, self.layoutData2, self.data2)
281
282    def onCheckChosenData(self):
283        """ check that data1 and data2 are compatible """
284
285        if not all([self.data1OK, self.data2OK]):
286            return False
287        else:
288            if self.cbData2.currentText() == 'Number':
289                self.cbData1.setStyleSheet(QtCore.QString(BG_WHITE))
290                self.cbData2.setStyleSheet(QtCore.QString(BG_WHITE))
291                return True
292
293            elif self.data1.__class__.__name__ != self.data2.__class__.__name__:
294                self.cbData1.setStyleSheet(QtCore.QString(BG_RED))
295                self.cbData2.setStyleSheet(QtCore.QString(BG_RED))
296                print self.data1.__class__.__name__ != self.data2.__class__.__name__
297                logging.info('Cannot compute data of different dimensions')
298                return False
299
300            elif self.data1.__class__.__name__ == 'Data1D'\
301                    and (len(self.data2.x) != len(self.data1.x) or
302                             not all(i == j for i, j in zip(self.data1.x, self.data2.x))):
303                logging.info('Cannot compute 1D data of different lengths')
304                self.cbData1.setStyleSheet(QtCore.QString(BG_RED))
305                self.cbData2.setStyleSheet(QtCore.QString(BG_RED))
306                return False
307
308            elif self.data1.__class__.__name__ == 'Data2D' \
309                    and (len(self.data2.qx_data) != len(self.data1.qx_data) \
310                    or len(self.data2.qy_data) != len(self.data1.qy_data)
311                    or not all(i == j for i, j in
312                                     zip(self.data1.qx_data, self.data2.qx_data))
313                    or not all(i == j for i, j in
314                                zip(self.data1.qy_data, self.data2.qy_data))
315                         ):
316                self.cbData1.setStyleSheet(QtCore.QString(BG_RED))
317                self.cbData2.setStyleSheet(QtCore.QString(BG_RED))
318                logging.info('Cannot compute 2D data of different lengths')
319                return False
320
321            else:
322                self.cbData1.setStyleSheet(QtCore.QString(BG_WHITE))
323                self.cbData2.setStyleSheet(QtCore.QString(BG_WHITE))
324                return True
325
326    def onCheckOutputName(self):
327        """ Check that name of output does not already exist """
328        name_to_check = str(self.txtOutputData.text())
329        self.txtOutputData.setStyleSheet(QtCore.QString(BG_WHITE))
330
331        if name_to_check is None or name_to_check == '':
332            self.txtOutputData.setStyleSheet(QtCore.QString(BG_RED))
333            logging.info('No output name')
334            return False
335
336        elif name_to_check in self.list_data_items:
337            self.txtOutputData.setStyleSheet(QtCore.QString(BG_RED))
338            logging.info('The Output data name already exists')
339            return False
340
341        else:
342            self.txtOutputData.setStyleSheet(QtCore.QString(BG_WHITE))
343            return True
344
345    # ########
346    # Modification of inputs
347    # ########
348    def _findId(self, name):
349        """ find id of name in list of filenames """
350        isinstance(name, basestring)
351
352        for key_id in self.filenames.keys():
353            # data with title
354            if self.filenames[key_id].get_data().title:
355                input = self.filenames[key_id].get_data().title
356            # data without title
357            else:
358                input = str(key_id)
359            if name in input:
360                return key_id
361
362    def _extractData(self, key_id):
363        """ Extract data from file with id contained in list of filenames """
364        data_complete = self.filenames[key_id].get_data()
365        dimension = data_complete.__class__.__name__
366
367        if dimension in ('Data1D', 'Data2D'):
368            return copy.deepcopy(data_complete)
369
370        else:
371            logging.info('Error with data format')
372            return
373
374    # ########
375    # PLOTS
376    # ########
377    def newPlot(self, graph, layout):
378        """ Create template for graphs with default '?' layout"""
379        assert isinstance(graph, QtGui.QGraphicsView)
380        assert isinstance(layout, QtGui.QHBoxLayout)
381
382        # clear layout
383        if layout.count() > 0:
384            item = layout.takeAt(0)
385            layout.removeItem(item)
386
387        layout.setContentsMargins(0, 0, 0, 0)
388        layout.addWidget(self.prepareSubgraphWithData("?"))
389
390        graph.setLayout(layout)
391
392    def updatePlot(self, graph, layout, data):
393        """ plot data in graph after clearing its layout """
394
395        assert isinstance(graph, QtGui.QGraphicsView)
396        assert isinstance(layout, QtGui.QHBoxLayout)
397
398        # clear layout
399        if layout.count() > 0:
400            item = layout.takeAt(0)
401            layout.removeItem(item)
402
403        layout.setContentsMargins(0, 0, 0, 0)
404
405        if isinstance(data, Data2D):
406            # plot 2D data
407            plotter2D = Plotter2DWidget(self, quickplot=True)
408            plotter2D.data = data
409            plotter2D.scale = 'linear'
410
411            plotter2D.ax.tick_params(axis='x', labelsize=8)
412            plotter2D.ax.tick_params(axis='y', labelsize=8)
413
414            # Draw zero axis lines.
415            plotter2D.ax.axhline(linewidth=1)
416            plotter2D.ax.axvline(linewidth=1)
417
418            graph.setLayout(layout)
419            layout.addWidget(plotter2D)
420            # remove x- and ylabels
421            plotter2D.y_label = ''
422            plotter2D.x_label = ''
423            plotter2D.plot(show_colorbar=False)
424            plotter2D.show()
425
426        elif isinstance(data, Data1D):
427            # plot 1D data
428            plotter = PlotterWidget(self, quickplot=True)
429            plotter.data = data
430
431            graph.setLayout(layout)
432            layout.addWidget(plotter)
433
434            plotter.ax.tick_params(axis='x', labelsize=8)
435            plotter.ax.tick_params(axis='y', labelsize=8)
436
437            plotter.plot(hide_error=True, marker='.')
438            # plotter.legend = None
439
440            plotter.show()
441
442        elif float(data) and self.cbData2.currentText() == 'Number':
443            # display value of coefficient (to be applied to Data1)
444            # in graphData2
445            layout.addWidget(self.prepareSubgraphWithData(data))
446
447            graph.setLayout(layout)
448
449    def prepareSubgraphWithData(self, data):
450        """ Create graphics view containing scene with string """
451        scene = QtGui.QGraphicsScene()
452        scene.addText(str(data))
453
454        subgraph = QtGui.QGraphicsView()
455        subgraph.setScene(scene)
456
457        return subgraph
Note: See TracBrowser for help on using the repository browser.