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

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

Improved line colour handling for 1D data charts.

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