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

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

mdi frames for main applications

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