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

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

minor fixes in 2d image get_bin

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