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

ESS_GUIESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since 4e85147 was 863ebca, checked in by Piotr Rozyczko <piotrrozyczko@…>, 6 years ago

Introduced navigation bar toggle in context menu for all types of
charts. SASVIEW-890

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