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

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

Added porod plot scales shortcut

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