source: sasview/src/sas/qtgui/Plotting/Plotter.py @ e0ed8a8

Last change on this file since e0ed8a8 was e0ed8a8, checked in by Adam Washington <adam.washington@…>, 7 years ago

Draw Sesans data in linear coordinates

  • Property mode set to 100644
File size: 24.4 KB
RevLine 
[8cb6cd6]1from PyQt4 import QtGui
[aadf0af1]2from PyQt4 import QtCore
3import functools
4import copy
[8cb6cd6]5import matplotlib.pyplot as plt
[9290b1a]6from matplotlib.font_manager import FontProperties
[dc5ef15]7from sas.qtgui.Plotting.PlotterData import Data1D
[83eb5208]8from sas.qtgui.Plotting.PlotterBase import PlotterBase
9from sas.qtgui.Plotting.AddText import AddText
10from sas.qtgui.Plotting.SetGraphRange import SetGraphRange
11from sas.qtgui.Plotting.LinearFit import LinearFit
12from sas.qtgui.Plotting.PlotProperties import PlotProperties
[dc5ef15]13
14import sas.qtgui.Utilities.GuiUtils as GuiUtils
[83eb5208]15import sas.qtgui.Plotting.PlotUtilities as PlotUtilities
[8cb6cd6]16
[416fa8f]17class PlotterWidget(PlotterBase):
[c4e5400]18    """
19    1D Plot widget for use with a QDialog
[fecfe28]20    """
[416fa8f]21    def __init__(self, parent=None, manager=None, quickplot=False):
22        super(PlotterWidget, self).__init__(parent, manager=manager, quickplot=quickplot)
[570a58f9]23
[27313b7]24        self.parent = parent
[8cb6cd6]25
[aadf0af1]26        # Dictionary of {plot_id:Data1d}
27        self.plot_dict = {}
28
[fed94a2]29        # Window for text add
30        self.addText = AddText(self)
[aadf0af1]31
[fed94a2]32        # Log-ness of the axes
[570a58f9]33        self.xLogLabel = "log10(x)"
34        self.yLogLabel = "log10(y)"
35
36        # Data container for the linear fit
[fed94a2]37        self.fit_result = Data1D(x=[], y=[], dy=None)
38        self.fit_result.symbol = 13
39        self.fit_result.name = "Fit"
[570a58f9]40
41        # Add a slot for receiving update signal from LinearFit
[fed94a2]42        # NEW style signals
[570a58f9]43        #self.updatePlot = QtCore.pyqtSignal(tuple)
[fed94a2]44        # self.updatePlot.connect(self.onFitDisplay)
45        # OLD style signals
[570a58f9]46        QtCore.QObject.connect(self, QtCore.SIGNAL('updatePlot'), self.onFitDisplay)
47
[31c5b58]48    @property
49    def data(self):
50        return self._data
51
52    @data.setter
53    def data(self, value):
[8cb6cd6]54        """ data setter """
[31c5b58]55        self._data = value
[6d05e1d]56        self.xLabel = "%s(%s)"%(value._xaxis, value._xunit)
57        self.yLabel = "%s(%s)"%(value._yaxis, value._yunit)
[e0ed8a8]58        if value.isSesans:
59            self.xscale = 'linear'
60            self.yscale = 'linear'
[aadf0af1]61        self.title(title=value.name)
[8cb6cd6]62
[f0bb711]63    def plot(self, data=None, color=None, marker=None, hide_error=False):
[8cb6cd6]64        """
[aadf0af1]65        Add a new plot of self._data to the chart.
[8cb6cd6]66        """
[9290b1a]67        # Data1D
68        if isinstance(data, Data1D):
69            self.data = data
70        assert(self._data)
71
[87cc73a]72        is_fit = (self.data.id=="fit")
[570a58f9]73
[f182f93]74        # Transform data if required.
75        # TODO: it properly!
76        #if data.xtransform is not None or data.ytransform is not None:
77        #    a, b, c, d = GuiUtils.xyTransform(self.data, self.data.xtransform, self.data.ytransform)
78
[87cc73a]79        # Shortcuts
[ef01be4]80        ax = self.ax
[87cc73a]81        x = self._data.view.x
82        y = self._data.view.y
83
84        # Marker symbol. Passed marker is one of matplotlib.markers characters
85        # Alternatively, picked up from Data1D as an int index of PlotUtilities.SHAPES dict
86        if marker is None:
87            marker = self.data.symbol
[6fd4e36]88            # Try name first
89            try:
90                marker = PlotUtilities.SHAPES[marker]
91            except KeyError:
92                marker = PlotUtilities.SHAPES.values()[marker]
[87cc73a]93
[6fd4e36]94        assert marker is not None
[87cc73a]95        # Plot name
[b789967]96        if self.data.title:
97            self.title(title=self.data.title)
98        else:
99            self.title(title=self.data.name)
[87cc73a]100
101        # Error marker toggle
102        if hide_error is None:
103            hide_error = self.data.hide_error
104
105        # Plot color
106        if color is None:
107            color = self.data.custom_color
108
109        color = PlotUtilities.getValidColor(color)
110
111        markersize = self._data.markersize
112
[239214f]113        # Draw non-standard markers
114        l_width = markersize * 0.4
115        if marker == '-' or marker == '--':
116            line = self.ax.plot(x, y, color=color, lw=l_width, marker='',
117                             linestyle=marker, label=self._title, zorder=10)[0]
118
119        elif marker == 'vline':
120            y_min = min(y)*9.0/10.0 if min(y) < 0 else 0.0
121            line = self.ax.vlines(x=x, ymin=y_min, ymax=y, color=color,
122                            linestyle='-', label=self._title, lw=l_width, zorder=1)
123
124        elif marker == 'step':
125            line = self.ax.step(x, y, color=color, marker='', linestyle='-',
126                                label=self._title, lw=l_width, zorder=1)[0]
[8cb6cd6]127
[c4e5400]128        else:
[87cc73a]129            # plot data with/without errorbars
130            if hide_error:
131                line = ax.plot(x, y, marker=marker, color=color, markersize=markersize,
132                        linestyle='', label=self._title, picker=True)
133            else:
134                line = ax.errorbar(x, y,
135                            yerr=self._data.view.dy, xerr=None,
136                            capsize=2, linestyle='',
137                            barsabove=False,
138                            color=color,
139                            marker=marker,
140                            markersize=markersize,
141                            lolims=False, uplims=False,
142                            xlolims=False, xuplims=False,
143                            label=self._title,
144                            picker=True)
[8cb6cd6]145
[aadf0af1]146        # Update the list of data sets (plots) in chart
147        self.plot_dict[self._data.id] = self.data
148
[8cb6cd6]149        # Now add the legend with some customizations.
[f0bb711]150
151        self.legend = ax.legend(loc='upper right', shadow=True)
152        if self.legend:
153            self.legend.set_picker(True)
[8cb6cd6]154
[6d05e1d]155        # Current labels for axes
[570a58f9]156        if self.y_label and not is_fit:
157            ax.set_ylabel(self.y_label)
158        if self.x_label and not is_fit:
159            ax.set_xlabel(self.x_label)
[6d05e1d]160
161        # Include scaling (log vs. linear)
162        ax.set_xscale(self.xscale)
[3b7b218]163        ax.set_yscale(self.yscale)
[8cb6cd6]164
[257bd57]165        # define the ranges
166        self.setRange = SetGraphRange(parent=self,
167            x_range=self.ax.get_xlim(), y_range=self.ax.get_ylim())
168
[8cb6cd6]169        # refresh canvas
170        self.canvas.draw()
171
[aadf0af1]172    def createContextMenu(self):
[c4e5400]173        """
174        Define common context menu and associated actions for the MPL widget
175        """
176        self.defaultContextMenu()
177
[aadf0af1]178        # Separate plots
179        self.addPlotsToContextMenu()
180
[27313b7]181        # Additional menu items
182        self.contextMenu.addSeparator()
183        self.actionAddText = self.contextMenu.addAction("Add Text")
184        self.actionRemoveText = self.contextMenu.addAction("Remove Text")
185        self.contextMenu.addSeparator()
186        self.actionChangeScale = self.contextMenu.addAction("Change Scale")
187        self.contextMenu.addSeparator()
188        self.actionSetGraphRange = self.contextMenu.addAction("Set Graph Range")
189        self.actionResetGraphRange =\
190            self.contextMenu.addAction("Reset Graph Range")
191        # Add the title change for dialogs
[aadf0af1]192        #if self.parent:
193        self.contextMenu.addSeparator()
194        self.actionWindowTitle = self.contextMenu.addAction("Window Title")
[27313b7]195
196        # Define the callbacks
197        self.actionAddText.triggered.connect(self.onAddText)
198        self.actionRemoveText.triggered.connect(self.onRemoveText)
199        self.actionChangeScale.triggered.connect(self.onScaleChange)
200        self.actionSetGraphRange.triggered.connect(self.onSetGraphRange)
201        self.actionResetGraphRange.triggered.connect(self.onResetGraphRange)
202        self.actionWindowTitle.triggered.connect(self.onWindowsTitle)
[c4e5400]203
[aadf0af1]204    def addPlotsToContextMenu(self):
205        """
206        Adds operations on all plotted sets of data to the context menu
207        """
208        for id in self.plot_dict.keys():
209            plot = self.plot_dict[id]
[b789967]210
211            name = plot.name if plot.name else plot.title
[aadf0af1]212            plot_menu = self.contextMenu.addMenu('&%s' % name)
213
214            self.actionDataInfo = plot_menu.addAction("&DataInfo")
215            self.actionDataInfo.triggered.connect(
216                                functools.partial(self.onDataInfo, plot))
217
218            self.actionSavePointsAsFile = plot_menu.addAction("&Save Points as a File")
219            self.actionSavePointsAsFile.triggered.connect(
220                                functools.partial(self.onSavePoints, plot))
221            plot_menu.addSeparator()
222
223            if plot.id != 'fit':
224                self.actionLinearFit = plot_menu.addAction('&Linear Fit')
[570a58f9]225                self.actionLinearFit.triggered.connect(
226                                functools.partial(self.onLinearFit, id))
[aadf0af1]227                plot_menu.addSeparator()
228
229            self.actionRemovePlot = plot_menu.addAction("Remove")
230            self.actionRemovePlot.triggered.connect(
231                                functools.partial(self.onRemovePlot, id))
232
233            if not plot.is_data:
234                self.actionFreeze = plot_menu.addAction('&Freeze')
235                self.actionFreeze.triggered.connect(
236                                functools.partial(self.onFreeze, id))
237            plot_menu.addSeparator()
238
239            if plot.is_data:
240                self.actionHideError = plot_menu.addAction("Hide Error Bar")
241                if plot.dy is not None and plot.dy != []:
242                    if plot.hide_error:
243                        self.actionHideError.setText('Show Error Bar')
244                else:
245                    self.actionHideError.setEnabled(False)
246                self.actionHideError.triggered.connect(
247                                functools.partial(self.onToggleHideError, id))
248                plot_menu.addSeparator()
249
250            self.actionModifyPlot = plot_menu.addAction('&Modify Plot Property')
[87cc73a]251            self.actionModifyPlot.triggered.connect(
252                                functools.partial(self.onModifyPlot, id))
[aadf0af1]253
254    def createContextMenuQuick(self):
[6d05e1d]255        """
256        Define context menu and associated actions for the quickplot MPL widget
257        """
[c4e5400]258        # Default actions
259        self.defaultContextMenu()
260
261        # Additional actions
[6d05e1d]262        self.actionToggleGrid = self.contextMenu.addAction("Toggle Grid On/Off")
263        self.contextMenu.addSeparator()
264        self.actionChangeScale = self.contextMenu.addAction("Change Scale")
265
266        # Define the callbacks
267        self.actionToggleGrid.triggered.connect(self.onGridToggle)
268        self.actionChangeScale.triggered.connect(self.onScaleChange)
269
270    def onScaleChange(self):
271        """
272        Show a dialog allowing axes rescaling
273        """
274        if self.properties.exec_() == QtGui.QDialog.Accepted:
[570a58f9]275            self.xLogLabel, self.yLogLabel = self.properties.getValues()
276            self.xyTransform(self.xLogLabel, self.yLogLabel)
[6d05e1d]277
[27313b7]278    def onAddText(self):
279        """
280        Show a dialog allowing adding custom text to the chart
281        """
[9290b1a]282        if self.addText.exec_() == QtGui.QDialog.Accepted:
283            # Retrieve the new text, its font and color
284            extra_text = self.addText.text()
285            extra_font = self.addText.font()
286            extra_color = self.addText.color()
287
288            # Place the text on the screen at (0,0)
289            pos_x = self.x_click
290            pos_y = self.y_click
291
292            # Map QFont onto MPL font
293            mpl_font = FontProperties()
294            mpl_font.set_size(int(extra_font.pointSize()))
295            mpl_font.set_family(str(extra_font.family()))
296            mpl_font.set_weight(int(extra_font.weight()))
297            # MPL style names
298            styles = ['normal', 'italic', 'oblique']
299            # QFont::Style maps directly onto the above
300            try:
301                mpl_font.set_style(styles[extra_font.style()])
302            except:
303                pass
304
305            if len(extra_text) > 0:
306                new_text = self.ax.text(str(pos_x),
307                                        str(pos_y),
308                                        extra_text,
309                                        color=extra_color,
310                                        fontproperties=mpl_font)
311                # Update the list of annotations
312                self.textList.append(new_text)
313                self.canvas.draw_idle()
[27313b7]314
315    def onRemoveText(self):
316        """
[9290b1a]317        Remove the most recently added text
[27313b7]318        """
[d3ca363]319        num_text = len(self.textList)
320        if num_text < 1:
321            return
322        txt = self.textList[num_text - 1]
323        text_remove = txt.get_text()
324        txt.remove()
325        self.textList.remove(txt)
326
327        self.canvas.draw_idle()
[27313b7]328
329    def onSetGraphRange(self):
330        """
331        Show a dialog allowing setting the chart ranges
332        """
[d3ca363]333        # min and max of data
334        if self.setRange.exec_() == QtGui.QDialog.Accepted:
[257bd57]335            x_range = self.setRange.xrange()
336            y_range = self.setRange.yrange()
337            if x_range is not None and y_range is not None:
338                self.ax.set_xlim(x_range)
339                self.ax.set_ylim(y_range)
340                self.canvas.draw_idle()
[27313b7]341
342    def onResetGraphRange(self):
343        """
[d3ca363]344        Resets the chart X and Y ranges to their original values
[27313b7]345        """
[d3ca363]346        x_range = (self.data.x.min(), self.data.x.max())
347        y_range = (self.data.y.min(), self.data.y.max())
[257bd57]348        if x_range is not None and y_range is not None:
349            self.ax.set_xlim(x_range)
350            self.ax.set_ylim(y_range)
351            self.canvas.draw_idle()
[27313b7]352
[570a58f9]353    def onLinearFit(self, id):
[aadf0af1]354        """
355        Creates and displays a simple linear fit for the selected plot
356        """
[570a58f9]357        selected_plot = self.plot_dict[id]
358
359        maxrange = (min(selected_plot.x), max(selected_plot.x))
360        fitrange = self.ax.get_xlim()
361
362        fit_dialog = LinearFit(parent=self,
363                    data=selected_plot,
364                    max_range=maxrange,
365                    fit_range=fitrange,
366                    xlabel=self.xLogLabel,
367                    ylabel=self.yLogLabel)
368        if fit_dialog.exec_() == QtGui.QDialog.Accepted:
369            return
[aadf0af1]370
[87cc73a]371    def replacePlot(self, id, new_plot):
372        """
373        Remove plot 'id' and add 'new_plot' to the chart.
374        This effectlvely refreshes the chart with changes to one of its plots
375        """
376        self.removePlot(id)
377        self.plot(data=new_plot)
378
[aadf0af1]379    def onRemovePlot(self, id):
380        """
[570a58f9]381        Responds to the plot delete action
382        """
383        self.removePlot(id)
384
385        if len(self.plot_dict) == 0:
386            # last plot: graph is empty must be the panel must be destroyed
387                self.parent.close()
388
389    def removePlot(self, id):
390        """
[aadf0af1]391        Deletes the selected plot from the chart
392        """
[fef38e8]393        if id not in self.plot_dict.keys():
[570a58f9]394            return
395
[aadf0af1]396        selected_plot = self.plot_dict[id]
397
398        plot_dict = copy.deepcopy(self.plot_dict)
399
[b46f285]400        # Labels might have been changed
401        xl = self.ax.xaxis.label.get_text()
402        yl = self.ax.yaxis.label.get_text()
403
[aadf0af1]404        self.plot_dict = {}
405
406        plt.cla()
407        self.ax.cla()
408
409        for ids in plot_dict:
410            if ids != id:
[b46f285]411                self.plot(data=plot_dict[ids], hide_error=plot_dict[ids].hide_error)
412
413        # Reset the labels
414        self.ax.set_xlabel(xl)
415        self.ax.set_ylabel(yl)
416        self.canvas.draw()
[aadf0af1]417
418    def onFreeze(self, id):
419        """
420        Freezes the selected plot to a separate chart
421        """
422        plot = self.plot_dict[id]
423        self.manager.add_data(data_list=[plot])
424
[87cc73a]425    def onModifyPlot(self, id):
[aadf0af1]426        """
427        Allows for MPL modifications to the selected plot
428        """
[87cc73a]429        selected_plot = self.plot_dict[id]
430
431        # Old style color - single integer for enum color
432        # New style color - #hhhhhh
433        color = selected_plot.custom_color
434        # marker symbol and size
435        marker = selected_plot.symbol
436        marker_size = selected_plot.markersize
437        # plot name
438        legend = selected_plot.title
439
440        plotPropertiesWidget = PlotProperties(self,
441                                color=color,
442                                marker=marker,
443                                marker_size=marker_size,
444                                legend=legend)
445        if plotPropertiesWidget.exec_() == QtGui.QDialog.Accepted:
446            # Update Data1d
[0f3c22d]447            selected_plot.markersize = plotPropertiesWidget.markersize()
448            selected_plot.custom_color = plotPropertiesWidget.color()
449            selected_plot.symbol = plotPropertiesWidget.marker()
450            selected_plot.title = plotPropertiesWidget.legend()
[87cc73a]451
452            # Redraw the plot
453            self.replacePlot(id, selected_plot)
[aadf0af1]454
455    def onToggleHideError(self, id):
456        """
457        Toggles hide error/show error menu item
458        """
459        selected_plot = self.plot_dict[id]
460        current = selected_plot.hide_error
461
462        # Flip the flag
463        selected_plot.hide_error = not current
464
465        plot_dict = copy.deepcopy(self.plot_dict)
466        self.plot_dict = {}
467
468        # Clean the canvas
469        plt.cla()
470        self.ax.cla()
471
472        # Recreate the plots but reverse the error flag for the current
473        for ids in plot_dict:
474            if ids == id:
475                self.plot(data=plot_dict[ids], hide_error=(not current))
476            else:
477                self.plot(data=plot_dict[ids], hide_error=plot_dict[ids].hide_error)               
478
[6d05e1d]479    def xyTransform(self, xLabel="", yLabel=""):
480        """
481        Transforms x and y in View and set the scale
482        """
[570a58f9]483        # Transform all the plots on the chart
484        for id in self.plot_dict.keys():
485            current_plot = self.plot_dict[id]
486            if current_plot.id == "fit":
487                self.removePlot(id)
488                continue
[fed94a2]489
[570a58f9]490            new_xlabel, new_ylabel, xscale, yscale =\
491                GuiUtils.xyTransform(current_plot, xLabel, yLabel)
492            self.xscale = xscale
493            self.yscale = yscale
[b46f285]494
[570a58f9]495            # Plot the updated chart
496            self.removePlot(id)
[b46f285]497
498            # This assignment will wrap the label in Latex "$"
499            self.xLabel = new_xlabel
500            self.yLabel = new_ylabel
[a66ff280]501            # Directly overwrite the data to avoid label reassignment
502            self._data = current_plot
[87cc73a]503            self.plot()
[570a58f9]504
505        pass # debug hook
506
[fed94a2]507    def onFitDisplay(self, fit_data):
508        """
509        Add a linear fitting line to the chart
510        """
511        # Create new data structure with fitting result
512        tempx = fit_data[0]
513        tempy = fit_data[1]
514        self.fit_result.x = []
515        self.fit_result.y = []
516        self.fit_result.x = tempx
517        self.fit_result.y = tempy
518        self.fit_result.dx = None
519        self.fit_result.dy = None
520
521        #Remove another Fit, if exists
522        self.removePlot("fit")
523
524        self.fit_result.reset_view()
525        #self.offset_graph()
526
527        # Set plot properties
528        self.fit_result.id = 'fit'
529        self.fit_result.title = 'Fit'
530        self.fit_result.name = 'Fit'
531
532        # Plot the line
[87cc73a]533        self.plot(data=self.fit_result, marker='-', hide_error=True)
[fed94a2]534
[3bdbfcc]535    def onMplMouseDown(self, event):
536        """
537        Left button down and ready to drag
538        """
539        # Check that the LEFT button was pressed
[0268aed]540        if event.button != 1:
541            return
542
543        self.leftdown = True
544        for text in self.textList:
545            if text.contains(event)[0]: # If user has clicked on text
546                self.selectedText = text
547                return
548        if event.inaxes is None:
549            return
550        try:
551            self.x_click = float(event.xdata)  # / size_x
552            self.y_click = float(event.ydata)  # / size_y
553        except:
554            self.position = None
[3bdbfcc]555
556    def onMplMouseUp(self, event):
557        """
558        Set the data coordinates of the click
559        """
560        self.x_click = event.xdata
561        self.y_click = event.ydata
562
563        # Check that the LEFT button was released
564        if event.button == 1:
565            self.leftdown = False
566            self.selectedText = None
567
568        #release the legend
569        if self.gotLegend == 1:
570            self.gotLegend = 0
571
572    def onMplMouseMotion(self, event):
573        """
574        Check if the left button is press and the mouse in moving.
575        Compute delta for x and y coordinates and then perform the drag
576        """
577        if self.gotLegend == 1 and self.leftdown:
578            self.onLegendMotion(event)
579            return
580
[0268aed]581        #if self.leftdown and self.selectedText is not None:
[9f25bce]582        if not self.leftdown or self.selectedText is None:
[3bdbfcc]583            return
[0268aed]584        # User has clicked on text and is dragging
[9f25bce]585        if event.inaxes is None:
[0268aed]586            # User has dragged outside of axes
587            self.selectedText = None
588        else:
589            # Only move text if mouse is within axes
590            self.selectedText.set_position((event.xdata, event.ydata))
591            self.canvas.draw_idle()
592        return
[3bdbfcc]593
594    def onMplPick(self, event):
595        """
596        On pick legend
597        """
598        legend = self.legend
[0268aed]599        if event.artist != legend:
600            return
601        # Get the box of the legend.
602        bbox = self.legend.get_window_extent()
603        # Get mouse coordinates at time of pick.
604        self.mouse_x = event.mouseevent.x
605        self.mouse_y = event.mouseevent.y
606        # Get legend coordinates at time of pick.
607        self.legend_x = bbox.xmin
608        self.legend_y = bbox.ymin
609        # Indicate we picked up the legend.
610        self.gotLegend = 1
611
612        #self.legend.legendPatch.set_alpha(0.5)
[3bdbfcc]613
614    def onLegendMotion(self, event):
615        """
616        On legend in motion
617        """
618        ax = event.inaxes
619        if ax == None:
620            return
621        # Event occurred inside a plotting area
622        lo_x, hi_x = ax.get_xlim()
623        lo_y, hi_y = ax.get_ylim()
624        # How much the mouse moved.
625        x = mouse_diff_x = self.mouse_x - event.x
626        y = mouse_diff_y = self.mouse_y - event.y
627        # Put back inside
628        if x < lo_x:
629            x = lo_x
630        if x > hi_x:
631            x = hi_x
632        if y < lo_y:
633            y = lo_y
634        if y > hi_y:
635            y = hi_y
636        # Move the legend from its previous location by that same amount
637        loc_in_canvas = self.legend_x - mouse_diff_x, \
638                        self.legend_y - mouse_diff_y
639        # Transform into legend coordinate system
640        trans_axes = self.legend.parent.transAxes.inverted()
641        loc_in_norm_axes = trans_axes.transform_point(loc_in_canvas)
642        self.legend_pos_loc = tuple(loc_in_norm_axes)
643        self.legend._loc = self.legend_pos_loc
644        # self.canvas.draw()
645        self.canvas.draw_idle()
646
647    def onMplWheel(self, event):
648        """
649        Process mouse wheel as zoom events
650        """
651        ax = event.inaxes
652        step = event.step
653
654        if ax != None:
655            # Event occurred inside a plotting area
656            lo, hi = ax.get_xlim()
657            lo, hi = PlotUtilities.rescale(lo, hi, step,
658                              pt=event.xdata, scale=ax.get_xscale())
659            if not self.xscale == 'log' or lo > 0:
660                self._scale_xlo = lo
661                self._scale_xhi = hi
662                ax.set_xlim((lo, hi))
663
664            lo, hi = ax.get_ylim()
665            lo, hi = PlotUtilities.rescale(lo, hi, step, pt=event.ydata,
666                              scale=ax.get_yscale())
667            if not self.yscale == 'log' or lo > 0:
668                self._scale_ylo = lo
669                self._scale_yhi = hi
670                ax.set_ylim((lo, hi))
671        else:
672            # Check if zoom happens in the axes
673            xdata, ydata = None, None
674            x, y = event.x, event.y
675
676            for ax in self.axes:
677                insidex, _ = ax.xaxis.contains(event)
678                if insidex:
679                    xdata, _ = ax.transAxes.inverted().transform_point((x, y))
680                insidey, _ = ax.yaxis.contains(event)
681                if insidey:
682                    _, ydata = ax.transAxes.inverted().transform_point((x, y))
683            if xdata is not None:
684                lo, hi = ax.get_xlim()
685                lo, hi = PlotUtilities.rescale(lo, hi, step,
686                                  bal=xdata, scale=ax.get_xscale())
687                if not self.xscale == 'log' or lo > 0:
688                    self._scale_xlo = lo
689                    self._scale_xhi = hi
690                    ax.set_xlim((lo, hi))
691            if ydata is not None:
692                lo, hi = ax.get_ylim()
693                lo, hi = PlotUtilities.rescale(lo, hi, step, bal=ydata,
694                                  scale=ax.get_yscale())
695                if not self.yscale == 'log' or lo > 0:
696                    self._scale_ylo = lo
697                    self._scale_yhi = hi
698                    ax.set_ylim((lo, hi))
699        self.canvas.draw_idle()
700
[c4e5400]701
[416fa8f]702class Plotter(QtGui.QDialog, PlotterWidget):
703    def __init__(self, parent=None, quickplot=False):
704
705        QtGui.QDialog.__init__(self)
[aadf0af1]706        PlotterWidget.__init__(self, parent=self, manager=parent, quickplot=quickplot)
[c4e5400]707        icon = QtGui.QIcon()
708        icon.addPixmap(QtGui.QPixmap(":/res/ball.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
709        self.setWindowIcon(icon)
710
[416fa8f]711
Note: See TracBrowser for help on using the repository browser.