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

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

a bit additions for workshop

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