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

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

rotation for only smaller # of pixels

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