source: sasview/src/sas/plottools/PlotPanel.py @ 892a2cc

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 892a2cc was 7e0f9b5, checked in by Ricardo M. Ferraz Leal <ricleal@…>, 10 years ago

String containing only "$$" was crashing the panel display.

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