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

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

Clumsy fix to the single-data, multi-fitpage plotting issue SASVIEW-1018.
Fixed tests after replacing plot_dict indexing from .id to .name

  • Property mode set to 100644
File size: 25.8 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.name] = self.data
185
186        self.plot_lines[self._data.name] = 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
204    def createContextMenu(self):
205        """
206        Define common context menu and associated actions for the MPL widget
207        """
208        self.defaultContextMenu()
209
210        # Separate plots
211        self.addPlotsToContextMenu()
212
213        # Additional menu items
214        self.contextMenu.addSeparator()
215        self.actionAddText = self.contextMenu.addAction("Add Text")
216        self.actionRemoveText = self.contextMenu.addAction("Remove Text")
217        self.contextMenu.addSeparator()
218        self.actionChangeScale = self.contextMenu.addAction("Change Scale")
219        self.contextMenu.addSeparator()
220        self.actionSetGraphRange = self.contextMenu.addAction("Set Graph Range")
221        self.actionResetGraphRange =\
222            self.contextMenu.addAction("Reset Graph Range")
223        # Add the title change for dialogs
224        #if self.parent:
225        self.contextMenu.addSeparator()
226        self.actionWindowTitle = self.contextMenu.addAction("Window Title")
227
228        # Define the callbacks
229        self.actionAddText.triggered.connect(self.onAddText)
230        self.actionRemoveText.triggered.connect(self.onRemoveText)
231        self.actionChangeScale.triggered.connect(self.onScaleChange)
232        self.actionSetGraphRange.triggered.connect(self.onSetGraphRange)
233        self.actionResetGraphRange.triggered.connect(self.onResetGraphRange)
234        self.actionWindowTitle.triggered.connect(self.onWindowsTitle)
235
236    def addPlotsToContextMenu(self):
237        """
238        Adds operations on all plotted sets of data to the context menu
239        """
240        for id in list(self.plot_dict.keys()):
241            plot = self.plot_dict[id]
242
243            name = plot.name if plot.name else plot.title
244            plot_menu = self.contextMenu.addMenu('&%s' % name)
245
246            self.actionDataInfo = plot_menu.addAction("&DataInfo")
247            self.actionDataInfo.triggered.connect(
248                                functools.partial(self.onDataInfo, plot))
249
250            self.actionSavePointsAsFile = plot_menu.addAction("&Save Points as a File")
251            self.actionSavePointsAsFile.triggered.connect(
252                                functools.partial(self.onSavePoints, plot))
253            plot_menu.addSeparator()
254
255            if plot.id != 'fit':
256                self.actionLinearFit = plot_menu.addAction('&Linear Fit')
257                self.actionLinearFit.triggered.connect(
258                                functools.partial(self.onLinearFit, id))
259                plot_menu.addSeparator()
260
261            self.actionRemovePlot = plot_menu.addAction("Remove")
262            self.actionRemovePlot.triggered.connect(
263                                functools.partial(self.onRemovePlot, id))
264
265            if not plot.is_data:
266                self.actionFreeze = plot_menu.addAction('&Freeze')
267                self.actionFreeze.triggered.connect(
268                                functools.partial(self.onFreeze, id))
269            plot_menu.addSeparator()
270
271            if plot.is_data:
272                self.actionHideError = plot_menu.addAction("Hide Error Bar")
273                if plot.dy is not None and plot.dy != []:
274                    if plot.hide_error:
275                        self.actionHideError.setText('Show Error Bar')
276                else:
277                    self.actionHideError.setEnabled(False)
278                self.actionHideError.triggered.connect(
279                                functools.partial(self.onToggleHideError, id))
280                plot_menu.addSeparator()
281
282            self.actionModifyPlot = plot_menu.addAction('&Modify Plot Property')
283            self.actionModifyPlot.triggered.connect(
284                                functools.partial(self.onModifyPlot, id))
285
286    def createContextMenuQuick(self):
287        """
288        Define context menu and associated actions for the quickplot MPL widget
289        """
290        # Default actions
291        self.defaultContextMenu()
292
293        # Additional actions
294        self.actionToggleGrid = self.contextMenu.addAction("Toggle Grid On/Off")
295        self.contextMenu.addSeparator()
296        self.actionChangeScale = self.contextMenu.addAction("Change Scale")
297
298        # Define the callbacks
299        self.actionToggleGrid.triggered.connect(self.onGridToggle)
300        self.actionChangeScale.triggered.connect(self.onScaleChange)
301
302    def onScaleChange(self):
303        """
304        Show a dialog allowing axes rescaling
305        """
306        if self.properties.exec_() == QtWidgets.QDialog.Accepted:
307            self.xLogLabel, self.yLogLabel = self.properties.getValues()
308            self.data.xtransform = self.xLogLabel
309            self.data.ytransform = self.yLogLabel
310            self.xyTransform(self.xLogLabel, self.yLogLabel)
311
312    def onAddText(self):
313        """
314        Show a dialog allowing adding custom text to the chart
315        """
316        if self.addText.exec_() != QtWidgets.QDialog.Accepted:
317            return
318
319        # Retrieve the new text, its font and color
320        extra_text = self.addText.text()
321        extra_font = self.addText.font()
322        extra_color = self.addText.color()
323
324        # Place the text on the screen at the click location
325        pos_x = self.x_click
326        pos_y = self.y_click
327
328        # Map QFont onto MPL font
329        mpl_font = FontProperties()
330        mpl_font.set_size(int(extra_font.pointSize()))
331        mpl_font.set_family(str(extra_font.family()))
332        mpl_font.set_weight(int(extra_font.weight()))
333        # MPL style names
334        styles = ['normal', 'italic', 'oblique']
335        # QFont::Style maps directly onto the above
336        try:
337            mpl_font.set_style(styles[extra_font.style()])
338        except:
339            pass
340
341        if len(extra_text) > 0:
342            new_text = self.ax.text(pos_x,
343                                    pos_y,
344                                    extra_text,
345                                    color=extra_color,
346                                    fontproperties=mpl_font)
347
348            # Update the list of annotations
349            self.textList.append(new_text)
350            self.canvas.draw()
351
352    def onRemoveText(self):
353        """
354        Remove the most recently added text
355        """
356        num_text = len(self.textList)
357        if num_text < 1:
358            return
359        txt = self.textList[num_text - 1]
360        text_remove = txt.get_text()
361        try:
362            txt.remove()
363        except ValueError:
364            # Text got already deleted somehow
365            pass
366        self.textList.remove(txt)
367
368        self.canvas.draw_idle()
369
370    def onSetGraphRange(self):
371        """
372        Show a dialog allowing setting the chart ranges
373        """
374        # min and max of data
375        if self.setRange.exec_() == QtWidgets.QDialog.Accepted:
376            x_range = self.setRange.xrange()
377            y_range = self.setRange.yrange()
378            if x_range is not None and y_range is not None:
379                self.ax.set_xlim(x_range)
380                self.ax.set_ylim(y_range)
381                self.canvas.draw_idle()
382
383    def onResetGraphRange(self):
384        """
385        Resets the chart X and Y ranges to their original values
386        """
387        x_range = (self.data.x.min(), self.data.x.max())
388        y_range = (self.data.y.min(), self.data.y.max())
389        if x_range is not None and y_range is not None:
390            self.ax.set_xlim(x_range)
391            self.ax.set_ylim(y_range)
392            self.canvas.draw_idle()
393
394    def onLinearFit(self, id):
395        """
396        Creates and displays a simple linear fit for the selected plot
397        """
398        selected_plot = self.plot_dict[id]
399
400        maxrange = (min(selected_plot.x), max(selected_plot.x))
401        fitrange = self.ax.get_xlim()
402
403        fit_dialog = LinearFit(parent=self,
404                    data=selected_plot,
405                    max_range=maxrange,
406                    fit_range=fitrange,
407                    xlabel=self.xLogLabel,
408                    ylabel=self.yLogLabel)
409        fit_dialog.updatePlot.connect(self.onFitDisplay)
410        if fit_dialog.exec_() == QtWidgets.QDialog.Accepted:
411            return
412
413    def replacePlot(self, id, new_plot):
414        """
415        Remove plot 'id' and add 'new_plot' to the chart.
416        This effectlvely refreshes the chart with changes to one of its plots
417        """
418        self.removePlot(id)
419        self.plot(data=new_plot)
420
421    def onRemovePlot(self, id):
422        """
423        Responds to the plot delete action
424        """
425        self.removePlot(id)
426
427        if len(self.plot_dict) == 0:
428            # last plot: graph is empty must be the panel must be destroyed
429                self.parent.close()
430
431    def removePlot(self, id):
432        """
433        Deletes the selected plot from the chart
434        """
435        if id not in list(self.plot_dict.keys()):
436            return
437
438        selected_plot = self.plot_dict[id]
439
440        plot_dict = copy.deepcopy(self.plot_dict)
441
442        # Labels might have been changed
443        xl = self.ax.xaxis.label.get_text()
444        yl = self.ax.yaxis.label.get_text()
445
446        self.plot_dict = {}
447
448        mpl.pyplot.cla()
449        self.ax.cla()
450
451        for ids in plot_dict:
452            if ids != id:
453                self.plot(data=plot_dict[ids], hide_error=plot_dict[ids].hide_error)
454
455        # Reset the labels
456        self.ax.set_xlabel(xl)
457        self.ax.set_ylabel(yl)
458        self.canvas.draw()
459
460    def onFreeze(self, id):
461        """
462        Freezes the selected plot to a separate chart
463        """
464        plot = self.plot_dict[id]
465        self.manager.add_data(data_list=[plot])
466
467    def onModifyPlot(self, id):
468        """
469        Allows for MPL modifications to the selected plot
470        """
471        selected_plot = self.plot_dict[id]
472        selected_line = self.plot_lines[id]
473        # Old style color - single integer for enum color
474        # New style color - #hhhhhh
475        color = selected_plot.custom_color
476        # marker symbol and size
477        marker = selected_plot.symbol
478        marker_size = selected_plot.markersize
479        # plot name
480        legend = selected_plot.title
481
482        plotPropertiesWidget = PlotProperties(self,
483                                color=color,
484                                marker=marker,
485                                marker_size=marker_size,
486                                legend=legend)
487        if plotPropertiesWidget.exec_() == QtWidgets.QDialog.Accepted:
488            # Update Data1d
489            selected_plot.markersize = plotPropertiesWidget.markersize()
490            selected_plot.custom_color = plotPropertiesWidget.color()
491            selected_plot.symbol = plotPropertiesWidget.marker()
492            selected_plot.title = plotPropertiesWidget.legend()
493
494            # Redraw the plot
495            self.replacePlot(id, selected_plot)
496
497    def onToggleHideError(self, id):
498        """
499        Toggles hide error/show error menu item
500        """
501        selected_plot = self.plot_dict[id]
502        current = selected_plot.hide_error
503
504        # Flip the flag
505        selected_plot.hide_error = not current
506
507        plot_dict = copy.deepcopy(self.plot_dict)
508        self.plot_dict = {}
509
510        # Clean the canvas
511        mpl.pyplot.cla()
512        self.ax.cla()
513
514        # Recreate the plots but reverse the error flag for the current
515        for ids in plot_dict:
516            if ids == id:
517                self.plot(data=plot_dict[ids], hide_error=(not current))
518            else:
519                self.plot(data=plot_dict[ids], hide_error=plot_dict[ids].hide_error)               
520
521    def xyTransform(self, xLabel="", yLabel=""):
522        """
523        Transforms x and y in View and set the scale
524        """
525        # Transform all the plots on the chart
526        for id in list(self.plot_dict.keys()):
527            current_plot = self.plot_dict[id]
528            if current_plot.id == "fit":
529                self.removePlot(id)
530                continue
531
532            new_xlabel, new_ylabel, xscale, yscale =\
533                GuiUtils.xyTransform(current_plot, xLabel, yLabel)
534            self.xscale = xscale
535            self.yscale = yscale
536
537            # Plot the updated chart
538            self.removePlot(id)
539
540            # This assignment will wrap the label in Latex "$"
541            self.xLabel = new_xlabel
542            self.yLabel = new_ylabel
543
544            self.plot(data=current_plot, transform=False)
545
546        pass # debug hook
547
548    def onFitDisplay(self, fit_data):
549        """
550        Add a linear fitting line to the chart
551        """
552        # Create new data structure with fitting result
553        tempx = fit_data[0]
554        tempy = fit_data[1]
555        self.fit_result.x = []
556        self.fit_result.y = []
557        self.fit_result.x = tempx
558        self.fit_result.y = tempy
559        self.fit_result.dx = None
560        self.fit_result.dy = None
561
562        #Remove another Fit, if exists
563        self.removePlot("Fit")
564
565        self.fit_result.reset_view()
566        #self.offset_graph()
567
568        # Set plot properties
569        self.fit_result.id = 'fit'
570        self.fit_result.title = 'Fit'
571        self.fit_result.name = 'Fit'
572
573        # Plot the line
574        self.plot(data=self.fit_result, marker='-', hide_error=True)
575
576    def onMplMouseDown(self, event):
577        """
578        Left button down and ready to drag
579        """
580        # Check that the LEFT button was pressed
581        if event.button != 1:
582            return
583
584        self.leftdown = True
585        for text in self.textList:
586            if text.contains(event)[0]: # If user has clicked on text
587                self.selectedText = text
588                return
589        if event.inaxes is None:
590            return
591        try:
592            self.x_click = float(event.xdata)  # / size_x
593            self.y_click = float(event.ydata)  # / size_y
594        except:
595            self.position = None
596
597    def onMplMouseUp(self, event):
598        """
599        Set the data coordinates of the click
600        """
601        self.x_click = event.xdata
602        self.y_click = event.ydata
603
604        # Check that the LEFT button was released
605        if event.button == 1:
606            self.leftdown = False
607            self.selectedText = None
608
609        #release the legend
610        if self.gotLegend == 1:
611            self.gotLegend = 0
612
613    def onMplMouseMotion(self, event):
614        """
615        Check if the left button is press and the mouse in moving.
616        Compute delta for x and y coordinates and then perform the drag
617        """
618        if self.gotLegend == 1 and self.leftdown:
619            self.onLegendMotion(event)
620            return
621
622        #if self.leftdown and self.selectedText is not None:
623        if not self.leftdown or self.selectedText is None:
624            return
625        # User has clicked on text and is dragging
626        if event.inaxes is None:
627            # User has dragged outside of axes
628            self.selectedText = None
629        else:
630            # Only move text if mouse is within axes
631            self.selectedText.set_position((event.xdata, event.ydata))
632            self.canvas.draw_idle()
633        return
634
635    def onMplPick(self, event):
636        """
637        On pick legend
638        """
639        legend = self.legend
640        if event.artist != legend:
641            return
642        # Get the box of the legend.
643        bbox = self.legend.get_window_extent()
644        # Get mouse coordinates at time of pick.
645        self.mouse_x = event.mouseevent.x
646        self.mouse_y = event.mouseevent.y
647        # Get legend coordinates at time of pick.
648        self.legend_x = bbox.xmin
649        self.legend_y = bbox.ymin
650        # Indicate we picked up the legend.
651        self.gotLegend = 1
652
653        #self.legend.legendPatch.set_alpha(0.5)
654
655    def onLegendMotion(self, event):
656        """
657        On legend in motion
658        """
659        ax = event.inaxes
660        if ax is None:
661            return
662        # Event occurred inside a plotting area
663        lo_x, hi_x = ax.get_xlim()
664        lo_y, hi_y = ax.get_ylim()
665        # How much the mouse moved.
666        x = mouse_diff_x = self.mouse_x - event.x
667        y = mouse_diff_y = self.mouse_y - event.y
668        # Put back inside
669        if x < lo_x:
670            x = lo_x
671        if x > hi_x:
672            x = hi_x
673        if y < lo_y:
674            y = lo_y
675        if y > hi_y:
676            y = hi_y
677        # Move the legend from its previous location by that same amount
678        loc_in_canvas = self.legend_x - mouse_diff_x, \
679                        self.legend_y - mouse_diff_y
680        # Transform into legend coordinate system
681        trans_axes = self.legend.parent.transAxes.inverted()
682        loc_in_norm_axes = trans_axes.transform_point(loc_in_canvas)
683        self.legend_pos_loc = tuple(loc_in_norm_axes)
684        self.legend._loc = self.legend_pos_loc
685        # self.canvas.draw()
686        self.canvas.draw_idle()
687
688    def onMplWheel(self, event):
689        """
690        Process mouse wheel as zoom events
691        """
692        ax = event.inaxes
693        step = event.step
694
695        if ax is not None:
696            # Event occurred inside a plotting area
697            lo, hi = ax.get_xlim()
698            lo, hi = PlotUtilities.rescale(lo, hi, step,
699                              pt=event.xdata, scale=ax.get_xscale())
700            if not self.xscale == 'log' or lo > 0:
701                self._scale_xlo = lo
702                self._scale_xhi = hi
703                ax.set_xlim((lo, hi))
704
705            lo, hi = ax.get_ylim()
706            lo, hi = PlotUtilities.rescale(lo, hi, step, pt=event.ydata,
707                              scale=ax.get_yscale())
708            if not self.yscale == 'log' or lo > 0:
709                self._scale_ylo = lo
710                self._scale_yhi = hi
711                ax.set_ylim((lo, hi))
712        else:
713            # Check if zoom happens in the axes
714            xdata, ydata = None, None
715            x, y = event.x, event.y
716
717            for ax in self.axes:
718                insidex, _ = ax.xaxis.contains(event)
719                if insidex:
720                    xdata, _ = ax.transAxes.inverted().transform_point((x, y))
721                insidey, _ = ax.yaxis.contains(event)
722                if insidey:
723                    _, ydata = ax.transAxes.inverted().transform_point((x, y))
724            if xdata is not None:
725                lo, hi = ax.get_xlim()
726                lo, hi = PlotUtilities.rescale(lo, hi, step,
727                                  bal=xdata, scale=ax.get_xscale())
728                if not self.xscale == 'log' or lo > 0:
729                    self._scale_xlo = lo
730                    self._scale_xhi = hi
731                    ax.set_xlim((lo, hi))
732            if ydata is not None:
733                lo, hi = ax.get_ylim()
734                lo, hi = PlotUtilities.rescale(lo, hi, step, bal=ydata,
735                                  scale=ax.get_yscale())
736                if not self.yscale == 'log' or lo > 0:
737                    self._scale_ylo = lo
738                    self._scale_yhi = hi
739                    ax.set_ylim((lo, hi))
740        self.canvas.draw_idle()
741
742
743class Plotter(QtWidgets.QDialog, PlotterWidget):
744    def __init__(self, parent=None, quickplot=False):
745
746        QtWidgets.QDialog.__init__(self)
747        PlotterWidget.__init__(self, parent=self, manager=parent, quickplot=quickplot)
748        icon = QtGui.QIcon()
749        icon.addPixmap(QtGui.QPixmap(":/res/ball.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
750        self.setWindowIcon(icon)
751
752
Note: See TracBrowser for help on using the repository browser.