source: sasview/src/sas/qtgui/PlotterBase.py @ ef01be4

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

More context menu functionality in plots - SASVIEW-167

  • Property mode set to 100755
File size: 13.1 KB
Line 
1import logging
2import copy
3import numpy
4import pylab
5
6from PyQt4 import QtGui
7from PyQt4 import QtCore
8
9# TODO: Replace the qt4agg calls below with qt5 equivalent.
10# Requires some code modifications.
11# https://www.boxcontrol.net/embedding-matplotlib-plot-on-pyqt5-gui.html
12#
13from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
14from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
15from matplotlib.backend_bases import NavigationToolbar2
16
17import matplotlib.pyplot as plt
18
19DEFAULT_CMAP = pylab.cm.jet
20from sas.qtgui.ScaleProperties import ScaleProperties
21import sas.qtgui.PlotUtilities as PlotUtilities
22import sas.qtgui.PlotHelper as PlotHelper
23
24class PlotterBase(QtGui.QDialog):
25    def __init__(self, parent=None, quickplot=False):
26        super(PlotterBase, self).__init__(parent)
27
28        # Required for the communicator
29        self.parent = parent
30        self.quickplot = quickplot
31
32        # a figure instance to plot on
33        self.figure = plt.figure()
34
35        # this is the Canvas Widget that displays the `figure`
36        # it takes the `figure` instance as a parameter to __init__
37        self.canvas = FigureCanvas(self.figure)
38
39        # this is the Navigation widget
40        # it takes the Canvas widget and a parent
41        self.toolbar = NavigationToolbar(self.canvas, self)
42
43        self.properties = ScaleProperties(self)
44
45        # set the layout
46        layout = QtGui.QVBoxLayout()
47        layout.addWidget(self.canvas)
48
49        # defaults
50        self.current_plot = 111
51        self._data = []
52        self.qx_data = []
53        self.qy_data = []
54        self.color=0
55        self.symbol=0
56        self.grid_on = False
57        self.scale = 'linear'
58
59        # default color map
60        self.cmap = DEFAULT_CMAP
61
62        self.ax = self.figure.add_subplot(self.current_plot)
63        self.canvas.figure.set_facecolor('#FFFFFF')
64
65        if not quickplot:
66            layout.addWidget(self.toolbar)
67            # Notify the helper
68            PlotHelper.addPlot(self)
69            # Notify the listeners
70            self.parent.communicator.activeGraphsSignal.emit(PlotHelper.currentPlots())
71        else:
72            self.contextMenuQuickPlot()
73
74        self.setLayout(layout)
75
76    @property
77    def data(self):
78        return self._data
79
80    @data.setter
81    def data(self, data=None):
82        """ data setter """
83        pass
84
85    def title(self, title=""):
86        """ title setter """
87        self.title = title
88
89    def id(self, id=""):
90        """ id setter """
91        self.id = id
92
93    def xLabel(self, xlabel=""):
94        """ x-label setter """
95        self.x_label = r'$%s$'% xlabel
96
97    def yLabel(self, ylabel=""):
98        """ y-label setter """
99        self.y_label = r'$%s$'% ylabel
100
101    @property
102    def yscale(self):
103        """ Y-axis scale getter """
104        return self.yscale
105
106    @yscale.setter
107    def yscale(self, scale='linear'):
108        """ Y-axis scale setter """
109        self.subplot.set_yscale(scale, nonposy='clip')
110        self.yscale = scale
111
112    @property
113    def xscale(self):
114        """ X-axis scale getter """
115        return self.xscale
116
117    @xscale.setter
118    def xscale(self, scale='linear'):
119        """ X-axis scale setter """
120        self.subplot.set_xscale(scale)
121        self.xscale = scale
122
123    def contextMenuQuickPlot(self):
124        """
125        Define context menu and associated actions for the MPL widget
126        """
127        # Actions
128        self.contextMenu = QtGui.QMenu(self)
129        self.actionSaveImage = self.contextMenu.addAction("Save Image")
130        self.actionPrintImage = self.contextMenu.addAction("Print Image")
131        self.actionCopyToClipboard = self.contextMenu.addAction("Copy to Clipboard")
132        self.contextMenu.addSeparator()
133        self.actionToggleGrid = self.contextMenu.addAction("Toggle Grid On/Off")
134        self.contextMenu.addSeparator()
135        self.actionChangeScale = self.contextMenu.addAction("Change Scale")
136
137        # Define the callbacks
138        self.actionSaveImage.triggered.connect(self.onImageSave)
139        self.actionPrintImage.triggered.connect(self.onImagePrint)
140        self.actionCopyToClipboard.triggered.connect(self.onClipboardCopy)
141        self.actionToggleGrid.triggered.connect(self.onGridToggle)
142        self.actionChangeScale.triggered.connect(self.onScaleChange)
143
144    def contextMenuEvent(self, event):
145        """
146        Display the context menu
147        """
148        self.contextMenu.exec_( self.canvas.mapToGlobal(event.pos()) )
149
150    def clean(self):
151        """
152        Redraw the graph
153        """
154        self.figure.delaxes(self.ax)
155        self.ax = self.figure.add_subplot(self.current_plot)
156
157    def plot(self, marker=None, linestyle=None):
158        """
159        VIRTUAL
160        Plot the content of self._data
161        """
162        raise ImportError("Plot method must be implemented in derived class.")
163
164    def closeEvent(self, event):
165        """
166        Overwrite the close event adding helper notification
167        """
168        # Please remove me from your database.
169        PlotHelper.deletePlot(PlotHelper.idOfPlot(self))
170        # Notify the listeners
171        self.parent.communicator.activeGraphsSignal.emit(PlotHelper.currentPlots())
172        event.accept()
173
174    def onImageSave(self):
175        """
176        Use the internal MPL method for saving to file
177        """
178        self.toolbar.save_figure()
179
180    def onImagePrint(self):
181        """
182        Display printer dialog and print the MPL widget area
183        """
184        # Define the printer
185        printer = QtGui.QPrinter()
186
187        # Display the print dialog
188        dialog = QtGui.QPrintDialog(printer)
189        dialog.setModal(True)
190        dialog.setWindowTitle("Print")
191        if(dialog.exec_() != QtGui.QDialog.Accepted):
192            return
193
194        painter = QtGui.QPainter(printer)
195        # Create a label with pixmap drawn
196        pmap = QtGui.QPixmap.grabWidget(self)
197        printLabel = QtGui.QLabel()
198        printLabel.setPixmap(pmap)
199
200        # Print the label
201        printLabel.render(painter)
202        painter.end()
203
204    def onClipboardCopy(self):
205        """
206        Copy MPL widget area to buffer
207        """
208        bmp = QtGui.QApplication.clipboard()
209        pixmap = QtGui.QPixmap.grabWidget(self.canvas)
210        bmp.setPixmap(pixmap)
211
212    def onGridToggle(self):
213        """
214        Add/remove grid lines from MPL plot
215        """
216        self.grid_on = (not self.grid_on)
217        self.ax.grid(self.grid_on)
218        self.canvas.draw_idle()
219
220    def onScaleChange(self):
221        """
222        Show a dialog allowing axes rescaling
223        """
224        if self.properties.exec_() == QtGui.QDialog.Accepted:
225            xLabel, yLabel = self.properties.getValues()
226            #self.xyTransform(xLabel, yLabel)
227
228    def xyTransform(self, xLabel="", yLabel=""):
229        """
230        Transforms x and y in View and set the scale
231        """
232        # The logic should be in the right order
233        self.ly = None
234        self.q_ctrl = None
235        # Changing the scale might be incompatible with
236        # currently displayed data (for instance, going
237        # from ln to log when all plotted values have
238        # negative natural logs).
239        # Go linear and only change the scale at the end.
240        self.set_xscale("linear")
241        self.set_yscale("linear")
242        _xscale = 'linear'
243        _yscale = 'linear'
244        # Local data is either 1D or 2D
245        #for item in list:
246        #if item.id == 'fit':
247        #    continue
248        item.setLabel(self.xLabel, self.yLabel)
249        # control axis labels from the panel itself
250        yname, yunits = item.get_yaxis()
251        if self.yaxis_label != None:
252            yname = self.yaxis_label
253            yunits = self.yaxis_unit
254        else:
255            self.yaxis_label = yname
256            self.yaxis_unit = yunits
257        xname, xunits = item.get_xaxis()
258        if self.xaxis_label != None:
259            xname = self.xaxis_label
260            xunits = self.xaxis_unit
261        else:
262            self.xaxis_label = xname
263            self.xaxis_unit = xunits
264        # Goes through all possible scales
265        if self.xLabel == "x":
266            item.transformX(transform.toX, transform.errToX)
267            self.graph._xaxis_transformed("%s" % xname, "%s" % xunits)
268        if self.xLabel == "x^(2)":
269            item.transformX(transform.toX2, transform.errToX2)
270            xunits = convert_unit(2, xunits)
271            self.graph._xaxis_transformed("%s^{2}" % xname, "%s" % xunits)
272        if self.xLabel == "x^(4)":
273            item.transformX(transform.toX4, transform.errToX4)
274            xunits = convert_unit(4, xunits)
275            self.graph._xaxis_transformed("%s^{4}" % xname, "%s" % xunits)
276        if self.xLabel == "ln(x)":
277            item.transformX(transform.toLogX, transform.errToLogX)
278            self.graph._xaxis_transformed("\ln{(%s)}" % xname, "%s" % xunits)
279        if self.xLabel == "log10(x)":
280            item.transformX(transform.toX_pos, transform.errToX_pos)
281            _xscale = 'log'
282            self.graph._xaxis_transformed("%s" % xname, "%s" % xunits)
283        if self.xLabel == "log10(x^(4))":
284            item.transformX(transform.toX4, transform.errToX4)
285            xunits = convert_unit(4, xunits)
286            self.graph._xaxis_transformed("%s^{4}" % xname, "%s" % xunits)
287            _xscale = 'log'
288        if self.yLabel == "ln(y)":
289            item.transformY(transform.toLogX, transform.errToLogX)
290            self.graph._yaxis_transformed("\ln{(%s)}" % yname, "%s" % yunits)
291        if self.yLabel == "y":
292            item.transformY(transform.toX, transform.errToX)
293            self.graph._yaxis_transformed("%s" % yname, "%s" % yunits)
294        if self.yLabel == "log10(y)":
295            item.transformY(transform.toX_pos, transform.errToX_pos)
296            _yscale = 'log'
297            self.graph._yaxis_transformed("%s" % yname, "%s" % yunits)
298        if self.yLabel == "y^(2)":
299            item.transformY(transform.toX2, transform.errToX2)
300            yunits = convert_unit(2, yunits)
301            self.graph._yaxis_transformed("%s^{2}" % yname, "%s" % yunits)
302        if self.yLabel == "1/y":
303            item.transformY(transform.toOneOverX, transform.errOneOverX)
304            yunits = convert_unit(-1, yunits)
305            self.graph._yaxis_transformed("1/%s" % yname, "%s" % yunits)
306        if self.yLabel == "y*x^(2)":
307            item.transformY(transform.toYX2, transform.errToYX2)
308            xunits = convert_unit(2, self.xaxis_unit)
309            self.graph._yaxis_transformed("%s \ \ %s^{2}" % (yname, xname),
310                                            "%s%s" % (yunits, xunits))
311        if self.yLabel == "y*x^(4)":
312            item.transformY(transform.toYX4, transform.errToYX4)
313            xunits = convert_unit(4, self.xaxis_unit)
314            self.graph._yaxis_transformed("%s \ \ %s^{4}" % (yname, xname),
315                                            "%s%s" % (yunits, xunits))
316        if self.yLabel == "1/sqrt(y)":
317            item.transformY(transform.toOneOverSqrtX,
318                            transform.errOneOverSqrtX)
319            yunits = convert_unit(-0.5, yunits)
320            self.graph._yaxis_transformed("1/\sqrt{%s}" % yname,
321                                            "%s" % yunits)
322        if self.yLabel == "ln(y*x)":
323            item.transformY(transform.toLogXY, transform.errToLogXY)
324            self.graph._yaxis_transformed("\ln{(%s \ \ %s)}" % (yname, xname),
325                                            "%s%s" % (yunits, self.xaxis_unit))
326        if self.yLabel == "ln(y*x^(2))":
327            item.transformY(transform.toLogYX2, transform.errToLogYX2)
328            xunits = convert_unit(2, self.xaxis_unit)
329            self.graph._yaxis_transformed("\ln (%s \ \ %s^{2})" % (yname, xname),
330                                            "%s%s" % (yunits, xunits))
331        if self.yLabel == "ln(y*x^(4))":
332            item.transformY(transform.toLogYX4, transform.errToLogYX4)
333            xunits = convert_unit(4, self.xaxis_unit)
334            self.graph._yaxis_transformed("\ln (%s \ \ %s^{4})" % (yname, xname),
335                                            "%s%s" % (yunits, xunits))
336        if self.yLabel == "log10(y*x^(4))":
337            item.transformY(transform.toYX4, transform.errToYX4)
338            xunits = convert_unit(4, self.xaxis_unit)
339            _yscale = 'log'
340            self.graph._yaxis_transformed("%s \ \ %s^{4}" % (yname, xname),
341                                            "%s%s" % (yunits, xunits))
342            item.transformView()
343
344        # set new label and units
345        yname = self.graph.prop["ylabel"]
346        yunits = ''
347        xname = self.graph.prop["xlabel"]
348        xunits = ''
349
350        self.resetFitView()
351        self.prevXtrans = self.xLabel
352        self.prevYtrans = self.yLabel
353        self.graph.render(self)
354        self.set_xscale(_xscale)
355        self.set_yscale(_yscale)
356
357        self.xaxis(xname, xunits, self.xaxis_font,
358                   self.xaxis_color, self.xaxis_tick)
359        self.yaxis(yname, yunits, self.yaxis_font,
360                   self.yaxis_color, self.yaxis_tick)
361        self.subplot.texts = self.textList
362
363        self.canvas.draw_idle()
Note: See TracBrowser for help on using the repository browser.