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

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

Startup time improvements - hiding expensive imports and such

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