source: sasview/src/sans/plottools/PlotPanel.py @ eddb6ec

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 eddb6ec was eddb6ec, checked in by Tobias Richter <tobias.richter@…>, 11 years ago

refs #57 Clean up warnings in Python code

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