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

ESS_GUIESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since acd0591d was 1f34e00, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 6 years ago

Don't show the plot if no data. Removed debug assert.

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