source: sasview/src/sas/sasgui/plottools/PlotPanel.py @ 8abd96d

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 8abd96d was 8abd96d, checked in by lewis, 8 years ago

Fix error bar calculations

Fixing the error bar calculations also removes the need to scale the y
axis when changing to a Porod or Kratky plot, so commit f2f6af9 has
effectively been reverted.

  • Property mode set to 100644
File size: 74.7 KB
Line 
1"""
2    Plot panel.
3"""
4import logging
5import traceback
6import wx
7# Try a normal import first
8# If it fails, try specifying a version
9import matplotlib
10matplotlib.interactive(False)
11#Use the WxAgg back end. The Wx one takes too long to render
12matplotlib.use('WXAgg')
13from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
14from matplotlib.figure import Figure
15import os
16import transform
17from plottables import Data1D
18#TODO: make the plottables interactive
19from binder import BindArtist
20from matplotlib.font_manager import FontProperties
21DEBUG = False
22
23from plottables import Graph
24from TextDialog import TextDialog
25from LabelDialog import LabelDialog
26import operator
27
28import math
29import pylab
30DEFAULT_CMAP = pylab.cm.jet
31import copy
32import numpy
33
34from sas.sasgui.guiframe.events import StatusEvent
35from .toolbar import NavigationToolBar, PlotPrintout, bind
36
37def show_tree(obj, d=0):
38    """Handy function for displaying a tree of graph objects"""
39    print "%s%s" % ("-"*d, obj.__class__.__name__)
40    if 'get_children' in dir(obj):
41        for a in obj.get_children(): show_tree(a, d + 1)
42
43from convert_units import convert_unit
44
45
46def _rescale(lo, hi, step, pt=None, bal=None, scale='linear'):
47    """
48        Rescale (lo,hi) by step, returning the new (lo,hi)
49        The scaling is centered on pt, with positive values of step
50        driving lo/hi away from pt and negative values pulling them in.
51        If bal is given instead of point, it is already in [0,1] coordinates.
52
53        This is a helper function for step-based zooming.
54
55    """
56    # Convert values into the correct scale for a linear transformation
57    # TODO: use proper scale transformers
58    loprev = lo
59    hiprev = hi
60    if scale == 'log':
61        assert lo > 0
62        if lo > 0:
63            lo = math.log10(lo)
64        if hi > 0:
65            hi = math.log10(hi)
66        if pt is not None:
67            pt = math.log10(pt)
68
69    # Compute delta from axis range * %, or 1-% if persent is negative
70    if step > 0:
71        delta = float(hi - lo) * step / 100
72    else:
73        delta = float(hi - lo) * step / (100 - step)
74
75    # Add scale factor proportionally to the lo and hi values,
76    # preserving the
77    # point under the mouse
78    if bal is None:
79        bal = float(pt - lo) / (hi - lo)
80    lo = lo - (bal * delta)
81    hi = hi + (1 - bal) * delta
82
83    # Convert transformed values back to the original scale
84    if scale == 'log':
85        if (lo <= -250) or (hi >= 250):
86            lo = loprev
87            hi = hiprev
88        else:
89            lo, hi = math.pow(10., lo), math.pow(10., hi)
90    return (lo, hi)
91
92
93class PlotPanel(wx.Panel):
94    """
95    The PlotPanel has a Figure and a Canvas. OnSize events simply set a
96    flag, and the actually redrawing of the
97    figure is triggered by an Idle event.
98    """
99    def __init__(self, parent, id=-1, xtransform=None,
100                 ytransform=None, scale='log_{10}',
101                 color=None, dpi=None, **kwargs):
102        """
103        """
104        wx.Panel.__init__(self, parent, id=id, **kwargs)
105        self.parent = parent
106        if hasattr(parent, "parent"):
107            self.parent = self.parent.parent
108        self.dimension = 1
109        self.gotLegend = 0  # to begin, legend is not picked.
110        self.legend_pos_loc = None
111        self.legend = None
112        self.line_collections_list = []
113        self.figure = Figure(None, dpi, linewidth=2.0)
114        self.color = '#b3b3b3'
115        from canvas import FigureCanvas
116        self.canvas = FigureCanvas(self, -1, self.figure)
117        self.SetColor(color)
118        self._resizeflag = True
119        self._SetSize()
120        self.subplot = self.figure.add_subplot(111)
121        self.figure.subplots_adjust(left=0.2, bottom=.2)
122        self.yscale = 'linear'
123        self.xscale = 'linear'
124        self.sizer = wx.BoxSizer(wx.VERTICAL)
125        self.sizer.Add(self.canvas, 1, wx.EXPAND)
126        #add toolbar
127        self.enable_toolbar = True
128        self.toolbar = None
129        self.add_toolbar()
130        self.SetSizer(self.sizer)
131
132        # Graph object to manage the plottables
133        self.graph = Graph()
134
135        #Boolean value to keep track of whether current legend is
136        #visible or not
137        self.legend_on = True
138        self.grid_on = False
139        #Location of legend, default is 0 or 'best'
140        self.legendLoc = 0
141        self.position = None
142        self._loc_labels = self.get_loc_label()
143
144        self.Bind(wx.EVT_CONTEXT_MENU, self.onContextMenu)
145
146        # Define some constants
147        self.colorlist = ['b', 'g', 'r', 'c', 'm', 'y', 'k']
148        self.symbollist = ['o', 'x', '^', 'v', '<', '>', '+',
149                           's', 'd', 'D', 'h', 'H', 'p', '-']
150
151        #List of texts currently on the plot
152        self.textList = []
153        #User scale
154        if xtransform != None:
155            self.xLabel = xtransform
156        else:
157            self.xLabel = "log10(x)"
158        if ytransform != None:
159            self.yLabel = ytransform
160        else:
161            self.yLabel = "log10(y)"
162        self.viewModel = "--"
163        # keep track if the previous transformation of x
164        # and y in Property dialog
165        self.prevXtrans = "log10(x)"
166        self.prevYtrans = "log10(y)"
167        self.scroll_id = self.canvas.mpl_connect('scroll_event', self.onWheel)
168        #taking care of dragging
169        self.motion_id = self.canvas.mpl_connect('motion_notify_event',
170                                                 self.onMouseMotion)
171        self.press_id = self.canvas.mpl_connect('button_press_event',
172                                                self.onLeftDown)
173        self.pick_id = self.canvas.mpl_connect('pick_event', self.onPick)
174        self.release_id = self.canvas.mpl_connect('button_release_event',
175                                                  self.onLeftUp)
176
177        wx.EVT_RIGHT_DOWN(self, self.onLeftDown)
178        # to turn axis off whenn resizing the panel
179        self.resizing = False
180
181        self.leftdown = False
182        self.leftup = False
183        self.mousemotion = False
184        self.axes = [self.subplot]
185        ## Fit dialog
186        self._fit_dialog = None
187        # Interactor
188        self.connect = BindArtist(self.subplot.figure)
189        #self.selected_plottable = None
190
191        # new data for the fit
192        self.fit_result = Data1D(x=[], y=[], dy=None)
193        self.fit_result.symbol = 13
194        #self.fit_result = Data1D(x=[], y=[],dx=None, dy=None)
195        self.fit_result.name = "Fit"
196        # For fit Dialog initial display
197        self.xmin = 0.0
198        self.xmax = 0.0
199        self.xminView = 0.0
200        self.xmaxView = 0.0
201        self._scale_xlo = None
202        self._scale_xhi = None
203        self._scale_ylo = None
204        self._scale_yhi = None
205        self.Avalue = None
206        self.Bvalue = None
207        self.ErrAvalue = None
208        self.ErrBvalue = None
209        self.Chivalue = None
210
211        # for 2D scale
212        if scale != 'linear':
213            scale = 'log_{10}'
214        self.scale = scale
215        self.data = None
216        self.qx_data = None
217        self.qy_data = None
218        self.xmin_2D = None
219        self.xmax_2D = None
220        self.ymin_2D = None
221        self.ymax_2D = None
222        ## store reference to the current plotted vmin and vmax of plotted image
223        ##z range in linear scale
224        self.zmin_2D = None
225        self.zmax_2D = None
226
227        #index array
228        self.index_x = None
229        self.index_y = None
230
231        #number of bins
232        self.x_bins = None
233        self.y_bins = None
234
235        ## default color map
236        self.cmap = DEFAULT_CMAP
237
238        # Dragging info
239        self.begDrag = False
240        self.xInit = None
241        self.yInit = None
242        self.xFinal = None
243        self.yFinal = None
244
245        #axes properties
246        self.xaxis_font = None
247        self.xaxis_label = None
248        self.xaxis_unit = None
249        self.xaxis_color = 'black'
250        self.xaxis_tick = None
251        self.yaxis_font = None
252        self.yaxis_label = None
253        self.yaxis_unit = None
254        self.yaxis_color = 'black'
255        self.yaxis_tick = None
256
257        # check if zoomed.
258        self.is_zoomed = False
259        # Plottables
260        self.plots = {}
261
262        # Default locations
263        self._default_save_location = os.getcwd()
264        # let canvas know about axes
265        self.canvas.set_panel(self)
266        self.ly = None
267        self.q_ctrl = None
268        #Bind focus to change the border color
269        self.canvas.Bind(wx.EVT_SET_FOCUS, self.on_set_focus)
270        self.canvas.Bind(wx.EVT_KILL_FOCUS, self.on_kill_focus)
271
272    def _SetInitialSize(self,):
273        """
274        """
275        pixels = self.parent.GetClientSize()
276        self.canvas.SetSize(pixels)
277        self.figure.set_size_inches(pixels[0] / self.figure.get_dpi(),
278                                    pixels[1] / self.figure.get_dpi(), forward=True)
279
280    def On_Paint(self, event):
281        """
282        """
283        self.canvas.SetBackgroundColour(self.color)
284
285    def on_set_focus(self, event):
286        """
287        Send to the parenet the current panel on focus
288        """
289        # light blue
290        self.color = '#0099f7'
291        self.figure.set_edgecolor(self.color)
292        if self.parent and self.window_caption:
293            self.parent.send_focus_to_datapanel(self.window_caption)
294        self.draw()
295
296    def on_kill_focus(self, event):
297        """
298        Reset the panel color
299        """
300        # light grey
301        self.color = '#b3b3b3'
302        self.figure.set_edgecolor(self.color)
303        self.draw()
304
305    def set_resizing(self, resizing=False):
306        """
307        Set the resizing (True/False)
308        """
309        pass  # Not implemented
310
311    def schedule_full_draw(self, func='append'):
312        """
313        Put self in schedule to full redraw list
314        """
315        pass  # Not implemented
316
317    def add_toolbar(self):
318        """
319        add toolbar
320        """
321        self.enable_toolbar = True
322        self.toolbar = NavigationToolBar(parent=self, canvas=self.canvas)
323        bind(self.toolbar, wx.EVT_TOOL, self.onResetGraph, id=self.toolbar._NTB2_RESET)
324        bind(self.toolbar, wx.EVT_TOOL, self.onContextMenu, id=self.toolbar._NTB2_HOME)
325        self.toolbar.Realize()
326        ## The 'SetToolBar()' is not working on MAC: JHC
327        #if IS_MAC:
328        # Mac platform (OSX 10.3, MacPython) does not seem to cope with
329        # having a toolbar in a sizer. This work-around gets the buttons
330        # back, but at the expense of having the toolbar at the top
331        #self.SetToolBar(self.toolbar)
332        #else:
333        # On Windows platform, default window size is incorrect, so set
334        # toolbar width to figure width.
335        tw, th = self.toolbar.GetSizeTuple()
336        fw, fh = self.canvas.GetSizeTuple()
337        # By adding toolbar in sizer, we are able to put it at the bottom
338        # of the frame - so appearance is closer to GTK version.
339        # As noted above, doesn't work for Mac.
340        self.toolbar.SetSize(wx.Size(fw, th))
341        self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND)
342
343        # update the axes menu on the toolbar
344        self.toolbar.update()
345
346    def onLeftDown(self, event):
347        """
348        left button down and ready to drag
349        """
350        # Check that the LEFT button was pressed
351        if event.button == 1:
352            self.leftdown = True
353            ax = event.inaxes
354            if ax != None:
355                self.xInit, self.yInit = event.xdata, event.ydata
356                try:
357                    pos_x = float(event.xdata)  # / size_x
358                    pos_y = float(event.ydata)  # / size_y
359                    pos_x = "%8.3g" % pos_x
360                    pos_y = "%8.3g" % pos_y
361                    self.position = str(pos_x), str(pos_y)
362                    wx.PostEvent(self.parent, StatusEvent(status=self.position))
363                except:
364                    self.position = None
365
366    def onLeftUp(self, event):
367        """
368        Dragging is done
369        """
370        # Check that the LEFT button was released
371        if event.button == 1:
372            self.leftdown = False
373            self.mousemotion = False
374            self.leftup = True
375
376        #release the legend
377        if self.gotLegend == 1:
378            self.gotLegend = 0
379            self.set_legend_alpha(1)
380
381    def set_legend_alpha(self, alpha=1):
382        """
383        Set legend alpha
384        """
385        if self.legend != None:
386            self.legend.legendPatch.set_alpha(alpha)
387
388    def onPick(self, event):
389        """
390        On pick legend
391        """
392        legend = self.legend
393        if event.artist == legend:
394            #gets the box of the legend.
395            bbox = self.legend.get_window_extent()
396            #get mouse coordinates at time of pick.
397            self.mouse_x = event.mouseevent.x
398            self.mouse_y = event.mouseevent.y
399            #get legend coordinates at time of pick.
400            self.legend_x = bbox.xmin
401            self.legend_y = bbox.ymin
402            #indicates we picked up the legend.
403            self.gotLegend = 1
404            self.set_legend_alpha(0.5)
405
406    def _on_legend_motion(self, event):
407        """
408        On legend in motion
409        """
410        ax = event.inaxes
411        if ax == None:
412            return
413        # Event occurred inside a plotting area
414        lo_x, hi_x = ax.get_xlim()
415        lo_y, hi_y = ax.get_ylim()
416        # How much the mouse moved.
417        x = mouse_diff_x = self.mouse_x - event.x
418        y = mouse_diff_y = self.mouse_y - event.y
419        # Put back inside
420        if x < lo_x:
421            x = lo_x
422        if x > hi_x:
423            x = hi_x
424        if y < lo_y:
425            y = lo_y
426        if y > hi_y:
427            y = hi_y
428        # Move the legend from its previous location by that same amount
429        loc_in_canvas = self.legend_x - mouse_diff_x, \
430                        self.legend_y - mouse_diff_y
431        # Transform into legend coordinate system
432        trans_axes = self.legend.parent.transAxes.inverted()
433        loc_in_norm_axes = trans_axes.transform_point(loc_in_canvas)
434        self.legend_pos_loc = tuple(loc_in_norm_axes)
435        self.legend._loc = self.legend_pos_loc
436        self.resizing = True
437        self.canvas.set_resizing(self.resizing)
438        self.canvas.draw()
439
440    def onMouseMotion(self, event):
441        """
442        check if the left button is press and the mouse in moving.
443        computer delta for x and y coordinates and then calls draghelper
444        to perform the drag
445        """
446        self.cusor_line(event)
447        if self.gotLegend == 1:
448            self._on_legend_motion(event)
449            return
450        if self.enable_toolbar:
451            #Disable dragging without the toolbar to allow zooming with toolbar
452            return
453        self.mousemotion = True
454        if self.leftdown == True and self.mousemotion == True:
455            ax = event.inaxes
456            if ax != None:  # the dragging is perform inside the figure
457                self.xFinal, self.yFinal = event.xdata, event.ydata
458                # Check whether this is the first point
459                if self.xInit == None:
460                    self.xInit = self.xFinal
461                    self.yInit = self.yFinal
462
463                xdelta = self.xFinal - self.xInit
464                ydelta = self.yFinal - self.yInit
465
466                if self.xscale == 'log':
467                    xdelta = math.log10(self.xFinal) - math.log10(self.xInit)
468                if self.yscale == 'log':
469                    ydelta = math.log10(self.yFinal) - math.log10(self.yInit)
470                self._dragHelper(xdelta, ydelta)
471            else:  # no dragging is perform elsewhere
472                self._dragHelper(0, 0)
473
474    def cusor_line(self, event):
475        """
476        """
477        pass
478
479    def _offset_graph(self):
480        """
481        Zoom and offset the graph to the last known settings
482        """
483        for ax in self.axes:
484            if self._scale_xhi is not None and self._scale_xlo is not None:
485                ax.set_xlim(self._scale_xlo, self._scale_xhi)
486            if self._scale_yhi is not None and self._scale_ylo is not None:
487                ax.set_ylim(self._scale_ylo, self._scale_yhi)
488
489    def _dragHelper(self, xdelta, ydelta):
490        """
491        dragging occurs here
492        """
493        # Event occurred inside a plotting area
494        for ax in self.axes:
495            lo, hi = ax.get_xlim()
496            newlo, newhi = lo - xdelta, hi - xdelta
497            if self.xscale == 'log':
498                if lo > 0:
499                    newlo = math.log10(lo) - xdelta
500                if hi > 0:
501                    newhi = math.log10(hi) - xdelta
502            if self.xscale == 'log':
503                self._scale_xlo = math.pow(10, newlo)
504                self._scale_xhi = math.pow(10, newhi)
505                ax.set_xlim(math.pow(10, newlo), math.pow(10, newhi))
506            else:
507                self._scale_xlo = newlo
508                self._scale_xhi = newhi
509                ax.set_xlim(newlo, newhi)
510
511            lo, hi = ax.get_ylim()
512            newlo, newhi = lo - ydelta, hi - ydelta
513            if self.yscale == 'log':
514                if lo > 0:
515                    newlo = math.log10(lo) - ydelta
516                if hi > 0:
517                    newhi = math.log10(hi) - ydelta
518            if  self.yscale == 'log':
519                self._scale_ylo = math.pow(10, newlo)
520                self._scale_yhi = math.pow(10, newhi)
521                ax.set_ylim(math.pow(10, newlo), math.pow(10, newhi))
522            else:
523                self._scale_ylo = newlo
524                self._scale_yhi = newhi
525                ax.set_ylim(newlo, newhi)
526        self.canvas.draw_idle()
527
528    def resetFitView(self):
529        """
530        For fit Dialog initial display
531        """
532        self.xmin = 0.0
533        self.xmax = 0.0
534        self.xminView = 0.0
535        self.xmaxView = 0.0
536        self._scale_xlo = None
537        self._scale_xhi = None
538        self._scale_ylo = None
539        self._scale_yhi = None
540        self.Avalue = None
541        self.Bvalue = None
542        self.ErrAvalue = None
543        self.ErrBvalue = None
544        self.Chivalue = None
545
546    def onWheel(self, event):
547        """
548        Process mouse wheel as zoom events
549
550        :param event: Wheel event
551
552        """
553        ax = event.inaxes
554        step = event.step
555
556        if ax != None:
557            # Event occurred inside a plotting area
558            lo, hi = ax.get_xlim()
559            lo, hi = _rescale(lo, hi, step,
560                              pt=event.xdata, scale=ax.get_xscale())
561            if not self.xscale == 'log' or lo > 0:
562                self._scale_xlo = lo
563                self._scale_xhi = hi
564                ax.set_xlim((lo, hi))
565
566            lo, hi = ax.get_ylim()
567            lo, hi = _rescale(lo, hi, step, pt=event.ydata,
568                              scale=ax.get_yscale())
569            if not self.yscale == 'log' or lo > 0:
570                self._scale_ylo = lo
571                self._scale_yhi = hi
572                ax.set_ylim((lo, hi))
573        else:
574            # Check if zoom happens in the axes
575            xdata, ydata = None, None
576            x, y = event.x, event.y
577
578            for ax in self.axes:
579                insidex, _ = ax.xaxis.contains(event)
580                if insidex:
581                    xdata, _ = ax.transAxes.inverted().transform_point((x, y))
582                insidey, _ = ax.yaxis.contains(event)
583                if insidey:
584                    _, ydata = ax.transAxes.inverted().transform_point((x, y))
585            if xdata is not None:
586                lo, hi = ax.get_xlim()
587                lo, hi = _rescale(lo, hi, step,
588                                  bal=xdata, scale=ax.get_xscale())
589                if not self.xscale == 'log' or lo > 0:
590                    self._scale_xlo = lo
591                    self._scale_xhi = hi
592                    ax.set_xlim((lo, hi))
593            if ydata is not None:
594                lo, hi = ax.get_ylim()
595                lo, hi = _rescale(lo, hi, step, bal=ydata,
596                                  scale=ax.get_yscale())
597                if not self.yscale == 'log' or lo > 0:
598                    self._scale_ylo = lo
599                    self._scale_yhi = hi
600                    ax.set_ylim((lo, hi))
601        self.canvas.draw_idle()
602
603    def returnTrans(self):
604        """
605        Return values and labels used by Fit Dialog
606        """
607        return self.xLabel, self.yLabel, self.Avalue, self.Bvalue, \
608                self.ErrAvalue, self.ErrBvalue, self.Chivalue
609
610    def setTrans(self, xtrans, ytrans):
611        """
612
613        :param xtrans: set x transformation on Property dialog
614        :param ytrans: set y transformation on Property dialog
615
616        """
617        self.prevXtrans = xtrans
618        self.prevYtrans = ytrans
619
620    def onFitting(self, event):
621        """
622        when clicking on linear Fit on context menu , display Fitting Dialog
623        """
624        plot_dict = {}
625        menu = event.GetEventObject()
626        event_id = event.GetId()
627        self.set_selected_from_menu(menu, event_id)
628        plotlist = self.graph.returnPlottable()
629        if self.graph.selected_plottable is not None:
630            for item in plotlist:
631                if item.id == self.graph.selected_plottable:
632                    plot_dict[item] = plotlist[item]
633        else:
634            plot_dict = plotlist
635        from fitDialog import LinearFit
636
637        if len(plot_dict.keys()) > 0:
638            first_item = plot_dict.keys()[0]
639            dlg = LinearFit(parent=None, plottable=first_item,
640                            push_data=self.onFitDisplay,
641                            transform=self.returnTrans,
642                            title='Linear Fit')
643
644            if (self.xmin != 0.0)and (self.xmax != 0.0)\
645                and(self.xminView != 0.0)and (self.xmaxView != 0.0):
646                dlg.setFitRange(self.xminView, self.xmaxView,
647                                self.xmin, self.xmax)
648            # It would be nice for this to NOT be modal (i.e. Show).
649            # Not sure about other ramifications - for example
650            # if a second linear fit is started before the first is closed.
651            # consider for future - being able to work on the plot while
652            # seing the fit values would be very nice  -- PDB 7/10/16
653            dlg.ShowModal()
654
655    def set_selected_from_menu(self, menu, id):
656        """
657        Set selected_plottable from context menu selection
658
659        :param menu: context menu item
660        :param id: menu item id
661        """
662        if len(self.plots) < 1:
663            return
664        name = menu.GetHelpString(id)
665        for plot in self.plots.values():
666            if plot.name == name:
667                self.graph.selected_plottable = plot.id
668                break
669
670    def linear_plottable_fit(self, plot):
671        """
672            when clicking on linear Fit on context menu, display Fitting Dialog
673
674            :param plot: PlotPanel owning the graph
675
676        """
677        from fitDialog import LinearFit
678        if self._fit_dialog is not None:
679            return
680        self._fit_dialog = LinearFit(None, plot, self.onFitDisplay,
681                                     self.returnTrans, -1, 'Linear Fit')
682        # Set the zoom area
683        if self._scale_xhi is not None and self._scale_xlo is not None:
684            self._fit_dialog.set_fit_region(self._scale_xlo, self._scale_xhi)
685        # Register the close event
686        self._fit_dialog.register_close(self._linear_fit_close)
687        # Show a non-model dialog
688        self._fit_dialog.Show()
689
690    def _linear_fit_close(self):
691        """
692        A fit dialog was closed
693        """
694        self._fit_dialog = None
695
696    def _onProperties(self, event):
697        """
698        when clicking on Properties on context menu ,
699        The Property dialog is displayed
700        The user selects a transformation for x or y value and
701        a new plot is displayed
702        """
703        if self._fit_dialog is not None:
704            self._fit_dialog.Destroy()
705            self._fit_dialog = None
706        plot_list = self.graph.returnPlottable()
707        if len(plot_list.keys()) > 0:
708            first_item = plot_list.keys()[0]
709            if first_item.x != []:
710                from PropertyDialog import Properties
711                dial = Properties(self, -1, 'Properties')
712                dial.setValues(self.prevXtrans, self.prevYtrans, self.viewModel)
713                if dial.ShowModal() == wx.ID_OK:
714                    self.xLabel, self.yLabel, self.viewModel = dial.getValues()
715                    if self.viewModel == "Linear y vs x":
716                        self.xLabel = "x"
717                        self.yLabel = "y"
718                        self.viewModel = "--"
719                        dial.setValues(self.xLabel, self.yLabel, self.viewModel)
720                    if self.viewModel == "Guinier lny vs x^(2)":
721                        self.xLabel = "x^(2)"
722                        self.yLabel = "ln(y)"
723                        self.viewModel = "--"
724                        dial.setValues(self.xLabel, self.yLabel, self.viewModel)
725                    if self.viewModel == "XS Guinier ln(y*x) vs x^(2)":
726                        self.xLabel = "x^(2)"
727                        self.yLabel = "ln(y*x)"
728                        self.viewModel = "--"
729                        dial.setValues(self.xLabel, self.yLabel, self.viewModel)
730                    if self.viewModel == "Porod y*x^(4) vs x^(4)":
731                        self.xLabel = "x^(4)"
732                        self.yLabel = "y*x^(4)"
733                        self.viewModel = "--"
734                        dial.setValues(self.xLabel, self.yLabel, self.viewModel)
735                    if self.viewModel == "Kratky y*x^(2) vs x":
736                        self.xLabel = "x"
737                        self.yLabel = "y*x^(2)"
738                        self.viewModel = "--"
739                        dial.setValues(self.xLabel, self.yLabel, self.viewModel)
740                    self._onEVT_FUNC_PROPERTY()
741                dial.Destroy()
742
743    def set_yscale(self, scale='linear'):
744        """
745        Set the scale on Y-axis
746
747        :param scale: the scale of y-axis
748
749        """
750        self.subplot.set_yscale(scale, nonposy='clip')
751        self.yscale = scale
752
753    def get_yscale(self):
754        """
755
756        :return: Y-axis scale
757
758        """
759        return self.yscale
760
761    def set_xscale(self, scale='linear'):
762        """
763        Set the scale on x-axis
764
765        :param scale: the scale of x-axis
766
767        """
768        self.subplot.set_xscale(scale)
769        self.xscale = scale
770
771    def get_xscale(self):
772        """
773
774        :return: x-axis scale
775
776        """
777        return self.xscale
778
779    def SetColor(self, rgbtuple):
780        """
781        Set figure and canvas colours to be the same
782
783        """
784        if not rgbtuple:
785            rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get()
786        col = [c / 255.0 for c in rgbtuple]
787        self.figure.set_facecolor(col)
788        self.figure.set_edgecolor(self.color)
789        self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple))
790
791    def _onSize(self, event):
792        """
793        """
794        self._resizeflag = True
795
796    def _onIdle(self, evt):
797        """
798        """
799        if self._resizeflag:
800            self._resizeflag = False
801            self._SetSize()
802            self.draw()
803
804    def _SetSize(self, pixels=None):
805        """
806        This method can be called to force the Plot to be a desired size,
807         which defaults to the ClientSize of the panel
808
809        """
810        if not pixels:
811            pixels = tuple(self.GetClientSize())
812        self.canvas.SetSize(pixels)
813        self.figure.set_size_inches(float(pixels[0]) / self.figure.get_dpi(),
814                                    float(pixels[1]) / self.figure.get_dpi())
815
816    def draw(self):
817        """
818        Where the actual drawing happens
819
820        """
821        self.figure.canvas.draw_idle()
822
823    def legend_picker(self, legend, event):
824        """
825            Pick up the legend patch
826        """
827        return self.legend.legendPatch.contains(event)
828
829    def get_loc_label(self):
830        """
831        Associates label to a specific legend location
832        """
833        _labels = {}
834        i = 0
835        _labels['best'] = i
836        i += 1
837        _labels['upper right'] = i
838        i += 1
839        _labels['upper left'] = i
840        i += 1
841        _labels['lower left'] = i
842        i += 1
843        _labels['lower right'] = i
844        i += 1
845        _labels['right'] = i
846        i += 1
847        _labels['center left'] = i
848        i += 1
849        _labels['center right'] = i
850        i += 1
851        _labels['lower center'] = i
852        i += 1
853        _labels['upper center'] = i
854        i += 1
855        _labels['center'] = i
856        return _labels
857
858    def onSaveImage(self, evt):
859        """
860        Implement save image
861        """
862        self.toolbar.save_figure(evt)
863
864    def onContextMenu(self, event):
865        """
866        Default context menu for a plot panel
867
868        """
869        # Slicer plot popup menu
870        wx_id = wx.NewId()
871        slicerpop = wx.Menu()
872        slicerpop.Append(wx_id, '&Save image', 'Save image as PNG')
873        wx.EVT_MENU(self, wx_id, self.onSaveImage)
874
875        wx_id = wx.NewId()
876        slicerpop.Append(wx_id, '&Printer setup', 'Set image size')
877        wx.EVT_MENU(self, wx_id, self.onPrinterSetup)
878
879        wx_id = wx.NewId()
880        slicerpop.Append(wx_id, '&Print image', 'Print image ')
881        wx.EVT_MENU(self, wx_id, self.onPrint)
882
883        wx_id = wx.NewId()
884        slicerpop.Append(wx_id, '&Copy', 'Copy to the clipboard')
885        wx.EVT_MENU(self, wx_id, self.OnCopyFigureMenu)
886
887        wx_id = wx.NewId()
888        slicerpop.AppendSeparator()
889        slicerpop.Append(wx_id, '&Properties')
890        wx.EVT_MENU(self, wx_id, self._onProperties)
891
892        wx_id = wx.NewId()
893        slicerpop.AppendSeparator()
894        slicerpop.Append(wx_id, '&Linear Fit')
895        wx.EVT_MENU(self, wx_id, self.onFitting)
896
897        wx_id = wx.NewId()
898        slicerpop.AppendSeparator()
899        slicerpop.Append(wx_id, '&Toggle Legend On/Off', 'Toggle Legend On/Off')
900        wx.EVT_MENU(self, wx_id, self.onLegend)
901
902        loc_menu = wx.Menu()
903        for label in self._loc_labels:
904            wx_id = wx.NewId()
905            loc_menu.Append(wx_id, str(label), str(label))
906            wx.EVT_MENU(self, wx_id, self.onChangeLegendLoc)
907        wx_id = wx.NewId()
908        slicerpop.AppendMenu(wx_id, '&Modify Legend Location', loc_menu)
909
910        wx_id = wx.NewId()
911        slicerpop.Append(wx_id, '&Modify Y Axis Label')
912        wx.EVT_MENU(self, wx_id, self._on_yaxis_label)
913        wx_id = wx.NewId()
914        slicerpop.Append(wx_id, '&Modify X Axis Label')
915        wx.EVT_MENU(self, wx_id, self._on_xaxis_label)
916
917        try:
918            # mouse event
919            pos_evt = event.GetPosition()
920            pos = self.ScreenToClient(pos_evt)
921        except:
922            # toolbar event
923            pos_x, pos_y = self.toolbar.GetPositionTuple()
924            pos = (pos_x, pos_y + 5)
925
926        self.PopupMenu(slicerpop, pos)
927
928    def onToolContextMenu(self, event):
929        """
930        ContextMenu from toolbar
931
932        :param event: toolbar event
933        """
934        # reset postion
935        self.position = None
936        if self.graph.selected_plottable != None:
937            self.graph.selected_plottable = None
938
939        self.onContextMenu(event)
940
941# modified kieranrcampbell ILL june2012
942    def onLegend(self, legOnOff):
943        """
944        Toggles whether legend is visible/not visible
945        """
946        self.legend_on = legOnOff
947        if not self.legend_on:
948            for ax in self.axes:
949                self.remove_legend(ax)
950        else:
951            # sort them by labels
952            handles, labels = self.subplot.get_legend_handles_labels()
953            hl = sorted(zip(handles, labels),
954                        key=operator.itemgetter(1))
955            handles2, labels2 = zip(*hl)
956            self.line_collections_list = handles2
957            self.legend = self.subplot.legend(handles2, labels2,
958                                              prop=FontProperties(size=10),
959                                              loc=self.legendLoc)
960            if self.legend != None:
961                self.legend.set_picker(self.legend_picker)
962                self.legend.set_axes(self.subplot)
963                self.legend.set_zorder(20)
964
965        self.subplot.figure.canvas.draw_idle()
966
967    def onChangeLegendLoc(self, event):
968        """
969        Changes legend loc based on user input
970        """
971        menu = event.GetEventObject()
972        label = menu.GetLabel(event.GetId())
973        self.ChangeLegendLoc(label)
974
975    def ChangeLegendLoc(self, label):
976        """
977        Changes legend loc based on user input
978        """
979        self.legendLoc = label
980        self.legend_pos_loc = None
981        # sort them by labels
982        handles, labels = self.subplot.get_legend_handles_labels()
983        hl = sorted(zip(handles, labels),
984                    key=operator.itemgetter(1))
985        handles2, labels2 = zip(*hl)
986        self.line_collections_list = handles2
987        self.legend = self.subplot.legend(handles2, labels2,
988                                          prop=FontProperties(size=10),
989                                          loc=self.legendLoc)
990        if self.legend != None:
991            self.legend.set_picker(self.legend_picker)
992            self.legend.set_axes(self.subplot)
993            self.legend.set_zorder(20)
994        self.subplot.figure.canvas.draw_idle()
995
996    def remove_legend(self, ax=None):
997        """
998        Remove legend for ax or the current axes.
999        """
1000        from pylab import gca
1001        if ax is None:
1002            ax = gca()
1003        ax.legend_ = None
1004
1005    def _on_addtext(self, event):
1006        """
1007        Allows you to add text to the plot
1008        """
1009        pos_x = 0
1010        pos_y = 0
1011        if self.position != None:
1012            pos_x, pos_y = self.position
1013        else:
1014            pos_x, pos_y = 0.01, 1.00
1015
1016        textdial = TextDialog(None, -1, 'Add Custom Text')
1017        if textdial.ShowModal() == wx.ID_OK:
1018            try:
1019                FONT = FontProperties()
1020                label = textdial.getText()
1021                xpos = pos_x
1022                ypos = pos_y
1023                font = FONT.copy()
1024                font.set_size(textdial.getSize())
1025                font.set_family(textdial.getFamily())
1026                font.set_style(textdial.getStyle())
1027                font.set_weight(textdial.getWeight())
1028                colour = textdial.getColor()
1029                if len(label) > 0 and xpos > 0 and ypos > 0:
1030                    new_text = self.subplot.text(str(xpos), str(ypos), label,
1031                                                 fontproperties=font,
1032                                                 color=colour)
1033                    self.textList.append(new_text)
1034                    self.subplot.figure.canvas.draw_idle()
1035            except:
1036                if self.parent != None:
1037                    msg = "Add Text: Error. Check your property values..."
1038                    wx.PostEvent(self.parent, StatusEvent(status=msg))
1039                else:
1040                    raise
1041        textdial.Destroy()
1042        #Have a pop up box come up for user to type in the
1043        #text that they want to add...then create text Plottable
1044        #based on this and plot it at user designated coordinates
1045
1046    def onGridOnOff(self, gridon_off):
1047        """
1048        Allows ON/OFF Grid
1049        """
1050        self.grid_on = gridon_off
1051
1052        self.subplot.figure.canvas.draw_idle()
1053
1054    def _on_xaxis_label(self, event):
1055        """
1056        Allows you to add text to the plot
1057        """
1058        xaxis_label, xaxis_unit, xaxis_font, xaxis_color, \
1059                     is_ok, is_tick = self._on_axis_label(axis='x')
1060        if not is_ok:
1061            return
1062
1063        self.xaxis_label = xaxis_label
1064        self.xaxis_unit = xaxis_unit
1065        self.xaxis_font = xaxis_font
1066        self.xaxis_color = xaxis_color
1067        if is_tick:
1068            self.xaxis_tick = xaxis_font
1069
1070        if self.data != None:
1071            # 2D
1072            self.xaxis(self.xaxis_label, self.xaxis_unit, \
1073                        self.xaxis_font, self.xaxis_color, self.xaxis_tick)
1074            self.subplot.figure.canvas.draw_idle()
1075        else:
1076            # 1D
1077            self._check_zoom_plot()
1078
1079    def _check_zoom_plot(self):
1080        """
1081        Check the zoom range and plot (1D only)
1082        """
1083        xlo, xhi = self.subplot.get_xlim()
1084        ylo, yhi = self.subplot.get_ylim()
1085        ## Set the view scale for all plots
1086        self._onEVT_FUNC_PROPERTY(False)
1087        if self.is_zoomed:
1088            # Recover the x,y limits
1089            self.subplot.set_xlim((xlo, xhi))
1090            self.subplot.set_ylim((ylo, yhi))
1091
1092    @property
1093    def is_zoomed(self):
1094        toolbar_zoomed = self.toolbar.GetToolEnabled(self.toolbar.wx_ids['Back'])
1095        return self._is_zoomed or toolbar_zoomed
1096
1097    @is_zoomed.setter
1098    def is_zoomed(self, value):
1099        self._is_zoomed = value
1100
1101    def _on_yaxis_label(self, event):
1102        """
1103        Allows you to add text to the plot
1104        """
1105        yaxis_label, yaxis_unit, yaxis_font, yaxis_color, \
1106                        is_ok, is_tick = self._on_axis_label(axis='y')
1107        if not is_ok:
1108            return
1109
1110        self.yaxis_label = yaxis_label
1111        self.yaxis_unit = yaxis_unit
1112        self.yaxis_font = yaxis_font
1113        self.yaxis_color = yaxis_color
1114        if is_tick:
1115            self.yaxis_tick = yaxis_font
1116
1117        if self.data != None:
1118            # 2D
1119            self.yaxis(self.yaxis_label, self.yaxis_unit, \
1120                        self.yaxis_font, self.yaxis_color, self.yaxis_tick)
1121            self.subplot.figure.canvas.draw_idle()
1122        else:
1123            # 1D
1124            self._check_zoom_plot()
1125
1126    def _on_axis_label(self, axis='x'):
1127        """
1128        Modify axes labels
1129
1130        :param axis: x or y axis [string]
1131        """
1132        is_ok = True
1133        title = 'Modify %s axis label' % axis
1134        font = 'serif'
1135        colour = 'black'
1136        if axis == 'x':
1137            label = self.xaxis_label
1138            unit = self.xaxis_unit
1139        else:
1140            label = self.yaxis_label
1141            unit = self.yaxis_unit
1142        textdial = TextDialog(None, -1, title, label, unit)
1143        if textdial.ShowModal() == wx.ID_OK:
1144            try:
1145                FONT = FontProperties()
1146                font = FONT.copy()
1147                font.set_size(textdial.getSize())
1148                font.set_family(textdial.getFamily())
1149                font.set_style(textdial.getStyle())
1150                font.set_weight(textdial.getWeight())
1151                unit = textdial.getUnit()
1152                colour = textdial.getColor()
1153                is_tick = textdial.getTickLabel()
1154                label_temp = textdial.getText()
1155                if label_temp.count("\%s" % "\\") > 0:
1156                    if self.parent != None:
1157                        msg = "Add Label: Error. Can not use double '\\' "
1158                        msg += "characters..."
1159                        wx.PostEvent(self.parent, StatusEvent(status=msg))
1160                else:
1161                    label = label_temp
1162            except:
1163                if self.parent != None:
1164                    msg = "Add Label: Error. Check your property values..."
1165                    wx.PostEvent(self.parent, StatusEvent(status=msg))
1166                else:
1167                    pass
1168        else:
1169            is_ok = False
1170            is_tick = True
1171        textdial.Destroy()
1172        return label, unit, font, colour, is_ok, is_tick
1173
1174    def _on_removetext(self, event):
1175        """
1176        Removes all text from the plot.
1177        Eventually, add option to remove specific text boxes
1178        """
1179        num_text = len(self.textList)
1180        if num_text < 1:
1181            if self.parent != None:
1182                msg = "Remove Text: Nothing to remove.  "
1183                wx.PostEvent(self.parent, StatusEvent(status=msg))
1184            else:
1185                raise
1186            return
1187        txt = self.textList[num_text - 1]
1188        try:
1189            text_remove = txt.get_text()
1190            txt.remove()
1191            if self.parent != None:
1192                msg = "Removed Text: '%s'. " % text_remove
1193                wx.PostEvent(self.parent, StatusEvent(status=msg))
1194        except:
1195            if self.parent != None:
1196                msg = "Remove Text: Error occurred. "
1197                wx.PostEvent(self.parent, StatusEvent(status=msg))
1198            else:
1199                raise
1200        self.textList.remove(txt)
1201
1202        self.subplot.figure.canvas.draw_idle()
1203
1204    def properties(self, prop):
1205        """
1206        Set some properties of the graph.
1207        The set of properties is not yet determined.
1208
1209        """
1210        # The particulars of how they are stored and manipulated (e.g., do
1211        # we want an inventory internally) is not settled.  I've used a
1212        # property dictionary for now.
1213        #
1214        # How these properties interact with a user defined style file is
1215        # even less clear.
1216
1217        # Properties defined by plot
1218
1219        # Ricardo:
1220        # A empty label "$$" will prevent the panel from displaying!
1221        if prop["xlabel"]:
1222            self.subplot.set_xlabel(r"$%s$"%prop["xlabel"])
1223        if prop["ylabel"]:
1224            self.subplot.set_ylabel(r"$%s$"%prop["ylabel"])
1225        self.subplot.set_title(prop["title"])
1226
1227
1228    def clear(self):
1229        """Reset the plot"""
1230        # TODO: Redraw is brutal.  Render to a backing store and swap in
1231        # TODO: rather than redrawing on the fly.
1232        self.subplot.clear()
1233        self.subplot.hold(True)
1234
1235    def render(self):
1236        """Commit the plot after all objects are drawn"""
1237        # TODO: this is when the backing store should be swapped in.
1238        if self.legend_on:
1239            ax = self.subplot
1240            ax.texts = self.textList
1241            try:
1242                handles, labels = ax.get_legend_handles_labels()
1243                # sort them by labels
1244                hl = sorted(zip(handles, labels),
1245                            key=operator.itemgetter(1))
1246                handles2, labels2 = zip(*hl)
1247                self.line_collections_list = handles2
1248                self.legend = ax.legend(handles2, labels2,
1249                                        prop=FontProperties(size=10),
1250                                        loc=self.legendLoc)
1251                if self.legend != None:
1252                    self.legend.set_picker(self.legend_picker)
1253                    self.legend.set_axes(self.subplot)
1254                    self.legend.set_zorder(20)
1255
1256            except:
1257                self.legend = ax.legend(prop=FontProperties(size=10),
1258                                        loc=self.legendLoc)
1259
1260    def xaxis(self, label, units, font=None, color='black', t_font=None):
1261        """xaxis label and units.
1262
1263        Axis labels know about units.
1264
1265        We need to do this so that we can detect when axes are not
1266        commesurate.  Currently this is ignored other than for formatting
1267        purposes.
1268
1269        """
1270
1271        self.xcolor = color
1272        if units.count("{") > 0 and units.count("$") < 2:
1273            units = '$' + units + '$'
1274        if label.count("{") > 0 and label.count("$") < 2:
1275            label = '$' + label + '$'
1276        if units != "":
1277            label = label + " (" + units + ")"
1278        if font:
1279            self.subplot.set_xlabel(label, fontproperties=font, color=color)
1280            if t_font != None:
1281                for tick in self.subplot.xaxis.get_major_ticks():
1282                    tick.label.set_fontproperties(t_font)
1283                for line in self.subplot.xaxis.get_ticklines():
1284                    size = t_font.get_size()
1285                    line.set_markersize(size / 3)
1286        else:
1287            self.subplot.set_xlabel(label, color=color)
1288        pass
1289
1290    def yaxis(self, label, units, font=None, color='black', t_font=None):
1291        """yaxis label and units."""
1292        self.ycolor = color
1293        if units.count("{") > 0 and units.count("$") < 2:
1294            units = '$' + units + '$'
1295        if label.count("{") > 0 and label.count("$") < 2:
1296            label = '$' + label + '$'
1297        if units != "":
1298            label = label + " (" + units + ")"
1299        if font:
1300            self.subplot.set_ylabel(label, fontproperties=font, color=color)
1301            if t_font != None:
1302                for tick_label in self.subplot.get_yticklabels():
1303                    tick_label.set_fontproperties(t_font)
1304                for line in self.subplot.yaxis.get_ticklines():
1305                    size = t_font.get_size()
1306                    line.set_markersize(size / 3)
1307        else:
1308            self.subplot.set_ylabel(label, color=color)
1309        pass
1310
1311    def _connect_to_xlim(self, callback):
1312        """Bind the xlim change notification to the callback"""
1313        def process_xlim(axes):
1314            lo, hi = subplot.get_xlim()
1315            callback(lo, hi)
1316        self.subplot.callbacks.connect('xlim_changed', process_xlim)
1317
1318    def interactive_points(self, x, y, dx=None, dy=None, name='', color=0,
1319                           symbol=0, markersize=5, zorder=1, id=None,
1320                           label=None, hide_error=False):
1321        """Draw markers with error bars"""
1322        self.subplot.set_yscale('linear')
1323        self.subplot.set_xscale('linear')
1324        if id is None:
1325            id = name
1326        from plottable_interactor import PointInteractor
1327        p = PointInteractor(self, self.subplot, zorder=zorder, id=id)
1328        if p.markersize != None:
1329            markersize = p.markersize
1330        p.points(x, y, dx=dx, dy=dy, color=color, symbol=symbol, zorder=zorder,
1331                 markersize=markersize, label=label, hide_error=hide_error)
1332
1333        self.subplot.set_yscale(self.yscale, nonposy='clip')
1334        self.subplot.set_xscale(self.xscale)
1335
1336    def interactive_curve(self, x, y, dy=None, name='', color=0,
1337                          symbol=0, zorder=1, id=None, label=None):
1338        """Draw markers with error bars"""
1339        self.subplot.set_yscale('linear')
1340        self.subplot.set_xscale('linear')
1341        if id is None:
1342            id = name
1343        from plottable_interactor import PointInteractor
1344        p = PointInteractor(self, self.subplot, zorder=zorder, id=id)
1345        p.curve(x, y, dy=dy, color=color, symbol=symbol, zorder=zorder,
1346                label=label)
1347
1348        self.subplot.set_yscale(self.yscale, nonposy='clip')
1349        self.subplot.set_xscale(self.xscale)
1350
1351    def plottable_selected(self, id):
1352        """
1353        Called to register a plottable as selected
1354        """
1355        #TODO: check that it really is in the list of plottables
1356        self.graph.selected_plottable = id
1357
1358    def points(self, x, y, dx=None, dy=None,
1359               color=0, symbol=0, marker_size=5, label=None,
1360               id=None, hide_error=False):
1361        """Draw markers with error bars"""
1362
1363        # Convert tuple (lo,hi) to array [(x-lo),(hi-x)]
1364        if dx != None and type(dx) == type(()):
1365            dx = nx.vstack((x - dx[0], dx[1] - x)).transpose()
1366        if dy != None and type(dy) == type(()):
1367            dy = nx.vstack((y - dy[0], dy[1] - y)).transpose()
1368        if dx == None and dy == None:
1369            self.subplot.plot(x, y, color=self._color(color),
1370                              marker=self._symbol(symbol),
1371                              markersize=marker_size,
1372                              linestyle='',
1373                              label=label)
1374        else:
1375            col = self._color(color)
1376            if hide_error:
1377                self.subplot.plot(x, y, color=col,
1378                                  marker=self._symbol(symbol),
1379                                  markersize=marker_size,
1380                                  linestyle='',
1381                                  label=label)
1382            else:
1383                self.subplot.errorbar(x, y, yerr=dy, xerr=None,
1384                                      ecolor=col, capsize=2, linestyle='',
1385                                      barsabove=False,
1386                                      mec=col, mfc=col,
1387                                      marker=self._symbol(symbol),
1388                                      markersize=marker_size,
1389                                      lolims=False, uplims=False,
1390                                      xlolims=False, xuplims=False, label=label)
1391
1392        self.subplot.set_yscale(self.yscale, nonposy='clip')
1393        self.subplot.set_xscale(self.xscale)
1394
1395    def _onToggleScale(self, event):
1396        """
1397        toggle axis and replot image
1398
1399        """
1400        zmin_2D_temp = self.zmin_2D
1401        zmax_2D_temp = self.zmax_2D
1402        if self.scale == 'log_{10}':
1403            self.scale = 'linear'
1404            if not self.zmin_2D is None:
1405                zmin_2D_temp = math.pow(10, self.zmin_2D)
1406            if not self.zmax_2D is None:
1407                zmax_2D_temp = math.pow(10, self.zmax_2D)
1408        else:
1409            self.scale = 'log_{10}'
1410            if not self.zmin_2D is None:
1411                # min log value: no log(negative)
1412                if self.zmin_2D <= 0:
1413                    zmin_2D_temp = -32
1414                else:
1415                    zmin_2D_temp = math.log10(self.zmin_2D)
1416            if not self.zmax_2D is None:
1417                zmax_2D_temp = math.log10(self.zmax_2D)
1418
1419        self.image(data=self.data, qx_data=self.qx_data,
1420                   qy_data=self.qy_data, xmin=self.xmin_2D,
1421                   xmax=self.xmax_2D,
1422                   ymin=self.ymin_2D, ymax=self.ymax_2D,
1423                   cmap=self.cmap, zmin=zmin_2D_temp,
1424                   zmax=zmax_2D_temp)
1425
1426    def image(self, data, qx_data, qy_data, xmin, xmax, ymin, ymax,
1427              zmin, zmax, color=0, symbol=0, markersize=0,
1428              label='data2D', cmap=DEFAULT_CMAP):
1429        """
1430        Render the current data
1431
1432        """
1433        self.data = data
1434        self.qx_data = qx_data
1435        self.qy_data = qy_data
1436        self.xmin_2D = xmin
1437        self.xmax_2D = xmax
1438        self.ymin_2D = ymin
1439        self.ymax_2D = ymax
1440        self.zmin_2D = zmin
1441        self.zmax_2D = zmax
1442        c = self._color(color)
1443        # If we don't have any data, skip.
1444        if self.data == None:
1445            return
1446        if self.data.ndim == 1:
1447            output = self._build_matrix()
1448        else:
1449            output = copy.deepcopy(self.data)
1450        # check scale
1451        if self.scale == 'log_{10}':
1452            try:
1453                if  self.zmin_2D <= 0  and len(output[output > 0]) > 0:
1454                    zmin_temp = self.zmin_2D
1455                    output[output > 0] = numpy.log10(output[output > 0])
1456                    #In log scale Negative values are not correct in general
1457                    #output[output<=0] = math.log(numpy.min(output[output>0]))
1458                elif self.zmin_2D <= 0:
1459                    zmin_temp = self.zmin_2D
1460                    output[output > 0] = numpy.zeros(len(output))
1461                    output[output <= 0] = -32
1462                else:
1463                    zmin_temp = self.zmin_2D
1464                    output[output > 0] = numpy.log10(output[output > 0])
1465                    #In log scale Negative values are not correct in general
1466                    #output[output<=0] = math.log(numpy.min(output[output>0]))
1467            except:
1468                #Too many problems in 2D plot with scale
1469                pass
1470
1471        else:
1472            zmin_temp = self.zmin_2D
1473        self.cmap = cmap
1474        if self.dimension != 3:
1475            #Re-adjust colorbar
1476            self.subplot.figure.subplots_adjust(left=0.2, right=.8, bottom=.2)
1477
1478            im = self.subplot.imshow(output, interpolation='nearest',
1479                                     origin='lower',
1480                                     vmin=zmin_temp, vmax=self.zmax_2D,
1481                                     cmap=self.cmap,
1482                                     extent=(self.xmin_2D, self.xmax_2D,
1483                                             self.ymin_2D, self.ymax_2D))
1484
1485            cbax = self.subplot.figure.add_axes([0.84, 0.2, 0.02, 0.7])
1486        else:
1487            # clear the previous 2D from memory
1488            # mpl is not clf, so we do
1489            self.subplot.figure.clear()
1490
1491            self.subplot.figure.subplots_adjust(left=0.1, right=.8, bottom=.1)
1492
1493            X = self.x_bins[0:-1]
1494            Y = self.y_bins[0:-1]
1495            X, Y = numpy.meshgrid(X, Y)
1496
1497            try:
1498                # mpl >= 1.0.0
1499                ax = self.subplot.figure.gca(projection='3d')
1500                #ax.disable_mouse_rotation()
1501                cbax = self.subplot.figure.add_axes([0.84, 0.1, 0.02, 0.8])
1502                if len(X) > 60:
1503                    ax.disable_mouse_rotation()
1504            except:
1505                # mpl < 1.0.0
1506                try:
1507                    from mpl_toolkits.mplot3d import Axes3D
1508                except:
1509                    logging.error("PlotPanel could not import Axes3D")
1510                self.subplot.figure.clear()
1511                ax = Axes3D(self.subplot.figure)
1512                if len(X) > 60:
1513                    ax.cla()
1514                cbax = None
1515            self.subplot.figure.canvas.resizing = False
1516            im = ax.plot_surface(X, Y, output, rstride=1, cstride=1, cmap=cmap,
1517                                 linewidth=0, antialiased=False)
1518            self.subplot.set_axis_off()
1519
1520        if cbax == None:
1521            ax.set_frame_on(False)
1522            cb = self.subplot.figure.colorbar(im, shrink=0.8, aspect=20)
1523        else:
1524            cb = self.subplot.figure.colorbar(im, cax=cbax)
1525        cb.update_bruteforce(im)
1526        cb.set_label('$' + self.scale + '$')
1527        if self.dimension != 3:
1528            self.figure.canvas.draw_idle()
1529        else:
1530            self.figure.canvas.draw()
1531
1532    def _build_matrix(self):
1533        """
1534        Build a matrix for 2d plot from a vector
1535        Returns a matrix (image) with ~ square binning
1536        Requirement: need 1d array formats of
1537        self.data, self.qx_data, and self.qy_data
1538        where each one corresponds to z, x, or y axis values
1539
1540        """
1541        # No qx or qy given in a vector format
1542        if self.qx_data == None or self.qy_data == None \
1543                or self.qx_data.ndim != 1 or self.qy_data.ndim != 1:
1544            # do we need deepcopy here?
1545            return copy.deepcopy(self.data)
1546
1547        # maximum # of loops to fillup_pixels
1548        # otherwise, loop could never stop depending on data
1549        max_loop = 1
1550        # get the x and y_bin arrays.
1551        self._get_bins()
1552        # set zero to None
1553
1554        #Note: Can not use scipy.interpolate.Rbf:
1555        # 'cause too many data points (>10000)<=JHC.
1556        # 1d array to use for weighting the data point averaging
1557        #when they fall into a same bin.
1558        weights_data = numpy.ones([self.data.size])
1559        # get histogram of ones w/len(data); this will provide
1560        #the weights of data on each bins
1561        weights, xedges, yedges = numpy.histogram2d(x=self.qy_data,
1562                                                    y=self.qx_data,
1563                                                    bins=[self.y_bins, self.x_bins],
1564                                                    weights=weights_data)
1565        # get histogram of data, all points into a bin in a way of summing
1566        image, xedges, yedges = numpy.histogram2d(x=self.qy_data,
1567                                                  y=self.qx_data,
1568                                                  bins=[self.y_bins, self.x_bins],
1569                                                  weights=self.data)
1570        # Now, normalize the image by weights only for weights>1:
1571        # If weight == 1, there is only one data point in the bin so
1572        # that no normalization is required.
1573        image[weights > 1] = image[weights > 1] / weights[weights > 1]
1574        # Set image bins w/o a data point (weight==0) as None (was set to zero
1575        # by histogram2d.)
1576        image[weights == 0] = None
1577
1578        # Fill empty bins with 8 nearest neighbors only when at least
1579        #one None point exists
1580        loop = 0
1581
1582        # do while loop until all vacant bins are filled up up
1583        #to loop = max_loop
1584        while not(numpy.isfinite(image[weights == 0])).all():
1585            if loop >= max_loop:  # this protects never-ending loop
1586                break
1587            image = self._fillup_pixels(image=image, weights=weights)
1588            loop += 1
1589
1590        return image
1591
1592    def _get_bins(self):
1593        """
1594        get bins
1595        set x_bins and y_bins into self, 1d arrays of the index with
1596        ~ square binning
1597        Requirement: need 1d array formats of
1598        self.qx_data, and self.qy_data
1599        where each one corresponds to  x, or y axis values
1600        """
1601        # No qx or qy given in a vector format
1602        if self.qx_data == None or self.qy_data == None \
1603                or self.qx_data.ndim != 1 or self.qy_data.ndim != 1:
1604            # do we need deepcopy here?
1605            return copy.deepcopy(self.data)
1606
1607        # find max and min values of qx and qy
1608        xmax = self.qx_data.max()
1609        xmin = self.qx_data.min()
1610        ymax = self.qy_data.max()
1611        ymin = self.qy_data.min()
1612
1613        # calculate the range of qx and qy: this way, it is a little
1614        # more independent
1615        x_size = xmax - xmin
1616        y_size = ymax - ymin
1617
1618        # estimate the # of pixels on each axes
1619        npix_y = int(math.floor(math.sqrt(len(self.qy_data))))
1620        npix_x = int(math.floor(len(self.qy_data) / npix_y))
1621
1622        # bin size: x- & y-directions
1623        xstep = x_size / (npix_x - 1)
1624        ystep = y_size / (npix_y - 1)
1625
1626        # max and min taking account of the bin sizes
1627        xmax = xmax + xstep / 2.0
1628        xmin = xmin - xstep / 2.0
1629        ymax = ymax + ystep / 2.0
1630        ymin = ymin - ystep / 2.0
1631
1632        # store x and y bin centers in q space
1633        x_bins = numpy.linspace(xmin, xmax, npix_x)
1634        y_bins = numpy.linspace(ymin, ymax, npix_y)
1635
1636        #set x_bins and y_bins
1637        self.x_bins = x_bins
1638        self.y_bins = y_bins
1639
1640    def _fillup_pixels(self, image=None, weights=None):
1641        """
1642        Fill z values of the empty cells of 2d image matrix
1643        with the average over up-to next nearest neighbor points
1644
1645        :param image: (2d matrix with some zi = None)
1646
1647        :return: image (2d array )
1648
1649        :TODO: Find better way to do for-loop below
1650
1651        """
1652        # No image matrix given
1653        if image == None or numpy.ndim(image) != 2 \
1654                or numpy.isfinite(image).all() \
1655                or weights == None:
1656            return image
1657        # Get bin size in y and x directions
1658        len_y = len(image)
1659        len_x = len(image[1])
1660        temp_image = numpy.zeros([len_y, len_x])
1661        weit = numpy.zeros([len_y, len_x])
1662        # do for-loop for all pixels
1663        for n_y in range(len(image)):
1664            for n_x in range(len(image[1])):
1665                # find only null pixels
1666                if weights[n_y][n_x] > 0 or numpy.isfinite(image[n_y][n_x]):
1667                    continue
1668                else:
1669                    # find 4 nearest neighbors
1670                    # check where or not it is at the corner
1671                    if n_y != 0 and numpy.isfinite(image[n_y - 1][n_x]):
1672                        temp_image[n_y][n_x] += image[n_y - 1][n_x]
1673                        weit[n_y][n_x] += 1
1674                    if n_x != 0 and numpy.isfinite(image[n_y][n_x - 1]):
1675                        temp_image[n_y][n_x] += image[n_y][n_x - 1]
1676                        weit[n_y][n_x] += 1
1677                    if n_y != len_y - 1 and numpy.isfinite(image[n_y + 1][n_x]):
1678                        temp_image[n_y][n_x] += image[n_y + 1][n_x]
1679                        weit[n_y][n_x] += 1
1680                    if n_x != len_x - 1 and numpy.isfinite(image[n_y][n_x + 1]):
1681                        temp_image[n_y][n_x] += image[n_y][n_x + 1]
1682                        weit[n_y][n_x] += 1
1683                    # go 4 next nearest neighbors when no non-zero
1684                    # neighbor exists
1685                    if n_y != 0 and n_x != 0 and\
1686                         numpy.isfinite(image[n_y - 1][n_x - 1]):
1687                        temp_image[n_y][n_x] += image[n_y - 1][n_x - 1]
1688                        weit[n_y][n_x] += 1
1689                    if n_y != len_y - 1 and n_x != 0 and \
1690                        numpy.isfinite(image[n_y + 1][n_x - 1]):
1691                        temp_image[n_y][n_x] += image[n_y + 1][n_x - 1]
1692                        weit[n_y][n_x] += 1
1693                    if n_y != len_y and n_x != len_x - 1 and \
1694                        numpy.isfinite(image[n_y - 1][n_x + 1]):
1695                        temp_image[n_y][n_x] += image[n_y - 1][n_x + 1]
1696                        weit[n_y][n_x] += 1
1697                    if n_y != len_y - 1 and n_x != len_x - 1 and \
1698                        numpy.isfinite(image[n_y + 1][n_x + 1]):
1699                        temp_image[n_y][n_x] += image[n_y + 1][n_x + 1]
1700                        weit[n_y][n_x] += 1
1701
1702        # get it normalized
1703        ind = (weit > 0)
1704        image[ind] = temp_image[ind] / weit[ind]
1705
1706        return image
1707
1708    def curve(self, x, y, dy=None, color=0, symbol=0, label=None):
1709        """Draw a line on a graph, possibly with confidence intervals."""
1710        c = self._color(color)
1711        self.subplot.set_yscale('linear')
1712        self.subplot.set_xscale('linear')
1713
1714        self.subplot.plot(x, y, color=c, marker='',
1715                          linestyle='-', label=label)
1716        self.subplot.set_yscale(self.yscale)
1717        self.subplot.set_xscale(self.xscale)
1718
1719    def _color(self, c):
1720        """Return a particular colour"""
1721        return self.colorlist[c % len(self.colorlist)]
1722
1723    def _symbol(self, s):
1724        """Return a particular symbol"""
1725        return self.symbollist[s % len(self.symbollist)]
1726
1727    def _replot(self, remove_fit=False):
1728        """
1729        Rescale the plottables according to the latest
1730        user selection and update the plot
1731
1732        :param remove_fit: Fit line will be removed if True
1733
1734        """
1735        self.graph.reset_scale()
1736        self._onEVT_FUNC_PROPERTY(remove_fit=remove_fit)
1737        #TODO: Why do we have to have the following line?
1738        self.fit_result.reset_view()
1739        self.graph.render(self)
1740        self.subplot.figure.canvas.draw_idle()
1741
1742    def _onEVT_FUNC_PROPERTY(self, remove_fit=True, show=True):
1743        """
1744        Receive the x and y transformation from myDialog,
1745        Transforms x and y in View
1746        and set the scale
1747        """
1748        # The logic should be in the right order
1749        # Delete first, and then get the whole list...
1750        if remove_fit:
1751            self.graph.delete(self.fit_result)
1752        self.ly = None
1753        self.q_ctrl = None
1754        list = self.graph.returnPlottable()
1755        # Changing the scale might be incompatible with
1756        # currently displayed data (for instance, going
1757        # from ln to log when all plotted values have
1758        # negative natural logs).
1759        # Go linear and only change the scale at the end.
1760        self.set_xscale("linear")
1761        self.set_yscale("linear")
1762        _xscale = 'linear'
1763        _yscale = 'linear'
1764        for item in list:
1765            item.setLabel(self.xLabel, self.yLabel)
1766            # control axis labels from the panel itself
1767            yname, yunits = item.get_yaxis()
1768            if self.yaxis_label != None:
1769                yname = self.yaxis_label
1770                yunits = self.yaxis_unit
1771            else:
1772                self.yaxis_label = yname
1773                self.yaxis_unit = yunits
1774            xname, xunits = item.get_xaxis()
1775            if self.xaxis_label != None:
1776                xname = self.xaxis_label
1777                xunits = self.xaxis_unit
1778            else:
1779                self.xaxis_label = xname
1780                self.xaxis_unit = xunits
1781            # Goes through all possible scales
1782            if self.xLabel == "x":
1783                item.transformX(transform.toX, transform.errToX)
1784                self.graph._xaxis_transformed("%s" % xname, "%s" % xunits)
1785            if self.xLabel == "x^(2)":
1786                item.transformX(transform.toX2, transform.errToX2)
1787                xunits = convert_unit(2, xunits)
1788                self.graph._xaxis_transformed("%s^{2}" % xname, "%s" % xunits)
1789            if self.xLabel == "x^(4)":
1790                item.transformX(transform.toX4, transform.errToX4)
1791                xunits = convert_unit(4, xunits)
1792                self.graph._xaxis_transformed("%s^{4}" % xname, "%s" % xunits)
1793            if self.xLabel == "ln(x)":
1794                item.transformX(transform.toLogX, transform.errToLogX)
1795                self.graph._xaxis_transformed("\ln\\ %s" % xname, "%s" % xunits)
1796            if self.xLabel == "log10(x)":
1797                item.transformX(transform.toX_pos, transform.errToX_pos)
1798                _xscale = 'log'
1799                self.graph._xaxis_transformed("%s" % xname, "%s" % xunits)
1800            if self.xLabel == "log10(x^(4))":
1801                item.transformX(transform.toX4, transform.errToX4)
1802                xunits = convert_unit(4, xunits)
1803                self.graph._xaxis_transformed("%s^{4}" % xname, "%s" % xunits)
1804                _xscale = 'log'
1805            if self.yLabel == "ln(y)":
1806                item.transformY(transform.toLogX, transform.errToLogX)
1807                self.graph._yaxis_transformed("\ln\\ %s" % yname, "%s" % yunits)
1808            if self.yLabel == "y":
1809                item.transformY(transform.toX, transform.errToX)
1810                self.graph._yaxis_transformed("%s" % yname, "%s" % yunits)
1811            if self.yLabel == "log10(y)":
1812                item.transformY(transform.toX_pos, transform.errToX_pos)
1813                _yscale = 'log'
1814                self.graph._yaxis_transformed("%s" % yname, "%s" % yunits)
1815            if self.yLabel == "y^(2)":
1816                item.transformY(transform.toX2, transform.errToX2)
1817                yunits = convert_unit(2, yunits)
1818                self.graph._yaxis_transformed("%s^{2}" % yname, "%s" % yunits)
1819            if self.yLabel == "1/y":
1820                item.transformY(transform.toOneOverX, transform.errOneOverX)
1821                yunits = convert_unit(-1, yunits)
1822                self.graph._yaxis_transformed("1/%s" % yname, "%s" % yunits)
1823            if self.yLabel == "y*x^(2)":
1824                item.transformY(transform.toYX2, transform.errToYX2)
1825                xunits = convert_unit(4, self.xaxis_unit)
1826                self.graph._yaxis_transformed("%s \ \ %s^{2}" % (yname, xname),
1827                                              "%s%s" % (yunits, xunits))
1828            if self.yLabel == "y*x^(4)":
1829                item.transformY(transform.toYX4, transform.errToYX4)
1830                xunits = convert_unit(4, self.xaxis_unit)
1831                self.graph._yaxis_transformed("%s \ \ %s^{4}" % (yname, xname),
1832                                              "%s%s" % (yunits, xunits))
1833            if self.yLabel == "1/sqrt(y)":
1834                item.transformY(transform.toOneOverSqrtX,
1835                                transform.errOneOverSqrtX)
1836                yunits = convert_unit(-0.5, yunits)
1837                self.graph._yaxis_transformed("1/\sqrt{%s}" % yname,
1838                                              "%s" % yunits)
1839            if self.yLabel == "ln(y*x)":
1840                item.transformY(transform.toLogXY, transform.errToLogXY)
1841                self.graph._yaxis_transformed("\ln (%s \ \ %s)" % (yname, xname),
1842                                              "%s%s" % (yunits, self.xaxis_unit))
1843            if self.yLabel == "ln(y*x^(2))":
1844                item.transformY(transform.toLogYX2, transform.errToLogYX2)
1845                xunits = convert_unit(2, self.xaxis_unit)
1846                self.graph._yaxis_transformed("\ln (%s \ \ %s^{2})" % (yname, xname),
1847                                              "%s%s" % (yunits, xunits))
1848            if self.yLabel == "ln(y*x^(4))":
1849                item.transformY(transform.toLogYX4, transform.errToLogYX4)
1850                xunits = convert_unit(4, self.xaxis_unit)
1851                self.graph._yaxis_transformed("\ln (%s \ \ %s^{4})" % (yname, xname),
1852                                              "%s%s" % (yunits, xunits))
1853            if self.yLabel == "log10(y*x^(4))":
1854                item.transformY(transform.toYX4, transform.errToYX4)
1855                xunits = convert_unit(4, self.xaxis_unit)
1856                _yscale = 'log'
1857                self.graph._yaxis_transformed("%s \ \ %s^{4}" % (yname, xname),
1858                                              "%s%s" % (yunits, xunits))
1859            item.transformView()
1860
1861        # set new label and units
1862        yname = self.graph.prop["ylabel"]
1863        yunits = ''
1864        xname = self.graph.prop["xlabel"]
1865        xunits = ''
1866
1867        self.resetFitView()
1868        self.prevXtrans = self.xLabel
1869        self.prevYtrans = self.yLabel
1870        self.graph.render(self)
1871        self.set_xscale(_xscale)
1872        self.set_yscale(_yscale)
1873
1874        self.xaxis(xname, xunits, self.xaxis_font,
1875                   self.xaxis_color, self.xaxis_tick)
1876        self.yaxis(yname, yunits, self.yaxis_font,
1877                   self.yaxis_color, self.yaxis_tick)
1878        self.subplot.texts = self.textList
1879        if show:
1880            self.subplot.figure.canvas.draw_idle()
1881
1882    def onFitDisplay(self, tempx, tempy, xminView,
1883                     xmaxView, xmin, xmax, func):
1884        """
1885        Add a new plottable into the graph .In this case this plottable
1886        will be used to fit some data
1887
1888        :param tempx: The x data of fit line
1889        :param tempy: The y data of fit line
1890        :param xminView: the lower bound of fitting range
1891        :param xminView: the upper bound of  fitting range
1892        :param xmin: the lowest value of data to fit to the line
1893        :param xmax: the highest value of data to fit to the line
1894
1895        """
1896        # Saving value to redisplay in Fit Dialog when it is opened again
1897        self.Avalue, self.Bvalue, self.ErrAvalue, \
1898                      self.ErrBvalue, self.Chivalue = func
1899        self.xminView = xminView
1900        self.xmaxView = xmaxView
1901        self.xmin = xmin
1902        self.xmax = xmax
1903        #In case need to change the range of data plotted
1904        for item in self.graph.returnPlottable():
1905            item.onFitRange(None, None)
1906        # Create new data plottable with result
1907        self.fit_result.x = []
1908        self.fit_result.y = []
1909        self.fit_result.x = tempx
1910        self.fit_result.y = tempy
1911        self.fit_result.dx = None
1912        self.fit_result.dy = None
1913        #Load the view with the new values
1914        self.fit_result.reset_view()
1915        # Add the new plottable to the graph
1916        self.graph.add(self.fit_result)
1917        self.graph.render(self)
1918        self._offset_graph()
1919        self.subplot.figure.canvas.draw_idle()
1920
1921    def onChangeCaption(self, event):
1922        """
1923        """
1924        if self.parent == None:
1925            return
1926        # get current caption
1927        old_caption = self.window_caption
1928        # Get new caption dialog
1929        dial = LabelDialog(None, -1, 'Modify Window Title', old_caption)
1930        if dial.ShowModal() == wx.ID_OK:
1931            new_caption = dial.getText()
1932
1933            # send to guiframe to change the panel caption
1934            caption = self.parent.on_change_caption(self.window_name,
1935                                                    old_caption, new_caption)
1936
1937            # also set new caption in plot_panels list
1938            self.parent.plot_panels[self.uid].window_caption = caption
1939            # set new caption
1940            self.window_caption = caption
1941
1942        dial.Destroy()
1943
1944    def onResetGraph(self, event):
1945        """
1946        Reset the graph by plotting the full range of data
1947        """
1948        for item in self.graph.returnPlottable():
1949            item.onReset()
1950        self.graph.render(self)
1951        self._onEVT_FUNC_PROPERTY(False)
1952        self.is_zoomed = False
1953        self.toolbar.update()
1954
1955    def onPrint(self, event=None):
1956        self.toolbar.print_figure(event)
1957
1958    def onPrinterSetup(self, event=None):
1959        """
1960        """
1961        self.canvas.Printer_Setup(event=event)
1962        self.Update()
1963
1964    def onPrinterPreview(self, event=None):
1965        """
1966        Matplotlib camvas can no longer print itself.  Thus need to do
1967        everything ourselves: need to create a printpreview frame to to
1968        see the preview but needs a parent frame object.  Also needs a
1969        printout object (just as any printing task).
1970        """
1971        try:
1972            #check if parent is a frame.  If not keep getting the higher
1973            #parent till we find a frame
1974            _plot = self
1975            while not isinstance(_plot, wx.Frame):
1976                _plot = _plot.GetParent()
1977                assert _plot is not None
1978
1979            #now create the printpeview object
1980            _preview = wx.PrintPreview(PlotPrintout(self.canvas),
1981                                       PlotPrintout(self.canvas))
1982            #and tie it to a printpreview frame then show it
1983            _frame = wx.PreviewFrame(_preview, _plot, "Print Preview", wx.Point(100, 100), wx.Size(600, 650))
1984            _frame.Centre(wx.BOTH)
1985            _frame.Initialize()
1986            _frame.Show(True)
1987        except:
1988            traceback.print_exc()
1989            pass
1990
1991    def OnCopyFigureMenu(self, evt):
1992        """
1993        Copy the current figure to clipboard
1994        """
1995        try:
1996            self.toolbar.copy_figure(self.canvas)
1997        except:
1998            print "Error in copy Image"
1999
2000
2001#---------------------------------------------------------------
2002class NoRepaintCanvas(FigureCanvasWxAgg):
2003    """
2004    We subclass FigureCanvasWxAgg, overriding the _onPaint method, so that
2005    the draw method is only called for the first two paint events. After that,
2006    the canvas will only be redrawn when it is resized.
2007
2008    """
2009    def __init__(self, *args, **kwargs):
2010        """
2011        """
2012        FigureCanvasWxAgg.__init__(self, *args, **kwargs)
2013        self._drawn = 0
2014
2015    def _onPaint(self, evt):
2016        """
2017        Called when wxPaintEvt is generated
2018
2019        """
2020        if not self._isRealized:
2021            self.realize()
2022        if self._drawn < 2:
2023            self.draw(repaint=False)
2024            self._drawn += 1
2025        self.gui_repaint(drawDC=wx.PaintDC(self))
Note: See TracBrowser for help on using the repository browser.