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

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

fixed a problem with 3d colorbar for mpl<1.0

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