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

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 f468791 was 5777106, checked in by Mathieu Doucet <doucetm@…>, 11 years ago

Moving things around. Will definitely not build.

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