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

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

added Rg etc on fitdialog

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