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

ESS_GUIESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since c2f3ca2 was c2f3ca2, checked in by Piotr Rozyczko <rozyczko@…>, 8 months ago

Improvements to fitting→plotting process SASVIEW-1029

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