source: sasview/src/sas/qtgui/Plotter2D.py @ 092a3d9

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

Color Map control for 2D charts. Initial commit - SASVIEW-391

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