source: sasview/src/sas/sasgui/plottools/PlotPanel.py @ 09d6719

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 09d6719 was 09d6719, checked in by lewis, 8 years ago

Add Kratky option to plot scale. Closes #205

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