source: sasview/src/sas/qtgui/Plotter.py @ 9290b1a

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

Added AddText? to plot, enabled legend drag - SASVIEW-378

  • Property mode set to 100644
File size: 12.4 KB
Line 
1from PyQt4 import QtGui
2
3import matplotlib.pyplot as plt
4from matplotlib.font_manager import FontProperties
5
6from sas.sasgui.guiframe.dataFitting import Data1D
7from sas.sasgui.plottools import transform
8from sas.sasgui.plottools.convert_units import convert_unit
9from sas.qtgui.PlotterBase import PlotterBase
10from sas.qtgui.AddText import AddText
11
12class PlotterWidget(PlotterBase):
13    """
14    1D Plot widget for use with a QDialog
15    """
16    def __init__(self, parent=None, manager=None, quickplot=False):
17        super(PlotterWidget, self).__init__(parent, manager=manager, quickplot=quickplot)
18        self.parent = parent
19
20    @property
21    def data(self):
22        return self._data
23
24    @data.setter
25    def data(self, value):
26        """ data setter """
27        self._data = value
28        self.xLabel = "%s(%s)"%(value._xaxis, value._xunit)
29        self.yLabel = "%s(%s)"%(value._yaxis, value._yunit)
30        self.title(title=value.title)
31
32    def plot(self, data=None, marker=None, linestyle=None, hide_error=False):
33        """
34        Plot self._data
35        """
36        # Data1D
37        if isinstance(data, Data1D):
38            self.data = data
39        assert(self._data)
40
41        # Shortcut for an axis
42        ax = self.ax
43
44        if marker == None:
45            marker = 'o'
46
47        if linestyle == None:
48            linestyle = ''
49
50        # plot data with/without errorbars
51        if hide_error:
52            ax.plot(self._data.view.x, self._data.view.y,
53                    marker=marker,
54                    linestyle=linestyle,
55                    label=self._title,
56                    picker=True)
57        else:
58            ax.errorbar(self._data.view.x, self._data.view.y,
59                        yerr=self._data.view.dx, xerr=None,
60                        capsize=2, linestyle='',
61                        barsabove=False,
62                        marker=marker,
63                        lolims=False, uplims=False,
64                        xlolims=False, xuplims=False,
65                        label=self._title,
66                        picker=True)
67
68        # Now add the legend with some customizations.
69        self.legend = ax.legend(loc='upper right', shadow=True)
70        #self.legend.get_frame().set_alpha(0.4)
71        self.legend.set_picker(True)
72
73        # Current labels for axes
74        ax.set_ylabel(self.y_label)
75        ax.set_xlabel(self.x_label)
76
77        # Title only for regular charts
78        if not self.quickplot:
79            ax.set_title(label=self._title)
80
81        # Include scaling (log vs. linear)
82        ax.set_xscale(self.xscale)
83        ax.set_yscale(self.yscale)
84
85        # refresh canvas
86        self.canvas.draw()
87
88    def contextMenu(self):
89        """
90        Define common context menu and associated actions for the MPL widget
91        """
92        self.defaultContextMenu()
93
94        # Additional menu items
95        self.contextMenu.addSeparator()
96        self.actionModifyGraphAppearance =\
97            self.contextMenu.addAction("Modify Graph Appearance")
98        self.contextMenu.addSeparator()
99        self.actionAddText = self.contextMenu.addAction("Add Text")
100        self.actionRemoveText = self.contextMenu.addAction("Remove Text")
101        self.contextMenu.addSeparator()
102        self.actionChangeScale = self.contextMenu.addAction("Change Scale")
103        self.contextMenu.addSeparator()
104        self.actionSetGraphRange = self.contextMenu.addAction("Set Graph Range")
105        self.actionResetGraphRange =\
106            self.contextMenu.addAction("Reset Graph Range")
107        # Add the title change for dialogs
108        if self.parent:
109            self.contextMenu.addSeparator()
110            self.actionWindowTitle = self.contextMenu.addAction("Window Title")
111
112        # Define the callbacks
113        self.actionModifyGraphAppearance.triggered.connect(self.onModifyGraph)
114        self.actionAddText.triggered.connect(self.onAddText)
115        self.actionRemoveText.triggered.connect(self.onRemoveText)
116        self.actionChangeScale.triggered.connect(self.onScaleChange)
117        self.actionSetGraphRange.triggered.connect(self.onSetGraphRange)
118        self.actionResetGraphRange.triggered.connect(self.onResetGraphRange)
119        self.actionWindowTitle.triggered.connect(self.onWindowsTitle)
120
121    def contextMenuQuickPlot(self):
122        """
123        Define context menu and associated actions for the quickplot MPL widget
124        """
125        # Default actions
126        self.defaultContextMenu()
127
128        # Additional actions
129        self.actionToggleGrid = self.contextMenu.addAction("Toggle Grid On/Off")
130        self.contextMenu.addSeparator()
131        self.actionChangeScale = self.contextMenu.addAction("Change Scale")
132
133        # Define the callbacks
134        self.actionToggleGrid.triggered.connect(self.onGridToggle)
135        self.actionChangeScale.triggered.connect(self.onScaleChange)
136
137    def onScaleChange(self):
138        """
139        Show a dialog allowing axes rescaling
140        """
141        if self.properties.exec_() == QtGui.QDialog.Accepted:
142            xLabel, yLabel = self.properties.getValues()
143            self.xyTransform(xLabel, yLabel)
144
145    def onModifyGraph(self):
146        """
147        Show a dialog allowing chart manipulations
148        """
149        print ("onModifyGraph")
150        pass
151
152    def onAddText(self):
153        """
154        Show a dialog allowing adding custom text to the chart
155        """
156        self.addText = AddText(self)
157        if self.addText.exec_() == QtGui.QDialog.Accepted:
158            # Retrieve the new text, its font and color
159            extra_text = self.addText.text()
160            extra_font = self.addText.font()
161            extra_color = self.addText.color()
162
163            # Place the text on the screen at (0,0)
164            pos_x = self.x_click
165            pos_y = self.y_click
166
167            # Map QFont onto MPL font
168            mpl_font = FontProperties()
169            mpl_font.set_size(int(extra_font.pointSize()))
170            mpl_font.set_family(str(extra_font.family()))
171            mpl_font.set_weight(int(extra_font.weight()))
172            # MPL style names
173            styles = ['normal', 'italic', 'oblique']
174            # QFont::Style maps directly onto the above
175            try:
176                mpl_font.set_style(styles[extra_font.style()])
177            except:
178                pass
179
180            if len(extra_text) > 0:
181                new_text = self.ax.text(str(pos_x),
182                                        str(pos_y),
183                                        extra_text,
184                                        color=extra_color,
185                                        fontproperties=mpl_font)
186                # Update the list of annotations
187                self.textList.append(new_text)
188                self.canvas.draw_idle()
189
190    def onRemoveText(self):
191        """
192        Remove the most recently added text
193        """
194        num_text = len(self.textList)
195        if num_text < 1:
196            return
197        txt = self.textList[num_text - 1]
198        text_remove = txt.get_text()
199        txt.remove()
200        self.textList.remove(txt)
201
202        self.canvas.draw_idle()
203
204    def onSetGraphRange(self):
205        """
206        Show a dialog allowing setting the chart ranges
207        """
208        print("onSetGraphRange")
209        pass
210
211    def onResetGraphRange(self):
212        """
213        Resets the chart X and Y ranges to the original values
214        """
215        print("onResetGraphRange")
216        pass
217
218    def xyTransform(self, xLabel="", yLabel=""):
219        """
220        Transforms x and y in View and set the scale
221        """
222        # Clear the plot first
223        plt.cla()
224        self.ax.cla()
225
226        # Changing the scale might be incompatible with
227        # currently displayed data (for instance, going
228        # from ln to log when all plotted values have
229        # negative natural logs).
230        # Go linear and only change the scale at the end.
231        self._xscale = "linear"
232        self._yscale = "linear"
233        _xscale = 'linear'
234        _yscale = 'linear'
235        # Local data is either 1D or 2D
236        if self.data.id == 'fit':
237            return
238
239        # control axis labels from the panel itself
240        yname, yunits = self.data.get_yaxis()
241        xname, xunits = self.data.get_xaxis()
242
243        # Goes through all possible scales
244        # self.x_label is already wrapped with Latex "$", so using the argument
245
246        # X
247        if xLabel == "x":
248            self.data.transformX(transform.toX, transform.errToX)
249            self.xLabel = "%s(%s)" % (xname, xunits)
250        if xLabel == "x^(2)":
251            self.data.transformX(transform.toX2, transform.errToX2)
252            xunits = convert_unit(2, xunits)
253            self.xLabel = "%s^{2}(%s)" % (xname, xunits)
254        if xLabel == "x^(4)":
255            self.data.transformX(transform.toX4, transform.errToX4)
256            xunits = convert_unit(4, xunits)
257            self.xLabel = "%s^{4}(%s)" % (xname, xunits)
258        if xLabel == "ln(x)":
259            self.data.transformX(transform.toLogX, transform.errToLogX)
260            self.xLabel = "\ln{(%s)}(%s)" % (xname, xunits)
261        if xLabel == "log10(x)":
262            self.data.transformX(transform.toX_pos, transform.errToX_pos)
263            _xscale = 'log'
264            self.xLabel = "%s(%s)" % (xname, xunits)
265        if xLabel == "log10(x^(4))":
266            self.data.transformX(transform.toX4, transform.errToX4)
267            xunits = convert_unit(4, xunits)
268            self.xLabel = "%s^{4}(%s)" % (xname, xunits)
269            _xscale = 'log'
270
271        # Y
272        if yLabel == "ln(y)":
273            self.data.transformY(transform.toLogX, transform.errToLogX)
274            self.yLabel = "\ln{(%s)}(%s)" % (yname, yunits)
275        if yLabel == "y":
276            self.data.transformY(transform.toX, transform.errToX)
277            self.yLabel = "%s(%s)" % (yname, yunits)
278        if yLabel == "log10(y)":
279            self.data.transformY(transform.toX_pos, transform.errToX_pos)
280            _yscale = 'log'
281            self.yLabel = "%s(%s)" % (yname, yunits)
282        if yLabel == "y^(2)":
283            self.data.transformY(transform.toX2, transform.errToX2)
284            yunits = convert_unit(2, yunits)
285            self.yLabel = "%s^{2}(%s)" % (yname, yunits)
286        if yLabel == "1/y":
287            self.data.transformY(transform.toOneOverX, transform.errOneOverX)
288            yunits = convert_unit(-1, yunits)
289            self.yLabel = "1/%s(%s)" % (yname, yunits)
290        if yLabel == "y*x^(2)":
291            self.data.transformY(transform.toYX2, transform.errToYX2)
292            xunits = convert_unit(2, xunits)
293            self.yLabel = "%s \ \ %s^{2}(%s%s)" % (yname, xname, yunits, xunits)
294        if yLabel == "y*x^(4)":
295            self.data.transformY(transform.toYX4, transform.errToYX4)
296            xunits = convert_unit(4, xunits)
297            self.yLabel = "%s \ \ %s^{4}(%s%s)" % (yname, xname, yunits, xunits)
298        if yLabel == "1/sqrt(y)":
299            self.data.transformY(transform.toOneOverSqrtX,
300                                 transform.errOneOverSqrtX)
301            yunits = convert_unit(-0.5, yunits)
302            self.yLabel = "1/\sqrt{%s}(%s)" % (yname, yunits)
303        if yLabel == "ln(y*x)":
304            self.data.transformY(transform.toLogXY, transform.errToLogXY)
305            self.yLabel = "\ln{(%s \ \ %s)}(%s%s)" % (yname, xname, yunits, xunits)
306        if yLabel == "ln(y*x^(2))":
307            self.data.transformY(transform.toLogYX2, transform.errToLogYX2)
308            xunits = convert_unit(2, xunits)
309            self.yLabel = "\ln (%s \ \ %s^{2})(%s%s)" % (yname, xname, yunits, xunits)
310        if yLabel == "ln(y*x^(4))":
311            self.data.transformY(transform.toLogYX4, transform.errToLogYX4)
312            xunits = convert_unit(4, xunits)
313            self.yLabel = "\ln (%s \ \ %s^{4})(%s%s)" % (yname, xname, yunits, xunits)
314        if yLabel == "log10(y*x^(4))":
315            self.data.transformY(transform.toYX4, transform.errToYX4)
316            xunits = convert_unit(4, xunits)
317            _yscale = 'log'
318            self.yLabel = "%s \ \ %s^{4}(%s%s)" % (yname, xname, yunits, xunits)
319
320        # Perform the transformation of data in data1d->View
321        self.data.transformView()
322
323        self.xscale = _xscale
324        self.yscale = _yscale
325
326        # Plot the updated chart
327        self.plot(marker='o', linestyle='')
328
329
330class Plotter(QtGui.QDialog, PlotterWidget):
331    def __init__(self, parent=None, quickplot=False):
332
333        QtGui.QDialog.__init__(self)
334        PlotterWidget.__init__(self, manager=parent, quickplot=quickplot)
335        icon = QtGui.QIcon()
336        icon.addPixmap(QtGui.QPixmap(":/res/ball.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
337        self.setWindowIcon(icon)
338
339
Note: See TracBrowser for help on using the repository browser.