source: sasview/src/sas/qtgui/Plotter.py @ 3bdbfcc

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

Reimplementation of the slicer functionality

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