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

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

Fixed erroneous error bars. SASVIEW-994

  • Property mode set to 100644
File size: 25.9 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
33        # Window for text add
34        self.addText = AddText(self)
35
36        # Log-ness of the axes
37        self.xLogLabel = "log10(x)"
38        self.yLogLabel = "log10(y)"
39
40        # Data container for the linear fit
41        self.fit_result = Data1D(x=[], y=[], dy=None)
42        self.fit_result.symbol = 13
43        self.fit_result.name = "Fit"
44
45    @property
46    def data(self):
47        return self._data
48
49    @data.setter
50    def data(self, value):
51        """ data setter """
52        self._data = value
53        if value._xunit:
54            self.xLabel = "%s(%s)"%(value._xaxis, value._xunit)
55        else:
56            self.xLabel = "%s"%(value._xaxis)
57        if value._yunit:
58            self.yLabel = "%s(%s)"%(value._yaxis, value._yunit)
59        else:
60            self.yLabel = "%s"%(value._yaxis)
61
62        if value.scale == 'linear' or value.isSesans:
63            self.xscale = 'linear'
64            self.yscale = 'linear'
65        self.title(title=value.name)
66
67    def plot(self, data=None, color=None, marker=None, hide_error=False, transform=True):
68        """
69        Add a new plot of self._data to the chart.
70        """
71        # Data1D
72        if isinstance(data, Data1D):
73            self.data = data
74        assert(self._data)
75
76        is_fit = (self.data.id=="fit")
77
78        if not is_fit:
79            # make sure we have some function to operate on
80            if self.data.xtransform is None:
81                self.data.xtransform = 'log10(x)'
82            if self.data.ytransform is None:
83                self.data.ytransform = 'log10(y)'
84
85            # Transform data if required.
86            if transform and (self.data.xtransform is not None or self.data.ytransform is not None):
87                _, _, xscale, yscale = GuiUtils.xyTransform(self.data, self.data.xtransform, self.data.ytransform)
88                if xscale != 'log':
89                    self.xscale = xscale
90                if yscale != 'log':
91                    self.yscale = yscale
92
93                # Redefine the Scale properties dialog
94                self.properties = ScaleProperties(self,
95                                        init_scale_x=self.data.xtransform,
96                                        init_scale_y=self.data.ytransform)
97
98        # Shortcuts
99        ax = self.ax
100        x = self._data.view.x
101        y = self._data.view.y
102
103        # Marker symbol. Passed marker is one of matplotlib.markers characters
104        # Alternatively, picked up from Data1D as an int index of PlotUtilities.SHAPES dict
105        if marker is None:
106            marker = self.data.symbol
107            # Try name first
108            try:
109                marker = dict(PlotUtilities.SHAPES)[marker]
110            except KeyError:
111                marker = list(PlotUtilities.SHAPES.values())[marker]
112
113        assert marker is not None
114        # Plot name
115        if self.data.title:
116            self.title(title=self.data.title)
117        else:
118            self.title(title=self.data.name)
119
120        # Error marker toggle
121        if hide_error is None:
122            hide_error = self.data.hide_error
123
124        # Plot color
125        if color is None:
126            color = self.data.custom_color
127
128        color = PlotUtilities.getValidColor(color)
129        self.data.custom_color = color
130
131        markersize = self._data.markersize
132
133        # Include scaling (log vs. linear)
134        ax.set_xscale(self.xscale, nonposy='clip')
135        ax.set_yscale(self.yscale, nonposy='clip')
136
137        # define the ranges
138        self.setRange = SetGraphRange(parent=self,
139            x_range=self.ax.get_xlim(), y_range=self.ax.get_ylim())
140
141        # Draw non-standard markers
142        l_width = markersize * 0.4
143        if marker == '-' or marker == '--':
144            line = self.ax.plot(x, y, color=color, lw=l_width, marker='',
145                             linestyle=marker, label=self._title, zorder=10)[0]
146
147        elif marker == 'vline':
148            y_min = min(y)*9.0/10.0 if min(y) < 0 else 0.0
149            line = self.ax.vlines(x=x, ymin=y_min, ymax=y, color=color,
150                            linestyle='-', label=self._title, lw=l_width, zorder=1)
151
152        elif marker == 'step':
153            line = self.ax.step(x, y, color=color, marker='', linestyle='-',
154                                label=self._title, lw=l_width, zorder=1)[0]
155
156        else:
157            # plot data with/without errorbars
158            if hide_error:
159                line = ax.plot(x, y, marker=marker, color=color, markersize=markersize,
160                        linestyle='', label=self._title, picker=True)
161            else:
162                dy = self._data.view.dy
163                # Convert tuple (lo,hi) to array [(x-lo),(hi-x)]
164                if dy is not None and type(dy) == type(()):
165                    dy = np.vstack((y - dy[0], dy[1] - y)).transpose()
166
167                line = ax.errorbar(x, y,
168                            yerr=dy,
169                            xerr=None,
170                            capsize=2, linestyle='',
171                            barsabove=False,
172                            color=color,
173                            marker=marker,
174                            markersize=markersize,
175                            lolims=False, uplims=False,
176                            xlolims=False, xuplims=False,
177                            label=self._title,
178                            zorder=1,
179                            picker=True)
180
181        # Now we know what the potential new color is, let's save it
182        if isinstance(line, mpl.lines.Line2D):
183            self.data.custom_color = line.get_color()
184
185        # Update the list of data sets (plots) in chart
186        self.plot_dict[self._data.id] = self.data
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        # define the ranges
202        #self.setRange = SetGraphRange(parent=self,
203        #    x_range=self.ax.get_xlim(), y_range=self.ax.get_ylim())
204
205        # refresh canvas
206        self.canvas.draw_idle()
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        self.removePlot(id)
423        self.plot(data=new_plot)
424
425    def onRemovePlot(self, id):
426        """
427        Responds to the plot delete action
428        """
429        self.removePlot(id)
430
431        if len(self.plot_dict) == 0:
432            # last plot: graph is empty must be the panel must be destroyed
433                self.parent.close()
434
435    def removePlot(self, id):
436        """
437        Deletes the selected plot from the chart
438        """
439        if id not in list(self.plot_dict.keys()):
440            return
441
442        selected_plot = self.plot_dict[id]
443
444        plot_dict = copy.deepcopy(self.plot_dict)
445
446        # Labels might have been changed
447        xl = self.ax.xaxis.label.get_text()
448        yl = self.ax.yaxis.label.get_text()
449
450        self.plot_dict = {}
451
452        mpl.pyplot.cla()
453        self.ax.cla()
454
455        for ids in plot_dict:
456            if ids != id:
457                self.plot(data=plot_dict[ids], hide_error=plot_dict[ids].hide_error)
458
459        # Reset the labels
460        self.ax.set_xlabel(xl)
461        self.ax.set_ylabel(yl)
462        self.canvas.draw()
463
464    def onFreeze(self, id):
465        """
466        Freezes the selected plot to a separate chart
467        """
468        plot = self.plot_dict[id]
469        self.manager.add_data(data_list=[plot])
470
471    def onModifyPlot(self, id):
472        """
473        Allows for MPL modifications to the selected plot
474        """
475        selected_plot = self.plot_dict[id]
476
477        # Old style color - single integer for enum color
478        # New style color - #hhhhhh
479        color = selected_plot.custom_color
480        # marker symbol and size
481        marker = selected_plot.symbol
482        marker_size = selected_plot.markersize
483        # plot name
484        legend = selected_plot.title
485
486        plotPropertiesWidget = PlotProperties(self,
487                                color=color,
488                                marker=marker,
489                                marker_size=marker_size,
490                                legend=legend)
491        if plotPropertiesWidget.exec_() == QtWidgets.QDialog.Accepted:
492            # Update Data1d
493            selected_plot.markersize = plotPropertiesWidget.markersize()
494            selected_plot.custom_color = plotPropertiesWidget.color()
495            selected_plot.symbol = plotPropertiesWidget.marker()
496            selected_plot.title = plotPropertiesWidget.legend()
497
498            # Redraw the plot
499            self.replacePlot(id, selected_plot)
500
501    def onToggleHideError(self, id):
502        """
503        Toggles hide error/show error menu item
504        """
505        selected_plot = self.plot_dict[id]
506        current = selected_plot.hide_error
507
508        # Flip the flag
509        selected_plot.hide_error = not current
510
511        plot_dict = copy.deepcopy(self.plot_dict)
512        self.plot_dict = {}
513
514        # Clean the canvas
515        mpl.pyplot.cla()
516        self.ax.cla()
517
518        # Recreate the plots but reverse the error flag for the current
519        for ids in plot_dict:
520            if ids == id:
521                self.plot(data=plot_dict[ids], hide_error=(not current))
522            else:
523                self.plot(data=plot_dict[ids], hide_error=plot_dict[ids].hide_error)               
524
525    def xyTransform(self, xLabel="", yLabel=""):
526        """
527        Transforms x and y in View and set the scale
528        """
529        # Transform all the plots on the chart
530        for id in list(self.plot_dict.keys()):
531            current_plot = self.plot_dict[id]
532            if current_plot.id == "fit":
533                self.removePlot(id)
534                continue
535
536            new_xlabel, new_ylabel, xscale, yscale =\
537                GuiUtils.xyTransform(current_plot, xLabel, yLabel)
538            self.xscale = xscale
539            self.yscale = yscale
540
541            # Plot the updated chart
542            self.removePlot(id)
543
544            # This assignment will wrap the label in Latex "$"
545            self.xLabel = new_xlabel
546            self.yLabel = new_ylabel
547
548            self.plot(data=current_plot, transform=False)
549
550        pass # debug hook
551
552    def onFitDisplay(self, fit_data):
553        """
554        Add a linear fitting line to the chart
555        """
556        # Create new data structure with fitting result
557        tempx = fit_data[0]
558        tempy = fit_data[1]
559        self.fit_result.x = []
560        self.fit_result.y = []
561        self.fit_result.x = tempx
562        self.fit_result.y = tempy
563        self.fit_result.dx = None
564        self.fit_result.dy = None
565
566        #Remove another Fit, if exists
567        self.removePlot("fit")
568
569        self.fit_result.reset_view()
570        #self.offset_graph()
571
572        # Set plot properties
573        self.fit_result.id = 'fit'
574        self.fit_result.title = 'Fit'
575        self.fit_result.name = 'Fit'
576
577        # Plot the line
578        self.plot(data=self.fit_result, marker='-', hide_error=True)
579
580    def onMplMouseDown(self, event):
581        """
582        Left button down and ready to drag
583        """
584        # Check that the LEFT button was pressed
585        if event.button != 1:
586            return
587
588        self.leftdown = True
589        for text in self.textList:
590            if text.contains(event)[0]: # If user has clicked on text
591                self.selectedText = text
592                return
593        if event.inaxes is None:
594            return
595        try:
596            self.x_click = float(event.xdata)  # / size_x
597            self.y_click = float(event.ydata)  # / size_y
598        except:
599            self.position = None
600
601    def onMplMouseUp(self, event):
602        """
603        Set the data coordinates of the click
604        """
605        self.x_click = event.xdata
606        self.y_click = event.ydata
607
608        # Check that the LEFT button was released
609        if event.button == 1:
610            self.leftdown = False
611            self.selectedText = None
612
613        #release the legend
614        if self.gotLegend == 1:
615            self.gotLegend = 0
616
617    def onMplMouseMotion(self, event):
618        """
619        Check if the left button is press and the mouse in moving.
620        Compute delta for x and y coordinates and then perform the drag
621        """
622        if self.gotLegend == 1 and self.leftdown:
623            self.onLegendMotion(event)
624            return
625
626        #if self.leftdown and self.selectedText is not None:
627        if not self.leftdown or self.selectedText is None:
628            return
629        # User has clicked on text and is dragging
630        if event.inaxes is None:
631            # User has dragged outside of axes
632            self.selectedText = None
633        else:
634            # Only move text if mouse is within axes
635            self.selectedText.set_position((event.xdata, event.ydata))
636            self.canvas.draw_idle()
637        return
638
639    def onMplPick(self, event):
640        """
641        On pick legend
642        """
643        legend = self.legend
644        if event.artist != legend:
645            return
646        # Get the box of the legend.
647        bbox = self.legend.get_window_extent()
648        # Get mouse coordinates at time of pick.
649        self.mouse_x = event.mouseevent.x
650        self.mouse_y = event.mouseevent.y
651        # Get legend coordinates at time of pick.
652        self.legend_x = bbox.xmin
653        self.legend_y = bbox.ymin
654        # Indicate we picked up the legend.
655        self.gotLegend = 1
656
657        #self.legend.legendPatch.set_alpha(0.5)
658
659    def onLegendMotion(self, event):
660        """
661        On legend in motion
662        """
663        ax = event.inaxes
664        if ax is None:
665            return
666        # Event occurred inside a plotting area
667        lo_x, hi_x = ax.get_xlim()
668        lo_y, hi_y = ax.get_ylim()
669        # How much the mouse moved.
670        x = mouse_diff_x = self.mouse_x - event.x
671        y = mouse_diff_y = self.mouse_y - event.y
672        # Put back inside
673        if x < lo_x:
674            x = lo_x
675        if x > hi_x:
676            x = hi_x
677        if y < lo_y:
678            y = lo_y
679        if y > hi_y:
680            y = hi_y
681        # Move the legend from its previous location by that same amount
682        loc_in_canvas = self.legend_x - mouse_diff_x, \
683                        self.legend_y - mouse_diff_y
684        # Transform into legend coordinate system
685        trans_axes = self.legend.parent.transAxes.inverted()
686        loc_in_norm_axes = trans_axes.transform_point(loc_in_canvas)
687        self.legend_pos_loc = tuple(loc_in_norm_axes)
688        self.legend._loc = self.legend_pos_loc
689        # self.canvas.draw()
690        self.canvas.draw_idle()
691
692    def onMplWheel(self, event):
693        """
694        Process mouse wheel as zoom events
695        """
696        ax = event.inaxes
697        step = event.step
698
699        if ax is not None:
700            # Event occurred inside a plotting area
701            lo, hi = ax.get_xlim()
702            lo, hi = PlotUtilities.rescale(lo, hi, step,
703                              pt=event.xdata, scale=ax.get_xscale())
704            if not self.xscale == 'log' or lo > 0:
705                self._scale_xlo = lo
706                self._scale_xhi = hi
707                ax.set_xlim((lo, hi))
708
709            lo, hi = ax.get_ylim()
710            lo, hi = PlotUtilities.rescale(lo, hi, step, pt=event.ydata,
711                              scale=ax.get_yscale())
712            if not self.yscale == 'log' or lo > 0:
713                self._scale_ylo = lo
714                self._scale_yhi = hi
715                ax.set_ylim((lo, hi))
716        else:
717            # Check if zoom happens in the axes
718            xdata, ydata = None, None
719            x, y = event.x, event.y
720
721            for ax in self.axes:
722                insidex, _ = ax.xaxis.contains(event)
723                if insidex:
724                    xdata, _ = ax.transAxes.inverted().transform_point((x, y))
725                insidey, _ = ax.yaxis.contains(event)
726                if insidey:
727                    _, ydata = ax.transAxes.inverted().transform_point((x, y))
728            if xdata is not None:
729                lo, hi = ax.get_xlim()
730                lo, hi = PlotUtilities.rescale(lo, hi, step,
731                                  bal=xdata, scale=ax.get_xscale())
732                if not self.xscale == 'log' or lo > 0:
733                    self._scale_xlo = lo
734                    self._scale_xhi = hi
735                    ax.set_xlim((lo, hi))
736            if ydata is not None:
737                lo, hi = ax.get_ylim()
738                lo, hi = PlotUtilities.rescale(lo, hi, step, bal=ydata,
739                                  scale=ax.get_yscale())
740                if not self.yscale == 'log' or lo > 0:
741                    self._scale_ylo = lo
742                    self._scale_yhi = hi
743                    ax.set_ylim((lo, hi))
744        self.canvas.draw_idle()
745
746
747class Plotter(QtWidgets.QDialog, PlotterWidget):
748    def __init__(self, parent=None, quickplot=False):
749
750        QtWidgets.QDialog.__init__(self)
751        PlotterWidget.__init__(self, parent=self, manager=parent, quickplot=quickplot)
752        icon = QtGui.QIcon()
753        icon.addPixmap(QtGui.QPixmap(":/res/ball.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
754        self.setWindowIcon(icon)
755
756
Note: See TracBrowser for help on using the repository browser.