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

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 685e0e3 was 685e0e3, checked in by piotr, 6 years ago

Fix for frozen theories not showing correct plots. SASVIEW-978
Fix for Linear Fit not working properly after recent changes.

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