source: sasview/src/sas/qtgui/Plotter.py @ d3ca363

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

Setting graph range - SASVIEW-381

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