source: sasview/src/sas/qtgui/Plotter2D.py @ 5d89f43

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

Code review for ColorMap?

  • Property mode set to 100644
File size: 11.2 KB
Line 
1import copy
2import numpy
3import pylab
4import functools
5
6from PyQt4 import QtGui
7from PyQt4 import QtCore
8
9DEFAULT_CMAP = pylab.cm.jet
10from mpl_toolkits.mplot3d import Axes3D
11
12import sas.qtgui.PlotUtilities as PlotUtilities
13from sas.qtgui.PlotterBase import PlotterBase
14from sas.qtgui.ColorMap import ColorMap
15from sas.sasgui.guiframe.dataFitting import Data2D
16
17# Minimum value of Z for which we will present data.
18MIN_Z=-32
19
20class Plotter2DWidget(PlotterBase):
21    """
22    2D Plot widget for use with a QDialog
23    """
24    def __init__(self, parent=None, manager=None, quickplot=False, dimension=2):
25        self.dimension = dimension
26        super(Plotter2DWidget, self).__init__(parent, manager=manager, quickplot=quickplot)
27
28        self.cmap = DEFAULT_CMAP.name
29        # Default scale
30        self.scale = 'log_{10}'
31        self.vmin = None
32        self.vmax = None
33
34    @property
35    def data(self):
36        return self._data
37
38    @data.setter
39    def data(self, data=None):
40        """ data setter """
41        self._data = data
42        self.qx_data = data.qx_data
43        self.qy_data = data.qy_data
44        self.xmin = data.xmin
45        self.xmax = data.xmax
46        self.ymin = data.ymin
47        self.ymax = data.ymax
48        self.zmin = data.zmin
49        self.zmax = data.zmax
50        self.label = data.name
51        self.xLabel = "%s(%s)"%(data._xaxis, data._xunit)
52        self.yLabel = "%s(%s)"%(data._yaxis, data._yunit)
53        self.title(title=data.title)
54
55    def plot(self, data=None, marker=None, linestyle=None):
56        """
57        Plot 2D self._data
58        """
59        # Assing data
60        if isinstance(data, Data2D):
61            self.data = data
62
63        assert(self._data)
64
65        # Toggle the scale
66        zmin_2D_temp, zmax_2D_temp = self.calculateDepth()
67
68        # Prepare and show the plot
69        self.showPlot(data=self.data.data,
70                      qx_data=self.qx_data,
71                      qy_data=self.qy_data,
72                      xmin=self.xmin,
73                      xmax=self.xmax,
74                      ymin=self.ymin, ymax=self.ymax,
75                      cmap=self.cmap, zmin=zmin_2D_temp,
76                      zmax=zmax_2D_temp)
77
78    def calculateDepth(self):
79        """
80        Re-calculate the plot depth parameters depending on the scale
81        """
82        # Toggle the scale
83        zmin_temp = self.zmin
84        zmax_temp = self.zmax
85        # self.scale predefined in the baseclass
86        if self.scale == 'log_{10}':
87            if self.zmin is not None:
88                zmin_temp = numpy.power(10, self.zmin)
89            if self.zmax is not None:
90                zmax_temp = numpy.power(10, self.zmax)
91        else:
92            if self.zmin is not None:
93                # min log value: no log(negative)
94                zmin_temp = MIN_Z if self.zmin <= 0 else numpy.log10(self.zmin)
95            if self.zmax is not None:
96                zmax_temp = numpy.log10(self.zmax)
97
98        return (zmin_temp, zmax_temp)
99
100
101    def createContextMenu(self):
102        """
103        Define common context menu and associated actions for the MPL widget
104        """
105        self.defaultContextMenu()
106
107        self.contextMenu.addSeparator()
108        self.actionDataInfo = self.contextMenu.addAction("&DataInfo")
109        self.actionDataInfo.triggered.connect(
110                              functools.partial(self.onDataInfo, self.data))
111
112        self.actionSavePointsAsFile = self.contextMenu.addAction("&Save Points as a File")
113        self.actionSavePointsAsFile.triggered.connect(
114                                functools.partial(self.onSavePoints, self.data))
115        self.contextMenu.addSeparator()
116
117        self.actionCircularAverage = self.contextMenu.addAction("&Perform Circular Average")
118        self.actionCircularAverage.triggered.connect(self.onCircularAverage)
119
120        self.actionSectorView = self.contextMenu.addAction("&Sector [Q View]")
121        self.actionSectorView.triggered.connect(self.onSectorView)
122        self.actionAnnulusView = self.contextMenu.addAction("&Annulus [Phi View]")
123        self.actionAnnulusView.triggered.connect(self.onAnnulusView)
124        self.actionBoxSum = self.contextMenu.addAction("&Box Sum")
125        self.actionBoxSum.triggered.connect(self.onBoxSum)
126        self.actionBoxAveragingX = self.contextMenu.addAction("&Box Averaging in Qx")
127        self.actionBoxAveragingX.triggered.connect(self.onBoxAveragingX)
128        self.actionBoxAveragingY = self.contextMenu.addAction("&Box Averaging in Qy")
129        self.actionBoxAveragingY.triggered.connect(self.onBoxAveragingY)
130        self.contextMenu.addSeparator()
131        self.actionEditGraphLabel = self.contextMenu.addAction("&Edit Graph Label")
132        self.actionEditGraphLabel.triggered.connect(self.onEditgraphLabel)
133        self.contextMenu.addSeparator()
134        self.actionColorMap = self.contextMenu.addAction("&2D Color Map")
135        self.actionColorMap.triggered.connect(self.onColorMap)
136        self.contextMenu.addSeparator()
137        self.actionChangeScale = self.contextMenu.addAction("Toggle Linear/Log Scale")
138        self.actionChangeScale.triggered.connect(self.onToggleScale)
139
140    def createContextMenuQuick(self):
141        """
142        Define context menu and associated actions for the quickplot MPL widget
143        """
144        self.defaultContextMenu()
145
146        if self.dimension == 2:
147            self.actionToggleGrid = self.contextMenu.addAction("Toggle Grid On/Off")
148            self.contextMenu.addSeparator()
149        self.actionChangeScale = self.contextMenu.addAction("Toggle Linear/Log Scale")
150
151        # Define the callbacks
152        self.actionChangeScale.triggered.connect(self.onToggleScale)
153        if self.dimension == 2:
154            self.actionToggleGrid.triggered.connect(self.onGridToggle)
155
156    def onToggleScale(self, event):
157        """
158        Toggle axis and replot image
159        """
160        # self.scale predefined in the baseclass
161        if self.scale == 'log_{10}':
162            self.scale = 'linear'
163        else:
164            self.scale = 'log_{10}'
165
166        self.plot()
167
168    def onCircularAverage(self):
169        """
170        """
171        pass
172
173    def onSectorView(self):
174        """
175        """
176        pass
177
178    def onAnnulusView(self):
179        """
180        """
181        pass
182
183    def onBoxSum(self):
184        """
185        """
186        pass
187
188    def onBoxAveragingX(self):
189        """
190        """
191        pass
192
193    def onBoxAveragingY(self):
194        """
195        """
196        pass
197
198    def onEditgraphLabel(self):
199        """
200        """
201        pass
202
203    def onColorMap(self):
204        """
205        Display the color map dialog and modify the plot's map accordingly
206        """
207        color_map_dialog = ColorMap(self, cmap=self.cmap,
208                                    vmin=self.vmin,
209                                    vmax=self.vmax,
210                                    data=self.data)
211
212        color_map_dialog.apply_signal.connect(self.onApplyMap)
213
214        if color_map_dialog.exec_() == QtGui.QDialog.Accepted:
215            self.onApplyMap(color_map_dialog.norm(), color_map_dialog.cmap())
216
217    def onApplyMap(self, v_values, cmap):
218        """
219        Update the chart color map based on data passed from the widget
220        """
221        self.cmap = str(cmap)
222        self.vmin, self.vmax = v_values
223        # Redraw the chart with new cmap
224        self.plot()
225
226    def showPlot(self, data, qx_data, qy_data, xmin, xmax, ymin, ymax,
227                 zmin, zmax, color=0, symbol=0, markersize=0,
228                 label='data2D', cmap=DEFAULT_CMAP):
229        """
230        Render and show the current data
231        """
232        self.qx_data = qx_data
233        self.qy_data = qy_data
234        self.xmin = xmin
235        self.xmax = xmax
236        self.ymin = ymin
237        self.ymax = ymax
238        self.zmin = zmin
239        self.zmax = zmax
240        # If we don't have any data, skip.
241        if data is None:
242            return
243        if data.ndim == 1:
244            output = PlotUtilities.build_matrix(data, self.qx_data, self.qy_data)
245        else:
246            output = copy.deepcopy(data)
247
248        zmin_temp = self.zmin
249        # check scale
250        if self.scale == 'log_{10}':
251            try:
252                if  self.zmin <= 0  and len(output[output > 0]) > 0:
253                    zmin_temp = self.zmin
254                    output[output > 0] = numpy.log10(output[output > 0])
255                elif self.zmin <= 0:
256                    zmin_temp = self.zmin
257                    output[output > 0] = numpy.zeros(len(output))
258                    output[output <= 0] = MIN_Z
259                else:
260                    zmin_temp = self.zmin
261                    output[output > 0] = numpy.log10(output[output > 0])
262            except:
263                #Too many problems in 2D plot with scale
264                output[output > 0] = numpy.log10(output[output > 0])
265                pass
266
267        self.cmap = cmap
268        if self.dimension != 3:
269            #Re-adjust colorbar
270            self.figure.subplots_adjust(left=0.2, right=.8, bottom=.2)
271
272            zmax_temp = self.zmax
273            if self.vmin is not None:
274                zmin_temp = self.vmin
275                zmax_temp = self.vmax
276
277            im = self.ax.imshow(output, interpolation='nearest',
278                                origin='lower',
279                                vmin=zmin_temp, vmax=zmax_temp,
280                                cmap=self.cmap,
281                                extent=(self.xmin, self.xmax,
282                                        self.ymin, self.ymax))
283
284            cbax = self.figure.add_axes([0.84, 0.2, 0.02, 0.7])
285
286            # Current labels for axes
287            self.ax.set_ylabel(self.y_label)
288            self.ax.set_xlabel(self.x_label)
289
290            # Title only for regular charts
291            if not self.quickplot:
292                self.ax.set_title(label=self._title)
293
294            if cbax is None:
295                ax.set_frame_on(False)
296                cb = self.figure.colorbar(im, shrink=0.8, aspect=20)
297            else:
298                cb = self.figure.colorbar(im, cax=cbax)
299
300            cb.update_bruteforce(im)
301            cb.set_label('$' + self.scale + '$')
302
303            self.vmin = cb.vmin
304            self.vmax = cb.vmax
305
306        else:
307            # clear the previous 2D from memory
308            self.figure.clear()
309
310            self.figure.subplots_adjust(left=0.1, right=.8, bottom=.1)
311
312            X = self._data.x_bins[0:-1]
313            Y = self._data.y_bins[0:-1]
314            X, Y = numpy.meshgrid(X, Y)
315
316            ax = Axes3D(self.figure)
317
318            # Disable rotation for large sets.
319            # TODO: Define "large" for a dataset
320            SET_TOO_LARGE = 500
321            if len(X) > SET_TOO_LARGE:
322                ax.disable_mouse_rotation()
323
324            self.figure.canvas.resizing = False
325            im = ax.plot_surface(X, Y, output, rstride=1, cstride=1, cmap=cmap,
326                                 linewidth=0, antialiased=False)
327            self.ax.set_axis_off()
328
329        if self.dimension != 3:
330            self.figure.canvas.draw_idle()
331        else:
332            self.figure.canvas.draw()
333
334class Plotter2D(QtGui.QDialog, Plotter2DWidget):
335    def __init__(self, parent=None, quickplot=False, dimension=2):
336
337        QtGui.QDialog.__init__(self)
338        Plotter2DWidget.__init__(self, manager=parent, quickplot=quickplot, dimension=dimension)
339        icon = QtGui.QIcon()
340        icon.addPixmap(QtGui.QPixmap(":/res/ball.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
341        self.setWindowIcon(icon)
Note: See TracBrowser for help on using the repository browser.