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

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

set 2D z log scale from natural log to 10 log

  • Property mode set to 100644
File size: 74.5 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._SetInitialSize() 
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 impleeted
327   
328    def schedule_full_draw(self, func='append'):   
329        """
330        Put self in schedule to full redraw list
331        """
332        pass # Not implemeted
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            cbax = self.subplot.figure.add_axes([0.84,0.2,0.02,0.7])
1486        else:
1487            # clear the previous 2D from memory
1488            self.subplot.figure.clf()
1489            self.subplot.figure.subplots_adjust(left=0.1, right=.8, bottom=.1) 
1490            try:
1491                # mpl >= 1.0.0
1492                ax = self.subplot.figure.gca(projection='3d')
1493                #ax.disable_mouse_rotation()
1494                cbax = self.subplot.figure.add_axes([0.84,0.1,0.02,0.8])
1495            except:
1496                # mpl < 1.0.0
1497                ax =  Axes3D(self.subplot.figure)
1498                cbax = None
1499
1500            X = self.x_bins[0:-1]
1501            Y = self.y_bins[0:-1]
1502            X, Y = numpy.meshgrid(X, Y)
1503   
1504            im = ax.plot_surface(X, Y, output, rstride=1, cstride=1, cmap=cmap,
1505                                   linewidth=0, antialiased=False)
1506            #ax.set_zlim3d(zmin_temp, self.zmax_2D)
1507            #ax.set_frame_on(False)
1508            self.subplot.set_axis_off()   
1509           
1510        if cbax == None:
1511            cb =self.subplot.figure.colorbar(im, shrink=0.6, aspect=20)
1512        else:
1513            cb =self.subplot.figure.colorbar(im, cax=cbax)
1514        cb.update_bruteforce(im)
1515        cb.set_label('$' + self.scale + '$')
1516       
1517        #if self.dimension != 3:
1518        self.figure.canvas.draw_idle()
1519   
1520    def _build_matrix(self):
1521        """
1522        Build a matrix for 2d plot from a vector
1523        Returns a matrix (image) with ~ square binning
1524        Requirement: need 1d array formats of
1525        self.data, self.qx_data, and self.qy_data
1526        where each one corresponds to z, x, or y axis values
1527       
1528        """
1529        # No qx or qy given in a vector format
1530        if self.qx_data == None or self.qy_data == None \
1531                or self.qx_data.ndim != 1 or self.qy_data.ndim != 1:
1532            # do we need deepcopy here?
1533            return copy.deepcopy(self.data)
1534     
1535        # maximum # of loops to fillup_pixels
1536        # otherwise, loop could never stop depending on data
1537        max_loop = 1
1538        # get the x and y_bin arrays.
1539        self._get_bins()
1540        # set zero to None
1541       
1542        #Note: Can not use scipy.interpolate.Rbf:
1543        # 'cause too many data points (>10000)<=JHC.
1544        # 1d array to use for weighting the data point averaging
1545        #when they fall into a same bin.         
1546        weights_data  = numpy.ones([self.data.size])
1547        # get histogram of ones w/len(data); this will provide
1548        #the weights of data on each bins
1549        weights, xedges, yedges = numpy.histogram2d(x=self.qy_data, 
1550                                                    y=self.qx_data,
1551                                                bins=[self.y_bins, self.x_bins],
1552                                                    weights=weights_data)
1553        # get histogram of data, all points into a bin in a way of summing
1554        image, xedges, yedges = numpy.histogram2d(x=self.qy_data, 
1555                                                  y=self.qx_data,
1556                                                bins=[self.y_bins, self.x_bins],
1557                                                weights=self.data)
1558        # Now, normalize the image by weights only for weights>1:
1559        # If weight == 1, there is only one data point in the bin so
1560        # that no normalization is required.
1561        image[weights>1] = image[weights>1]/weights[weights>1]
1562        # Set image bins w/o a data point (weight==0) as None (was set to zero
1563        # by histogram2d.)
1564        image[weights==0] = None
1565
1566        # Fill empty bins with 8 nearest neighbors only when at least
1567        #one None point exists
1568        loop = 0
1569       
1570        # do while loop until all vacant bins are filled up up
1571        #to loop = max_loop
1572        while(not(numpy.isfinite(image[weights == 0])).all()):
1573            if loop >= max_loop: # this protects never-ending loop
1574                break
1575            image = self._fillup_pixels(image=image, weights=weights)
1576            loop += 1
1577               
1578        return image
1579   
1580    def _get_bins(self): 
1581        """
1582        get bins
1583        set x_bins and y_bins into self, 1d arrays of the index with
1584        ~ square binning
1585        Requirement: need 1d array formats of
1586        self.qx_data, and self.qy_data
1587        where each one corresponds to  x, or y axis values
1588       
1589        """
1590        # No qx or qy given in a vector format
1591        if self.qx_data == None or self.qy_data == None \
1592                or self.qx_data.ndim != 1 or self.qy_data.ndim != 1:
1593            # do we need deepcopy here?
1594            return copy.deepcopy(self.data)
1595       
1596        # find max and min values of qx and qy
1597        xmax = numpy.max(self.qx_data)
1598        xmin = numpy.min(self.qx_data)
1599        ymax = numpy.max(self.qy_data)
1600        ymin = numpy.min(self.qy_data)
1601       
1602        # calculate the range of qx and qy: this way, it is a little
1603        # more independent
1604        x_size = xmax - xmin
1605        y_size = ymax - ymin
1606       
1607        # estimate the # of pixels on each axes 
1608        npix_y = int(math.floor(math.sqrt(len(self.qy_data))))
1609        npix_x = int(math.floor(len(self.qy_data) / npix_y))
1610
1611        # bin size: x- & y-directions     
1612        xstep = x_size / (npix_x - 1)
1613        ystep = y_size / (npix_y - 1)
1614
1615        # max and min taking account of the bin sizes
1616        xmax = xmax + xstep / 2
1617        xmin = xmin - xstep / 2
1618        ymax = ymax + ystep / 2
1619        ymin = ymin - ystep / 2
1620       
1621        # store x and y bin centers in q space
1622        x_bins = numpy.arange(xmin, xmax + xstep / 10, xstep)
1623        y_bins = numpy.arange(ymin, ymax + ystep / 10, ystep) 
1624     
1625        #set x_bins and y_bins
1626        self.x_bins = x_bins
1627        self.y_bins = y_bins
1628
1629    def _fillup_pixels(self, image=None, weights=None): 
1630        """
1631        Fill z values of the empty cells of 2d image matrix
1632        with the average over up-to next nearest neighbor points
1633       
1634        :param image: (2d matrix with some zi = None)
1635       
1636        :return: image (2d array )
1637       
1638        :TODO: Find better way to do for-loop below
1639       
1640        """
1641        # No image matrix given
1642        if image == None or numpy.ndim(image) != 2 \
1643                or numpy.isfinite(image).all() \
1644                or weights == None:
1645            return image
1646        # Get bin size in y and x directions
1647        len_y = len(image)
1648        len_x = len(image[1])
1649        temp_image = numpy.zeros([len_y, len_x])
1650        weit = numpy.zeros([len_y, len_x])
1651        # do for-loop for all pixels
1652        for n_y in range(len(image)):
1653            for n_x in range(len(image[1])):
1654                # find only null pixels
1655                if weights[n_y][n_x] > 0 or numpy.isfinite(image[n_y][n_x]):
1656                    continue
1657                else:
1658                    # find 4 nearest neighbors
1659                    # check where or not it is at the corner
1660                    if n_y != 0 and numpy.isfinite(image[n_y-1][n_x]):
1661                        temp_image[n_y][n_x] += image[n_y-1][n_x]
1662                        weit[n_y][n_x] += 1
1663                    if n_x != 0 and numpy.isfinite(image[n_y][n_x-1]):
1664                        temp_image[n_y][n_x] += image[n_y][n_x-1]
1665                        weit[n_y][n_x] += 1
1666                    if n_y != len_y -1 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 != len_x -1 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                    # go 4 next nearest neighbors when no non-zero
1673                    # neighbor exists
1674                    #if weight[n_y][n_x] == 0:
1675                    if n_y != 0 and n_x != 0 and\
1676                         numpy.isfinite(image[n_y-1][n_x-1]):
1677                        temp_image[n_y][n_x] += image[n_y-1][n_x-1]
1678                        weit[n_y][n_x] += 1
1679                    if n_y != len_y -1 and n_x != 0 and \
1680                        numpy.isfinite(image[n_y+1][n_x-1]):
1681                        temp_image[n_y][n_x] += image[n_y+1][n_x-1]
1682                        weit[n_y][n_x] += 1
1683                    if n_y != len_y and n_x != len_x -1 and \
1684                        numpy.isfinite(image[n_y-1][n_x+1]):
1685                        temp_image[n_y][n_x] += image[n_y-1][n_x+1] 
1686                        weit[n_y][n_x] += 1
1687                    if n_y != len_y -1 and n_x != len_x -1 and \
1688                        numpy.isfinite(image[n_y+1][n_x+1]):
1689                        temp_image[n_y][n_x] += image[n_y+1][n_x+1]
1690                        weit[n_y][n_x] += 1
1691
1692        # get it normalized
1693        ind = (weit > 0)
1694        image[ind] = temp_image[ind] / weit[ind]
1695       
1696        return image
1697         
1698    def curve(self, x, y, dy=None, color=0, symbol=0, label=None):
1699        """Draw a line on a graph, possibly with confidence intervals."""
1700        c = self._color(color)
1701        self.subplot.set_yscale('linear')
1702        self.subplot.set_xscale('linear')
1703       
1704        hlist = self.subplot.plot(x, y, color=c, marker='', 
1705                                  linestyle='-', label=label)
1706        self.subplot.set_yscale(self.yscale)
1707        self.subplot.set_xscale(self.xscale)
1708
1709    def _color(self, c):
1710        """Return a particular colour"""
1711        return self.colorlist[c%len(self.colorlist)]
1712
1713    def _symbol(self, s):
1714        """Return a particular symbol"""
1715        return self.symbollist[s%len(self.symbollist)]
1716   
1717    def _replot(self, remove_fit=False):
1718        """
1719        Rescale the plottables according to the latest
1720        user selection and update the plot
1721       
1722        :param remove_fit: Fit line will be removed if True
1723       
1724        """
1725        self.graph.reset_scale()
1726        self._onEVT_FUNC_PROPERTY(remove_fit=remove_fit)
1727        #TODO: Why do we have to have the following line?
1728        self.fit_result.reset_view()
1729        self.graph.render(self)
1730        self.subplot.figure.canvas.draw_idle()
1731   
1732    def _onEVT_FUNC_PROPERTY(self, remove_fit=True):
1733        """
1734        Receive the x and y transformation from myDialog,
1735        Transforms x and y in View
1736        and set the scale   
1737        """ 
1738        # The logic should be in the right order
1739        # Delete first, and then get the whole list...
1740        if remove_fit:
1741            self.graph.delete(self.fit_result)
1742           
1743        list = []
1744        list = self.graph.returnPlottable()
1745        # Changing the scale might be incompatible with
1746        # currently displayed data (for instance, going
1747        # from ln to log when all plotted values have
1748        # negative natural logs).
1749        # Go linear and only change the scale at the end.
1750        self.set_xscale("linear")
1751        self.set_yscale("linear")
1752        _xscale = 'linear'
1753        _yscale = 'linear'
1754        for item in list:
1755            item.setLabel(self.xLabel, self.yLabel)
1756           
1757            # control axis labels from the panel itself
1758            yname, yunits = item.get_yaxis()
1759            if self.yaxis_label != None:
1760                yname = self.yaxis_label
1761                yunits = self.yaxis_unit
1762            else:
1763                self.yaxis_label = yname
1764                self.yaxis_unit = yunits
1765            xname, xunits = item.get_xaxis()
1766            if self.xaxis_label != None:
1767                xname = self.xaxis_label
1768                xunits = self.xaxis_unit
1769            else:
1770                self.xaxis_label = xname
1771                self.xaxis_unit = xunits
1772            # Goes through all possible scales   
1773            if(self.xLabel == "x"):
1774                item.transformX(transform.toX, transform.errToX)
1775                self.graph._xaxis_transformed("%s" % xname, "%s" % xunits)
1776            if(self.xLabel == "x^(2)"):
1777                item.transformX(transform.toX2, transform.errToX2)
1778                xunits = convertUnit(2,xunits) 
1779                self.graph._xaxis_transformed("%s^{2}" % xname, "%s" % xunits)
1780            if(self.xLabel == "x^(4)"):
1781                item.transformX(transform.toX4, transform.errToX4)
1782                xunits = convertUnit(4,xunits) 
1783                self.graph._xaxis_transformed("%s^{4}" % xname, "%s" % xunits)
1784            if(self.xLabel == "ln(x)"):
1785                item.transformX(transform.toLogX,transform.errToLogX)
1786                self.graph._xaxis_transformed("\ln\\ %s" % xname, "%s" % xunits) 
1787            if(self.xLabel == "log10(x)"):
1788                item.transformX(transform.toX_pos, transform.errToX_pos)
1789                _xscale = 'log'
1790                self.graph._xaxis_transformed("%s" % xname, "%s" % xunits)
1791            if(self.xLabel == "log10(x^(4))"):
1792                item.transformX(transform.toX4, transform.errToX4)
1793                xunits = convertUnit(4,xunits) 
1794                self.graph._xaxis_transformed("%s^{4}" % xname, "%s" % xunits)
1795                _xscale = 'log'
1796            if(self.yLabel == "ln(y)"):
1797                item.transformY(transform.toLogX, transform.errToLogX)
1798                self.graph._yaxis_transformed("\ln\\ %s" % yname, "%s" % yunits)
1799            if(self.yLabel == "y"):
1800                item.transformY(transform.toX, transform.errToX)
1801                self.graph._yaxis_transformed("%s" % yname, "%s" % yunits)
1802            if(self.yLabel == "log10(y)"): 
1803                item.transformY(transform.toX_pos, transform.errToX_pos)
1804                _yscale = 'log' 
1805                self.graph._yaxis_transformed("%s" % yname, "%s" % yunits)
1806            if(self.yLabel == "y^(2)"):
1807                item.transformY(transform.toX2, transform.errToX2)   
1808                yunits = convertUnit(2, yunits) 
1809                self.graph._yaxis_transformed("%s^{2}" % yname, "%s" % yunits)
1810            if(self.yLabel == "1/y"):
1811                item.transformY(transform.toOneOverX, transform.errOneOverX)
1812                yunits = convertUnit(-1, yunits)
1813                self.graph._yaxis_transformed("1/%s" % yname, "%s" % yunits)
1814            if(self.yLabel == "y*x^(4)"):
1815                item.transformY(transform.toYX4, transform.errToYX4)
1816                xunits = convertUnit(4, self.xaxis_unit) 
1817                self.graph._yaxis_transformed("%s \ \ %s^{4}" % (yname,xname), 
1818                                               "%s%s" % (yunits,xunits))
1819            if(self.yLabel == "1/sqrt(y)"):
1820                item.transformY(transform.toOneOverSqrtX,
1821                                transform.errOneOverSqrtX )
1822                yunits = convertUnit(-0.5, yunits)
1823                self.graph._yaxis_transformed("1/\sqrt{%s}" %yname, "%s" % yunits)
1824            if(self.yLabel == "ln(y*x)"):
1825                item.transformY(transform.toLogXY, transform.errToLogXY)
1826                self.graph._yaxis_transformed("\ln (%s \ \ %s)" % (yname,xname),
1827                                                "%s%s" % (yunits, self.xaxis_unit))
1828            if(self.yLabel == "ln(y*x^(2))"):
1829                item.transformY( transform.toLogYX2, transform.errToLogYX2) 
1830                xunits = convertUnit(2, self.xaxis_unit) 
1831                self.graph._yaxis_transformed("\ln (%s \ \ %s^{2})" % (yname,xname), 
1832                                               "%s%s" % (yunits,xunits))
1833            if(self.yLabel == "ln(y*x^(4))"):
1834                item.transformY(transform.toLogYX4, transform.errToLogYX4)
1835                xunits = convertUnit(4, self.xaxis_unit) 
1836                self.graph._yaxis_transformed("\ln (%s \ \ %s^{4})" % (yname,xname), 
1837                                               "%s%s" % (yunits,xunits))
1838            if(self.yLabel == "log10(y*x^(4))"):
1839                item.transformY(transform.toYX4, transform.errToYX4)
1840                xunits = convertUnit(4, self.xaxis_unit) 
1841                _yscale = 'log' 
1842                self.graph._yaxis_transformed("%s \ \ %s^{4}" % (yname,xname), 
1843                                               "%s%s" % (yunits,xunits))
1844            if(self.viewModel == "Guinier lny vs x^(2)"):
1845                item.transformX(transform.toX2, transform.errToX2)
1846                xunits = convertUnit(2, xunits) 
1847                self.graph._xaxis_transformed("%s^{2}" % xname,  "%s" % xunits)
1848                item.transformY(transform.toLogX,transform.errToLogX )
1849                self.graph._yaxis_transformed("\ln\ \ %s" % yname,  "%s" % yunits)
1850            item.transformView()
1851 
1852        # set new label and units
1853        yname = self.graph.prop["ylabel"]
1854        yunits = ''
1855        xname = self.graph.prop["xlabel"]
1856        xunits = ''
1857        #self.yaxis_label = yname
1858        #self.yaxis_unit = yunits
1859        #self.xaxis_label = xname
1860        #self.xaxis_unit = xunits
1861               
1862        self.resetFitView()   
1863        self.prevXtrans = self.xLabel
1864        self.prevYtrans = self.yLabel 
1865        self.graph.render(self)
1866        self.set_xscale(_xscale)
1867        self.set_yscale(_yscale)
1868       
1869        self.xaxis(xname, xunits, self.xaxis_font, 
1870                   self.xaxis_color, self.xaxis_tick)
1871        self.yaxis(yname, yunits, self.yaxis_font, 
1872                   self.yaxis_color, self.yaxis_tick)
1873        self.subplot.texts = self.textList
1874        self.subplot.figure.canvas.draw_idle()
1875       
1876    def onFitDisplay(self, tempx, tempy, xminView, 
1877                     xmaxView, xmin, xmax, func):
1878        """
1879        Add a new plottable into the graph .In this case this plottable
1880        will be used to fit some data
1881       
1882        :param tempx: The x data of fit line
1883        :param tempy: The y data of fit line
1884        :param xminView: the lower bound of fitting range
1885        :param xminView: the upper bound of  fitting range
1886        :param xmin: the lowest value of data to fit to the line
1887        :param xmax: the highest value of data to fit to the line
1888       
1889        """
1890        # Saving value to redisplay in Fit Dialog when it is opened again
1891        self.Avalue, self.Bvalue, self.ErrAvalue, \
1892                      self.ErrBvalue, self.Chivalue = func
1893        self.xminView = xminView
1894        self.xmaxView = xmaxView
1895        self.xmin = xmin
1896        self.xmax = xmax
1897        #In case need to change the range of data plotted
1898        list = []
1899        list = self.graph.returnPlottable()
1900        for item in list:
1901            #item.onFitRange(xminView,xmaxView)
1902            item.onFitRange(None, None)
1903        # Create new data plottable with result
1904        self.fit_result.x = [] 
1905        self.fit_result.y = []
1906        self.fit_result.x = tempx 
1907        self.fit_result.y = tempy     
1908        self.fit_result.dx = None
1909        self.fit_result.dy = None
1910        #Load the view with the new values
1911        self.fit_result.reset_view()
1912        # Add the new plottable to the graph
1913        self.graph.add(self.fit_result) 
1914        self.graph.render(self)
1915        self._offset_graph()
1916        self.subplot.figure.canvas.draw_idle()
1917
1918    def onChangeCaption(self, event):
1919        """
1920        """
1921        if self.parent == None:
1922            return
1923        # get current caption
1924        old_caption = self.window_caption
1925        # Get new caption dialog
1926        dial = LabelDialog(None, -1, 'Modify Window Title', old_caption)
1927        if dial.ShowModal() == wx.ID_OK:
1928            new_caption = dial.getText() 
1929         
1930            # send to guiframe to change the panel caption
1931            caption = self.parent.on_change_caption(self.window_name, 
1932                                                    old_caption, new_caption) 
1933           
1934            # also set new caption in plot_panels list
1935            self.parent.plot_panels[self.uid].window_caption = caption
1936            # set new caption
1937            self.window_caption = caption
1938           
1939        dial.Destroy()
1940         
1941    def onResetGraph(self, event):
1942        """
1943        Reset the graph by plotting the full range of data
1944        """
1945        list = []
1946        list = self.graph.returnPlottable()
1947        for item in list:
1948            item.onReset()
1949        self.graph.render(self)
1950        self._onEVT_FUNC_PROPERTY(False)
1951        if self.is_zoomed:
1952                self.is_zoomed = False
1953        self.toolbar.update()
1954       
1955    def onPrinterSetup(self, event=None):
1956        """
1957        """
1958        self.canvas.Printer_Setup(event=event)
1959        self.Update()
1960
1961    def onPrinterPreview(self, event=None):
1962        """
1963        """
1964        try:
1965            self.canvas.Printer_Preview(event=event)
1966            self.Update()
1967        except:
1968            pass
1969       
1970    def onPrint(self, event=None):
1971        """
1972        """
1973        try:
1974            self.canvas.Printer_Print(event=event)
1975            self.Update()
1976        except:
1977            pass
1978     
1979    def OnCopyFigureMenu(self, evt):
1980        """
1981        Copy the current figure to clipboard
1982        """
1983        try:
1984            CopyImage(self.canvas)
1985        except:
1986            print "Error in copy Image"
1987
1988
1989           
1990#---------------------------------------------------------------       
1991class NoRepaintCanvas(FigureCanvasWxAgg):
1992    """
1993    We subclass FigureCanvasWxAgg, overriding the _onPaint method, so that
1994    the draw method is only called for the first two paint events. After that,
1995    the canvas will only be redrawn when it is resized.
1996   
1997    """
1998    def __init__(self, *args, **kwargs):
1999        """
2000        """
2001        FigureCanvasWxAgg.__init__(self, *args, **kwargs)
2002        self._drawn = 0
2003
2004    def _onPaint(self, evt):
2005        """
2006        Called when wxPaintEvt is generated
2007       
2008        """
2009        if not self._isRealized:
2010            self.realize()
2011        if self._drawn < 2:
2012            self.draw(repaint = False)
2013            self._drawn += 1
2014        self.gui_repaint(drawDC=wx.PaintDC(self))
2015           
2016
Note: See TracBrowser for help on using the repository browser.