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

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

processEvents() helps with proper chart generation. - SASVIEW-890
Fixed weighing in fitting - SASVIEW-1017
Fixed error bars after fitting - SASVIEW-1004

  • 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
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, nonposx='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        # Update the list of data sets (plots) in chart
182        self.plot_dict[self._data.id] = self.data
183
184        # Now add the legend with some customizations.
185
186        if self.showLegend:
187            self.legend = ax.legend(loc='upper right', shadow=True)
188            if self.legend:
189                self.legend.set_picker(True)
190
191        # Current labels for axes
192        if self.y_label and not is_fit:
193            ax.set_ylabel(self.y_label)
194        if self.x_label and not is_fit:
195            ax.set_xlabel(self.x_label)
196
197        # refresh canvas
198        self.canvas.draw_idle()
199        # This is an important processEvent.
200        # This allows charts to be properly updated in order
201        # of plots being applied.
202        QtWidgets.QApplication.processEvents()
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
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.