source: sasview/plottools/src/danse/common/plottools/PlotPanel.py @ d2843a9

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 d2843a9 was 52e0f2d, checked in by Jae Cho <jhjcho@…>, 12 years ago

trying to fix the problem in plots on MAC

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