source: sasview/src/sas/sasgui/plottools/PlotPanel.py @ 230178b

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 230178b was 16b769b, checked in by butler, 9 years ago

Fixes - copy plot to clipboard was silently failing since July 2015
due to failure to pass canvas argument inside a try except.

  • Property mode set to 100644
File size: 74.6 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            dlg.ShowModal()
649
650    def set_selected_from_menu(self, menu, id):
651        """
652        Set selected_plottable from context menu selection
653
654        :param menu: context menu item
655        :param id: menu item id
656        """
657        if len(self.plots) < 1:
658            return
659        name = menu.GetHelpString(id)
660        for plot in self.plots.values():
661            if plot.name == name:
662                self.graph.selected_plottable = plot.id
663                break
664
665    def linear_plottable_fit(self, plot):
666        """
667            when clicking on linear Fit on context menu, display Fitting Dialog
668
669            :param plot: PlotPanel owning the graph
670
671        """
672        from fitDialog import LinearFit
673        if self._fit_dialog is not None:
674            return
675        self._fit_dialog = LinearFit(None, plot, self.onFitDisplay,
676                                     self.returnTrans, -1, 'Linear Fit')
677        # Set the zoom area
678        if self._scale_xhi is not None and self._scale_xlo is not None:
679            self._fit_dialog.set_fit_region(self._scale_xlo, self._scale_xhi)
680        # Register the close event
681        self._fit_dialog.register_close(self._linear_fit_close)
682        # Show a non-model dialog
683        self._fit_dialog.Show()
684
685    def _linear_fit_close(self):
686        """
687        A fit dialog was closed
688        """
689        self._fit_dialog = None
690
691    def _onProperties(self, event):
692        """
693        when clicking on Properties on context menu ,
694        The Property dialog is displayed
695        The user selects a transformation for x or y value and
696        a new plot is displayed
697        """
698        if self._fit_dialog is not None:
699            self._fit_dialog.Destroy()
700            self._fit_dialog = None
701        plot_list = self.graph.returnPlottable()
702        if len(plot_list.keys()) > 0:
703            first_item = plot_list.keys()[0]
704            if first_item.x != []:
705                from PropertyDialog import Properties
706                dial = Properties(self, -1, 'Properties')
707                dial.setValues(self.prevXtrans, self.prevYtrans, self.viewModel)
708                if dial.ShowModal() == wx.ID_OK:
709                    self.xLabel, self.yLabel, self.viewModel = dial.getValues()
710                    if self.viewModel == "Linear y vs x":
711                        self.xLabel = "x"
712                        self.yLabel = "y"
713                        self.viewModel = "--"
714                        dial.setValues(self.xLabel, self.yLabel, self.viewModel)
715                    if self.viewModel == "Guinier lny vs x^(2)":
716                        self.xLabel = "x^(2)"
717                        self.yLabel = "ln(y)"
718                        self.viewModel = "--"
719                        dial.setValues(self.xLabel, self.yLabel, self.viewModel)
720                    if self.viewModel == "XS Guinier ln(y*x) vs x^(2)":
721                        self.xLabel = "x^(2)"
722                        self.yLabel = "ln(y*x)"
723                        self.viewModel = "--"
724                        dial.setValues(self.xLabel, self.yLabel, self.viewModel)
725                    if self.viewModel == "Porod y*x^(4) vs x^(4)":
726                        self.xLabel = "x^(4)"
727                        self.yLabel = "y*x^(4)"
728                        self.viewModel = "--"
729                        dial.setValues(self.xLabel, self.yLabel, self.viewModel)
730                    self._onEVT_FUNC_PROPERTY()
731                dial.Destroy()
732
733    def set_yscale(self, scale='linear'):
734        """
735        Set the scale on Y-axis
736
737        :param scale: the scale of y-axis
738
739        """
740        self.subplot.set_yscale(scale, nonposy='clip')
741        self.yscale = scale
742
743    def get_yscale(self):
744        """
745
746        :return: Y-axis scale
747
748        """
749        return self.yscale
750
751    def set_xscale(self, scale='linear'):
752        """
753        Set the scale on x-axis
754
755        :param scale: the scale of x-axis
756
757        """
758        self.subplot.set_xscale(scale)
759        self.xscale = scale
760
761    def get_xscale(self):
762        """
763
764        :return: x-axis scale
765
766        """
767        return self.xscale
768
769    def SetColor(self, rgbtuple):
770        """
771        Set figure and canvas colours to be the same
772
773        """
774        if not rgbtuple:
775            rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get()
776        col = [c / 255.0 for c in rgbtuple]
777        self.figure.set_facecolor(col)
778        self.figure.set_edgecolor(self.color)
779        self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple))
780
781    def _onSize(self, event):
782        """
783        """
784        self._resizeflag = True
785
786    def _onIdle(self, evt):
787        """
788        """
789        if self._resizeflag:
790            self._resizeflag = False
791            self._SetSize()
792            self.draw()
793
794    def _SetSize(self, pixels=None):
795        """
796        This method can be called to force the Plot to be a desired size,
797         which defaults to the ClientSize of the panel
798
799        """
800        if not pixels:
801            pixels = tuple(self.GetClientSize())
802        self.canvas.SetSize(pixels)
803        self.figure.set_size_inches(float(pixels[0]) / self.figure.get_dpi(),
804                                    float(pixels[1]) / self.figure.get_dpi())
805
806    def draw(self):
807        """
808        Where the actual drawing happens
809
810        """
811        self.figure.canvas.draw_idle()
812
813    def legend_picker(self, legend, event):
814        """
815            Pick up the legend patch
816        """
817        return self.legend.legendPatch.contains(event)
818
819    def get_loc_label(self):
820        """
821        Associates label to a specific legend location
822        """
823        _labels = {}
824        i = 0
825        _labels['best'] = i
826        i += 1
827        _labels['upper right'] = i
828        i += 1
829        _labels['upper left'] = i
830        i += 1
831        _labels['lower left'] = i
832        i += 1
833        _labels['lower right'] = i
834        i += 1
835        _labels['right'] = i
836        i += 1
837        _labels['center left'] = i
838        i += 1
839        _labels['center right'] = i
840        i += 1
841        _labels['lower center'] = i
842        i += 1
843        _labels['upper center'] = i
844        i += 1
845        _labels['center'] = i
846        return _labels
847
848    def onSaveImage(self, evt):
849        """
850        Implement save image
851        """
852        self.toolbar.save_figure(evt)
853
854    def onContextMenu(self, event):
855        """
856        Default context menu for a plot panel
857
858        """
859        # Slicer plot popup menu
860        wx_id = wx.NewId()
861        slicerpop = wx.Menu()
862        slicerpop.Append(wx_id, '&Save image', 'Save image as PNG')
863        wx.EVT_MENU(self, wx_id, self.onSaveImage)
864
865        wx_id = wx.NewId()
866        slicerpop.Append(wx_id, '&Printer setup', 'Set image size')
867        wx.EVT_MENU(self, wx_id, self.onPrinterSetup)
868
869        wx_id = wx.NewId()
870        slicerpop.Append(wx_id, '&Print image', 'Print image ')
871        wx.EVT_MENU(self, wx_id, self.onPrint)
872
873        wx_id = wx.NewId()
874        slicerpop.Append(wx_id, '&Copy', 'Copy to the clipboard')
875        wx.EVT_MENU(self, wx_id, self.OnCopyFigureMenu)
876
877        wx_id = wx.NewId()
878        slicerpop.AppendSeparator()
879        slicerpop.Append(wx_id, '&Properties')
880        wx.EVT_MENU(self, wx_id, self._onProperties)
881
882        wx_id = wx.NewId()
883        slicerpop.AppendSeparator()
884        slicerpop.Append(wx_id, '&Linear Fit')
885        wx.EVT_MENU(self, wx_id, self.onFitting)
886
887        wx_id = wx.NewId()
888        slicerpop.AppendSeparator()
889        slicerpop.Append(wx_id, '&Toggle Legend On/Off', 'Toggle Legend On/Off')
890        wx.EVT_MENU(self, wx_id, self.onLegend)
891
892        loc_menu = wx.Menu()
893        for label in self._loc_labels:
894            wx_id = wx.NewId()
895            loc_menu.Append(wx_id, str(label), str(label))
896            wx.EVT_MENU(self, wx_id, self.onChangeLegendLoc)
897        wx_id = wx.NewId()
898        slicerpop.AppendMenu(wx_id, '&Modify Legend Location', loc_menu)
899
900        wx_id = wx.NewId()
901        slicerpop.Append(wx_id, '&Modify Y Axis Label')
902        wx.EVT_MENU(self, wx_id, self._on_yaxis_label)
903        wx_id = wx.NewId()
904        slicerpop.Append(wx_id, '&Modify X Axis Label')
905        wx.EVT_MENU(self, wx_id, self._on_xaxis_label)
906
907        try:
908            # mouse event
909            pos_evt = event.GetPosition()
910            pos = self.ScreenToClient(pos_evt)
911        except:
912            # toolbar event
913            pos_x, pos_y = self.toolbar.GetPositionTuple()
914            pos = (pos_x, pos_y + 5)
915
916        self.PopupMenu(slicerpop, pos)
917
918    def onToolContextMenu(self, event):
919        """
920        ContextMenu from toolbar
921
922        :param event: toolbar event
923        """
924        # reset postion
925        self.position = None
926        if self.graph.selected_plottable != None:
927            self.graph.selected_plottable = None
928
929        self.onContextMenu(event)
930
931# modified kieranrcampbell ILL june2012
932    def onLegend(self, legOnOff):
933        """
934        Toggles whether legend is visible/not visible
935        """
936        self.legend_on = legOnOff
937        if not self.legend_on:
938            for ax in self.axes:
939                self.remove_legend(ax)
940        else:
941            # sort them by labels
942            handles, labels = self.subplot.get_legend_handles_labels()
943            hl = sorted(zip(handles, labels),
944                        key=operator.itemgetter(1))
945            handles2, labels2 = zip(*hl)
946            self.line_collections_list = handles2
947            self.legend = self.subplot.legend(handles2, labels2,
948                                              prop=FontProperties(size=10),
949                                              loc=self.legendLoc)
950            if self.legend != None:
951                self.legend.set_picker(self.legend_picker)
952                self.legend.set_axes(self.subplot)
953                self.legend.set_zorder(20)
954
955        self.subplot.figure.canvas.draw_idle()
956
957    def onChangeLegendLoc(self, event):
958        """
959        Changes legend loc based on user input
960        """
961        menu = event.GetEventObject()
962        label = menu.GetLabel(event.GetId())
963        self.ChangeLegendLoc(label)
964
965    def ChangeLegendLoc(self, label):
966        """
967        Changes legend loc based on user input
968        """
969        self.legendLoc = label
970        self.legend_pos_loc = None
971        # sort them by labels
972        handles, labels = self.subplot.get_legend_handles_labels()
973        hl = sorted(zip(handles, labels),
974                    key=operator.itemgetter(1))
975        handles2, labels2 = zip(*hl)
976        self.line_collections_list = handles2
977        self.legend = self.subplot.legend(handles2, labels2,
978                                          prop=FontProperties(size=10),
979                                          loc=self.legendLoc)
980        if self.legend != None:
981            self.legend.set_picker(self.legend_picker)
982            self.legend.set_axes(self.subplot)
983            self.legend.set_zorder(20)
984        self.subplot.figure.canvas.draw_idle()
985
986    def remove_legend(self, ax=None):
987        """
988        Remove legend for ax or the current axes.
989        """
990        from pylab import gca
991        if ax is None:
992            ax = gca()
993        ax.legend_ = None
994
995    def _on_addtext(self, event):
996        """
997        Allows you to add text to the plot
998        """
999        pos_x = 0
1000        pos_y = 0
1001        if self.position != None:
1002            pos_x, pos_y = self.position
1003        else:
1004            pos_x, pos_y = 0.01, 1.00
1005
1006        textdial = TextDialog(None, -1, 'Add Custom Text')
1007        if textdial.ShowModal() == wx.ID_OK:
1008            try:
1009                FONT = FontProperties()
1010                label = textdial.getText()
1011                xpos = pos_x
1012                ypos = pos_y
1013                font = FONT.copy()
1014                font.set_size(textdial.getSize())
1015                font.set_family(textdial.getFamily())
1016                font.set_style(textdial.getStyle())
1017                font.set_weight(textdial.getWeight())
1018                colour = textdial.getColor()
1019                if len(label) > 0 and xpos > 0 and ypos > 0:
1020                    new_text = self.subplot.text(str(xpos), str(ypos), label,
1021                                                 fontproperties=font,
1022                                                 color=colour)
1023                    self.textList.append(new_text)
1024                    self.subplot.figure.canvas.draw_idle()
1025            except:
1026                if self.parent != None:
1027                    msg = "Add Text: Error. Check your property values..."
1028                    wx.PostEvent(self.parent, StatusEvent(status=msg))
1029                else:
1030                    raise
1031        textdial.Destroy()
1032        #Have a pop up box come up for user to type in the
1033        #text that they want to add...then create text Plottable
1034        #based on this and plot it at user designated coordinates
1035
1036    def onGridOnOff(self, gridon_off):
1037        """
1038        Allows ON/OFF Grid
1039        """
1040        self.grid_on = gridon_off
1041
1042        self.subplot.figure.canvas.draw_idle()
1043
1044    def _on_xaxis_label(self, event):
1045        """
1046        Allows you to add text to the plot
1047        """
1048        xaxis_label, xaxis_unit, xaxis_font, xaxis_color, \
1049                     is_ok, is_tick = self._on_axis_label(axis='x')
1050        if not is_ok:
1051            return
1052
1053        self.xaxis_label = xaxis_label
1054        self.xaxis_unit = xaxis_unit
1055        self.xaxis_font = xaxis_font
1056        self.xaxis_color = xaxis_color
1057        if is_tick:
1058            self.xaxis_tick = xaxis_font
1059
1060        if self.data != None:
1061            # 2D
1062            self.xaxis(self.xaxis_label, self.xaxis_unit, \
1063                        self.xaxis_font, self.xaxis_color, self.xaxis_tick)
1064            self.subplot.figure.canvas.draw_idle()
1065        else:
1066            # 1D
1067            self._check_zoom_plot()
1068
1069    def _check_zoom_plot(self):
1070        """
1071        Check the zoom range and plot (1D only)
1072        """
1073        xlo, xhi = self.subplot.get_xlim()
1074        ylo, yhi = self.subplot.get_ylim()
1075        ## Set the view scale for all plots
1076        self._onEVT_FUNC_PROPERTY(False)
1077        if self.is_zoomed:
1078            # Recover the x,y limits
1079            self.subplot.set_xlim((xlo, xhi))
1080            self.subplot.set_ylim((ylo, yhi))
1081
1082    @property
1083    def is_zoomed(self):
1084        toolbar_zoomed = self.toolbar.GetToolEnabled(self.toolbar.wx_ids['Back'])
1085        return self._is_zoomed or toolbar_zoomed
1086
1087    @is_zoomed.setter
1088    def is_zoomed(self, value):
1089        self._is_zoomed = value
1090
1091    def _on_yaxis_label(self, event):
1092        """
1093        Allows you to add text to the plot
1094        """
1095        yaxis_label, yaxis_unit, yaxis_font, yaxis_color, \
1096                        is_ok, is_tick = self._on_axis_label(axis='y')
1097        if not is_ok:
1098            return
1099
1100        self.yaxis_label = yaxis_label
1101        self.yaxis_unit = yaxis_unit
1102        self.yaxis_font = yaxis_font
1103        self.yaxis_color = yaxis_color
1104        if is_tick:
1105            self.yaxis_tick = yaxis_font
1106
1107        if self.data != None:
1108            # 2D
1109            self.yaxis(self.yaxis_label, self.yaxis_unit, \
1110                        self.yaxis_font, self.yaxis_color, self.yaxis_tick)
1111            self.subplot.figure.canvas.draw_idle()
1112        else:
1113            # 1D
1114            self._check_zoom_plot()
1115
1116    def _on_axis_label(self, axis='x'):
1117        """
1118        Modify axes labels
1119
1120        :param axis: x or y axis [string]
1121        """
1122        is_ok = True
1123        title = 'Modify %s axis label' % axis
1124        font = 'serif'
1125        colour = 'black'
1126        if axis == 'x':
1127            label = self.xaxis_label
1128            unit = self.xaxis_unit
1129        else:
1130            label = self.yaxis_label
1131            unit = self.yaxis_unit
1132        textdial = TextDialog(None, -1, title, label, unit)
1133        if textdial.ShowModal() == wx.ID_OK:
1134            try:
1135                FONT = FontProperties()
1136                font = FONT.copy()
1137                font.set_size(textdial.getSize())
1138                font.set_family(textdial.getFamily())
1139                font.set_style(textdial.getStyle())
1140                font.set_weight(textdial.getWeight())
1141                unit = textdial.getUnit()
1142                colour = textdial.getColor()
1143                is_tick = textdial.getTickLabel()
1144                label_temp = textdial.getText()
1145                if label_temp.count("\%s" % "\\") > 0:
1146                    if self.parent != None:
1147                        msg = "Add Label: Error. Can not use double '\\' "
1148                        msg += "characters..."
1149                        wx.PostEvent(self.parent, StatusEvent(status=msg))
1150                else:
1151                    label = label_temp
1152            except:
1153                if self.parent != None:
1154                    msg = "Add Label: Error. Check your property values..."
1155                    wx.PostEvent(self.parent, StatusEvent(status=msg))
1156                else:
1157                    pass
1158        else:
1159            is_ok = False
1160            is_tick = True
1161        textdial.Destroy()
1162        return label, unit, font, colour, is_ok, is_tick
1163
1164    def _on_removetext(self, event):
1165        """
1166        Removes all text from the plot.
1167        Eventually, add option to remove specific text boxes
1168        """
1169        num_text = len(self.textList)
1170        if num_text < 1:
1171            if self.parent != None:
1172                msg = "Remove Text: Nothing to remove.  "
1173                wx.PostEvent(self.parent, StatusEvent(status=msg))
1174            else:
1175                raise
1176            return
1177        txt = self.textList[num_text - 1]
1178        try:
1179            text_remove = txt.get_text()
1180            txt.remove()
1181            if self.parent != None:
1182                msg = "Removed Text: '%s'. " % text_remove
1183                wx.PostEvent(self.parent, StatusEvent(status=msg))
1184        except:
1185            if self.parent != None:
1186                msg = "Remove Text: Error occurred. "
1187                wx.PostEvent(self.parent, StatusEvent(status=msg))
1188            else:
1189                raise
1190        self.textList.remove(txt)
1191
1192        self.subplot.figure.canvas.draw_idle()
1193
1194    def properties(self, prop):
1195        """
1196        Set some properties of the graph.
1197        The set of properties is not yet determined.
1198
1199        """
1200        # The particulars of how they are stored and manipulated (e.g., do
1201        # we want an inventory internally) is not settled.  I've used a
1202        # property dictionary for now.
1203        #
1204        # How these properties interact with a user defined style file is
1205        # even less clear.
1206
1207        # Properties defined by plot
1208       
1209        # Ricardo:
1210        # A empty label "$$" will prevent the panel from displaying!
1211       
1212        if prop["xlabel"]:
1213            self.subplot.set_xlabel(r"$%s$"%prop["xlabel"])
1214        if prop["ylabel"]:
1215            self.subplot.set_ylabel(r"$%s$"%prop["ylabel"])
1216        self.subplot.set_title(prop["title"])
1217       
1218
1219    def clear(self):
1220        """Reset the plot"""
1221        # TODO: Redraw is brutal.  Render to a backing store and swap in
1222        # TODO: rather than redrawing on the fly.
1223        self.subplot.clear()
1224        self.subplot.hold(True)
1225
1226    def render(self):
1227        """Commit the plot after all objects are drawn"""
1228        # TODO: this is when the backing store should be swapped in.
1229        if self.legend_on:
1230            ax = self.subplot
1231            ax.texts = self.textList
1232            try:
1233                handles, labels = ax.get_legend_handles_labels()
1234                # sort them by labels
1235                hl = sorted(zip(handles, labels),
1236                            key=operator.itemgetter(1))
1237                handles2, labels2 = zip(*hl)
1238                self.line_collections_list = handles2
1239                self.legend = ax.legend(handles2, labels2,
1240                                        prop=FontProperties(size=10),
1241                                        loc=self.legendLoc)
1242                if self.legend != None:
1243                    self.legend.set_picker(self.legend_picker)
1244                    self.legend.set_axes(self.subplot)
1245                    self.legend.set_zorder(20)
1246
1247            except:
1248                self.legend = ax.legend(prop=FontProperties(size=10),
1249                                        loc=self.legendLoc)
1250
1251    def xaxis(self, label, units, font=None, color='black', t_font=None):
1252        """xaxis label and units.
1253
1254        Axis labels know about units.
1255
1256        We need to do this so that we can detect when axes are not
1257        commesurate.  Currently this is ignored other than for formatting
1258        purposes.
1259
1260        """
1261
1262        self.xcolor = color
1263        if units.count("{") > 0 and units.count("$") < 2:
1264            units = '$' + units + '$'
1265        if label.count("{") > 0 and label.count("$") < 2:
1266            label = '$' + label + '$'
1267        if units != "":
1268            label = label + " (" + units + ")"
1269        if font:
1270            self.subplot.set_xlabel(label, fontproperties=font, color=color)
1271            if t_font != None:
1272                for tick in self.subplot.xaxis.get_major_ticks():
1273                    tick.label.set_fontproperties(t_font)
1274                for line in self.subplot.xaxis.get_ticklines():
1275                    size = t_font.get_size()
1276                    line.set_markersize(size / 3)
1277        else:
1278            self.subplot.set_xlabel(label, color=color)
1279        pass
1280
1281    def yaxis(self, label, units, font=None, color='black', t_font=None):
1282        """yaxis label and units."""
1283        self.ycolor = color
1284        if units.count("{") > 0 and units.count("$") < 2:
1285            units = '$' + units + '$'
1286        if label.count("{") > 0 and label.count("$") < 2:
1287            label = '$' + label + '$'
1288        if units != "":
1289            label = label + " (" + units + ")"
1290        if font:
1291            self.subplot.set_ylabel(label, fontproperties=font, color=color)
1292            if t_font != None:
1293                for tick_label in self.subplot.get_yticklabels():
1294                    tick_label.set_fontproperties(t_font)
1295                for line in self.subplot.yaxis.get_ticklines():
1296                    size = t_font.get_size()
1297                    line.set_markersize(size / 3)
1298        else:
1299            self.subplot.set_ylabel(label, color=color)
1300        pass
1301
1302    def _connect_to_xlim(self, callback):
1303        """Bind the xlim change notification to the callback"""
1304        def process_xlim(axes):
1305            lo, hi = subplot.get_xlim()
1306            callback(lo, hi)
1307        self.subplot.callbacks.connect('xlim_changed', process_xlim)
1308
1309    def interactive_points(self, x, y, dx=None, dy=None, name='', color=0,
1310                           symbol=0, markersize=5, zorder=1, id=None,
1311                           label=None, hide_error=False):
1312        """Draw markers with error bars"""
1313        self.subplot.set_yscale('linear')
1314        self.subplot.set_xscale('linear')
1315        if id is None:
1316            id = name
1317        from plottable_interactor import PointInteractor
1318        p = PointInteractor(self, self.subplot, zorder=zorder, id=id)
1319        if p.markersize != None:
1320            markersize = p.markersize
1321        p.points(x, y, dx=dx, dy=dy, color=color, symbol=symbol, zorder=zorder,
1322                 markersize=markersize, label=label, hide_error=hide_error)
1323
1324        self.subplot.set_yscale(self.yscale, nonposy='clip')
1325        self.subplot.set_xscale(self.xscale)
1326
1327    def interactive_curve(self, x, y, dy=None, name='', color=0,
1328                          symbol=0, zorder=1, id=None, label=None):
1329        """Draw markers with error bars"""
1330        self.subplot.set_yscale('linear')
1331        self.subplot.set_xscale('linear')
1332        if id is None:
1333            id = name
1334        from plottable_interactor import PointInteractor
1335        p = PointInteractor(self, self.subplot, zorder=zorder, id=id)
1336        p.curve(x, y, dy=dy, color=color, symbol=symbol, zorder=zorder,
1337                label=label)
1338
1339        self.subplot.set_yscale(self.yscale, nonposy='clip')
1340        self.subplot.set_xscale(self.xscale)
1341
1342    def plottable_selected(self, id):
1343        """
1344        Called to register a plottable as selected
1345        """
1346        #TODO: check that it really is in the list of plottables
1347        self.graph.selected_plottable = id
1348
1349    def points(self, x, y, dx=None, dy=None,
1350               color=0, symbol=0, marker_size=5, label=None,
1351               id=None, hide_error=False):
1352        """Draw markers with error bars"""
1353
1354        # Convert tuple (lo,hi) to array [(x-lo),(hi-x)]
1355        if dx != None and type(dx) == type(()):
1356            dx = nx.vstack((x - dx[0], dx[1] - x)).transpose()
1357        if dy != None and type(dy) == type(()):
1358            dy = nx.vstack((y - dy[0], dy[1] - y)).transpose()
1359        if dx == None and dy == None:
1360            self.subplot.plot(x, y, color=self._color(color),
1361                              marker=self._symbol(symbol),
1362                              markersize=marker_size,
1363                              linestyle='',
1364                              label=label)
1365        else:
1366            col = self._color(color)
1367            if hide_error:
1368                self.subplot.plot(x, y, color=col,
1369                                  marker=self._symbol(symbol),
1370                                  markersize=marker_size,
1371                                  linestyle='',
1372                                  label=label)
1373            else:
1374                self.subplot.errorbar(x, y, yerr=dy, xerr=None,
1375                                      ecolor=col, capsize=2, linestyle='',
1376                                      barsabove=False,
1377                                      mec=col, mfc=col,
1378                                      marker=self._symbol(symbol),
1379                                      markersize=marker_size,
1380                                      lolims=False, uplims=False,
1381                                      xlolims=False, xuplims=False, label=label)
1382
1383        self.subplot.set_yscale(self.yscale, nonposy='clip')
1384        self.subplot.set_xscale(self.xscale)
1385
1386    def _onToggleScale(self, event):
1387        """
1388        toggle axis and replot image
1389
1390        """
1391        zmin_2D_temp = self.zmin_2D
1392        zmax_2D_temp = self.zmax_2D
1393        if self.scale == 'log_{10}':
1394            self.scale = 'linear'
1395            if not self.zmin_2D is None:
1396                zmin_2D_temp = math.pow(10, self.zmin_2D)
1397            if not self.zmax_2D is None:
1398                zmax_2D_temp = math.pow(10, self.zmax_2D)
1399        else:
1400            self.scale = 'log_{10}'
1401            if not self.zmin_2D is None:
1402                # min log value: no log(negative)
1403                if self.zmin_2D <= 0:
1404                    zmin_2D_temp = -32
1405                else:
1406                    zmin_2D_temp = math.log10(self.zmin_2D)
1407            if not self.zmax_2D is None:
1408                zmax_2D_temp = math.log10(self.zmax_2D)
1409
1410        self.image(data=self.data, qx_data=self.qx_data,
1411                   qy_data=self.qy_data, xmin=self.xmin_2D,
1412                   xmax=self.xmax_2D,
1413                   ymin=self.ymin_2D, ymax=self.ymax_2D,
1414                   cmap=self.cmap, zmin=zmin_2D_temp,
1415                   zmax=zmax_2D_temp)
1416
1417    def image(self, data, qx_data, qy_data, xmin, xmax, ymin, ymax,
1418              zmin, zmax, color=0, symbol=0, markersize=0,
1419              label='data2D', cmap=DEFAULT_CMAP):
1420        """
1421        Render the current data
1422
1423        """
1424        self.data = data
1425        self.qx_data = qx_data
1426        self.qy_data = qy_data
1427        self.xmin_2D = xmin
1428        self.xmax_2D = xmax
1429        self.ymin_2D = ymin
1430        self.ymax_2D = ymax
1431        self.zmin_2D = zmin
1432        self.zmax_2D = zmax
1433        c = self._color(color)
1434        # If we don't have any data, skip.
1435        if self.data == None:
1436            return
1437        if self.data.ndim == 1:
1438            output = self._build_matrix()
1439        else:
1440            output = copy.deepcopy(self.data)
1441        # check scale
1442        if self.scale == 'log_{10}':
1443            try:
1444                if  self.zmin_2D <= 0  and len(output[output > 0]) > 0:
1445                    zmin_temp = self.zmin_2D
1446                    output[output > 0] = numpy.log10(output[output > 0])
1447                    #In log scale Negative values are not correct in general
1448                    #output[output<=0] = math.log(numpy.min(output[output>0]))
1449                elif self.zmin_2D <= 0:
1450                    zmin_temp = self.zmin_2D
1451                    output[output > 0] = numpy.zeros(len(output))
1452                    output[output <= 0] = -32
1453                else:
1454                    zmin_temp = self.zmin_2D
1455                    output[output > 0] = numpy.log10(output[output > 0])
1456                    #In log scale Negative values are not correct in general
1457                    #output[output<=0] = math.log(numpy.min(output[output>0]))
1458            except:
1459                #Too many problems in 2D plot with scale
1460                pass
1461
1462        else:
1463            zmin_temp = self.zmin_2D
1464        self.cmap = cmap
1465        if self.dimension != 3:
1466            #Re-adjust colorbar
1467            self.subplot.figure.subplots_adjust(left=0.2, right=.8, bottom=.2)
1468
1469            im = self.subplot.imshow(output, interpolation='nearest',
1470                                     origin='lower',
1471                                     vmin=zmin_temp, vmax=self.zmax_2D,
1472                                     cmap=self.cmap,
1473                                     extent=(self.xmin_2D, self.xmax_2D,
1474                                             self.ymin_2D, self.ymax_2D))
1475
1476            cbax = self.subplot.figure.add_axes([0.84, 0.2, 0.02, 0.7])
1477        else:
1478            # clear the previous 2D from memory
1479            # mpl is not clf, so we do
1480            self.subplot.figure.clear()
1481
1482            self.subplot.figure.subplots_adjust(left=0.1, right=.8, bottom=.1)
1483
1484            X = self.x_bins[0:-1]
1485            Y = self.y_bins[0:-1]
1486            X, Y = numpy.meshgrid(X, Y)
1487
1488            try:
1489                # mpl >= 1.0.0
1490                ax = self.subplot.figure.gca(projection='3d')
1491                #ax.disable_mouse_rotation()
1492                cbax = self.subplot.figure.add_axes([0.84, 0.1, 0.02, 0.8])
1493                if len(X) > 60:
1494                    ax.disable_mouse_rotation()
1495            except:
1496                # mpl < 1.0.0
1497                try:
1498                    from mpl_toolkits.mplot3d import Axes3D
1499                except:
1500                    logging.error("PlotPanel could not import Axes3D")
1501                self.subplot.figure.clear()
1502                ax = Axes3D(self.subplot.figure)
1503                if len(X) > 60:
1504                    ax.cla()
1505                cbax = None
1506            self.subplot.figure.canvas.resizing = False
1507            im = ax.plot_surface(X, Y, output, rstride=1, cstride=1, cmap=cmap,
1508                                 linewidth=0, antialiased=False)
1509            self.subplot.set_axis_off()
1510
1511        if cbax == None:
1512            ax.set_frame_on(False)
1513            cb = self.subplot.figure.colorbar(im, shrink=0.8, aspect=20)
1514        else:
1515            cb = self.subplot.figure.colorbar(im, cax=cbax)
1516        cb.update_bruteforce(im)
1517        cb.set_label('$' + self.scale + '$')
1518        if self.dimension != 3:
1519            self.figure.canvas.draw_idle()
1520        else:
1521            self.figure.canvas.draw()
1522
1523    def _build_matrix(self):
1524        """
1525        Build a matrix for 2d plot from a vector
1526        Returns a matrix (image) with ~ square binning
1527        Requirement: need 1d array formats of
1528        self.data, self.qx_data, and self.qy_data
1529        where each one corresponds to z, x, or y axis values
1530
1531        """
1532        # No qx or qy given in a vector format
1533        if self.qx_data == None or self.qy_data == None \
1534                or self.qx_data.ndim != 1 or self.qy_data.ndim != 1:
1535            # do we need deepcopy here?
1536            return copy.deepcopy(self.data)
1537
1538        # maximum # of loops to fillup_pixels
1539        # otherwise, loop could never stop depending on data
1540        max_loop = 1
1541        # get the x and y_bin arrays.
1542        self._get_bins()
1543        # set zero to None
1544
1545        #Note: Can not use scipy.interpolate.Rbf:
1546        # 'cause too many data points (>10000)<=JHC.
1547        # 1d array to use for weighting the data point averaging
1548        #when they fall into a same bin.
1549        weights_data = numpy.ones([self.data.size])
1550        # get histogram of ones w/len(data); this will provide
1551        #the weights of data on each bins
1552        weights, xedges, yedges = numpy.histogram2d(x=self.qy_data,
1553                                                    y=self.qx_data,
1554                                                    bins=[self.y_bins, self.x_bins],
1555                                                    weights=weights_data)
1556        # get histogram of data, all points into a bin in a way of summing
1557        image, xedges, yedges = numpy.histogram2d(x=self.qy_data,
1558                                                  y=self.qx_data,
1559                                                  bins=[self.y_bins, self.x_bins],
1560                                                  weights=self.data)
1561        # Now, normalize the image by weights only for weights>1:
1562        # If weight == 1, there is only one data point in the bin so
1563        # that no normalization is required.
1564        image[weights > 1] = image[weights > 1] / weights[weights > 1]
1565        # Set image bins w/o a data point (weight==0) as None (was set to zero
1566        # by histogram2d.)
1567        image[weights == 0] = None
1568
1569        # Fill empty bins with 8 nearest neighbors only when at least
1570        #one None point exists
1571        loop = 0
1572
1573        # do while loop until all vacant bins are filled up up
1574        #to loop = max_loop
1575        while not(numpy.isfinite(image[weights == 0])).all():
1576            if loop >= max_loop:  # this protects never-ending loop
1577                break
1578            image = self._fillup_pixels(image=image, weights=weights)
1579            loop += 1
1580
1581        return image
1582
1583    def _get_bins(self):
1584        """
1585        get bins
1586        set x_bins and y_bins into self, 1d arrays of the index with
1587        ~ square binning
1588        Requirement: need 1d array formats of
1589        self.qx_data, and self.qy_data
1590        where each one corresponds to  x, or y axis values
1591        """
1592        # No qx or qy given in a vector format
1593        if self.qx_data == None or self.qy_data == None \
1594                or self.qx_data.ndim != 1 or self.qy_data.ndim != 1:
1595            # do we need deepcopy here?
1596            return copy.deepcopy(self.data)
1597
1598        # find max and min values of qx and qy
1599        xmax = self.qx_data.max()
1600        xmin = self.qx_data.min()
1601        ymax = self.qy_data.max()
1602        ymin = self.qy_data.min()
1603
1604        # calculate the range of qx and qy: this way, it is a little
1605        # more independent
1606        x_size = xmax - xmin
1607        y_size = ymax - ymin
1608
1609        # estimate the # of pixels on each axes
1610        npix_y = int(math.floor(math.sqrt(len(self.qy_data))))
1611        npix_x = int(math.floor(len(self.qy_data) / npix_y))
1612
1613        # bin size: x- & y-directions
1614        xstep = x_size / (npix_x - 1)
1615        ystep = y_size / (npix_y - 1)
1616
1617        # max and min taking account of the bin sizes
1618        xmax = xmax + xstep / 2.0
1619        xmin = xmin - xstep / 2.0
1620        ymax = ymax + ystep / 2.0
1621        ymin = ymin - ystep / 2.0
1622
1623        # store x and y bin centers in q space
1624        x_bins = numpy.linspace(xmin, xmax, npix_x)
1625        y_bins = numpy.linspace(ymin, ymax, npix_y)
1626
1627        #set x_bins and y_bins
1628        self.x_bins = x_bins
1629        self.y_bins = y_bins
1630
1631    def _fillup_pixels(self, image=None, weights=None):
1632        """
1633        Fill z values of the empty cells of 2d image matrix
1634        with the average over up-to next nearest neighbor points
1635
1636        :param image: (2d matrix with some zi = None)
1637
1638        :return: image (2d array )
1639
1640        :TODO: Find better way to do for-loop below
1641
1642        """
1643        # No image matrix given
1644        if image == None or numpy.ndim(image) != 2 \
1645                or numpy.isfinite(image).all() \
1646                or weights == None:
1647            return image
1648        # Get bin size in y and x directions
1649        len_y = len(image)
1650        len_x = len(image[1])
1651        temp_image = numpy.zeros([len_y, len_x])
1652        weit = numpy.zeros([len_y, len_x])
1653        # do for-loop for all pixels
1654        for n_y in range(len(image)):
1655            for n_x in range(len(image[1])):
1656                # find only null pixels
1657                if weights[n_y][n_x] > 0 or numpy.isfinite(image[n_y][n_x]):
1658                    continue
1659                else:
1660                    # find 4 nearest neighbors
1661                    # check where or not it is at the corner
1662                    if n_y != 0 and numpy.isfinite(image[n_y - 1][n_x]):
1663                        temp_image[n_y][n_x] += image[n_y - 1][n_x]
1664                        weit[n_y][n_x] += 1
1665                    if n_x != 0 and numpy.isfinite(image[n_y][n_x - 1]):
1666                        temp_image[n_y][n_x] += image[n_y][n_x - 1]
1667                        weit[n_y][n_x] += 1
1668                    if n_y != len_y - 1 and numpy.isfinite(image[n_y + 1][n_x]):
1669                        temp_image[n_y][n_x] += image[n_y + 1][n_x]
1670                        weit[n_y][n_x] += 1
1671                    if n_x != len_x - 1 and numpy.isfinite(image[n_y][n_x + 1]):
1672                        temp_image[n_y][n_x] += image[n_y][n_x + 1]
1673                        weit[n_y][n_x] += 1
1674                    # go 4 next nearest neighbors when no non-zero
1675                    # neighbor exists
1676                    if n_y != 0 and n_x != 0 and\
1677                         numpy.isfinite(image[n_y - 1][n_x - 1]):
1678                        temp_image[n_y][n_x] += image[n_y - 1][n_x - 1]
1679                        weit[n_y][n_x] += 1
1680                    if n_y != len_y - 1 and n_x != 0 and \
1681                        numpy.isfinite(image[n_y + 1][n_x - 1]):
1682                        temp_image[n_y][n_x] += image[n_y + 1][n_x - 1]
1683                        weit[n_y][n_x] += 1
1684                    if n_y != len_y and n_x != len_x - 1 and \
1685                        numpy.isfinite(image[n_y - 1][n_x + 1]):
1686                        temp_image[n_y][n_x] += image[n_y - 1][n_x + 1]
1687                        weit[n_y][n_x] += 1
1688                    if n_y != len_y - 1 and n_x != len_x - 1 and \
1689                        numpy.isfinite(image[n_y + 1][n_x + 1]):
1690                        temp_image[n_y][n_x] += image[n_y + 1][n_x + 1]
1691                        weit[n_y][n_x] += 1
1692
1693        # get it normalized
1694        ind = (weit > 0)
1695        image[ind] = temp_image[ind] / weit[ind]
1696
1697        return image
1698
1699    def curve(self, x, y, dy=None, color=0, symbol=0, label=None):
1700        """Draw a line on a graph, possibly with confidence intervals."""
1701        c = self._color(color)
1702        self.subplot.set_yscale('linear')
1703        self.subplot.set_xscale('linear')
1704
1705        self.subplot.plot(x, y, color=c, marker='',
1706                          linestyle='-', label=label)
1707        self.subplot.set_yscale(self.yscale)
1708        self.subplot.set_xscale(self.xscale)
1709
1710    def _color(self, c):
1711        """Return a particular colour"""
1712        return self.colorlist[c % len(self.colorlist)]
1713
1714    def _symbol(self, s):
1715        """Return a particular symbol"""
1716        return self.symbollist[s % len(self.symbollist)]
1717
1718    def _replot(self, remove_fit=False):
1719        """
1720        Rescale the plottables according to the latest
1721        user selection and update the plot
1722
1723        :param remove_fit: Fit line will be removed if True
1724
1725        """
1726        self.graph.reset_scale()
1727        self._onEVT_FUNC_PROPERTY(remove_fit=remove_fit)
1728        #TODO: Why do we have to have the following line?
1729        self.fit_result.reset_view()
1730        self.graph.render(self)
1731        self.subplot.figure.canvas.draw_idle()
1732
1733    def _onEVT_FUNC_PROPERTY(self, remove_fit=True, show=True):
1734        """
1735        Receive the x and y transformation from myDialog,
1736        Transforms x and y in View
1737        and set the scale
1738        """
1739        # The logic should be in the right order
1740        # Delete first, and then get the whole list...
1741        if remove_fit:
1742            self.graph.delete(self.fit_result)
1743        self.ly = None
1744        self.q_ctrl = None
1745        list = self.graph.returnPlottable()
1746        # Changing the scale might be incompatible with
1747        # currently displayed data (for instance, going
1748        # from ln to log when all plotted values have
1749        # negative natural logs).
1750        # Go linear and only change the scale at the end.
1751        self.set_xscale("linear")
1752        self.set_yscale("linear")
1753        _xscale = 'linear'
1754        _yscale = 'linear'
1755        for item in list:
1756            item.setLabel(self.xLabel, self.yLabel)
1757
1758            # control axis labels from the panel itself
1759            yname, yunits = item.get_yaxis()
1760            if self.yaxis_label != None:
1761                yname = self.yaxis_label
1762                yunits = self.yaxis_unit
1763            else:
1764                self.yaxis_label = yname
1765                self.yaxis_unit = yunits
1766            xname, xunits = item.get_xaxis()
1767            if self.xaxis_label != None:
1768                xname = self.xaxis_label
1769                xunits = self.xaxis_unit
1770            else:
1771                self.xaxis_label = xname
1772                self.xaxis_unit = xunits
1773            # Goes through all possible scales
1774            if self.xLabel == "x":
1775                item.transformX(transform.toX, transform.errToX)
1776                self.graph._xaxis_transformed("%s" % xname, "%s" % xunits)
1777            if self.xLabel == "x^(2)":
1778                item.transformX(transform.toX2, transform.errToX2)
1779                xunits = convert_unit(2, xunits)
1780                self.graph._xaxis_transformed("%s^{2}" % xname, "%s" % xunits)
1781            if self.xLabel == "x^(4)":
1782                item.transformX(transform.toX4, transform.errToX4)
1783                xunits = convert_unit(4, xunits)
1784                self.graph._xaxis_transformed("%s^{4}" % xname, "%s" % xunits)
1785            if self.xLabel == "ln(x)":
1786                item.transformX(transform.toLogX, transform.errToLogX)
1787                self.graph._xaxis_transformed("\ln\\ %s" % xname, "%s" % xunits)
1788            if self.xLabel == "log10(x)":
1789                item.transformX(transform.toX_pos, transform.errToX_pos)
1790                _xscale = 'log'
1791                self.graph._xaxis_transformed("%s" % xname, "%s" % xunits)
1792            if self.xLabel == "log10(x^(4))":
1793                item.transformX(transform.toX4, transform.errToX4)
1794                xunits = convert_unit(4, xunits)
1795                self.graph._xaxis_transformed("%s^{4}" % xname, "%s" % xunits)
1796                _xscale = 'log'
1797            if self.yLabel == "ln(y)":
1798                item.transformY(transform.toLogX, transform.errToLogX)
1799                self.graph._yaxis_transformed("\ln\\ %s" % yname, "%s" % yunits)
1800            if self.yLabel == "y":
1801                item.transformY(transform.toX, transform.errToX)
1802                self.graph._yaxis_transformed("%s" % yname, "%s" % yunits)
1803            if self.yLabel == "log10(y)":
1804                item.transformY(transform.toX_pos, transform.errToX_pos)
1805                _yscale = 'log'
1806                self.graph._yaxis_transformed("%s" % yname, "%s" % yunits)
1807            if self.yLabel == "y^(2)":
1808                item.transformY(transform.toX2, transform.errToX2)
1809                yunits = convert_unit(2, yunits)
1810                self.graph._yaxis_transformed("%s^{2}" % yname, "%s" % yunits)
1811            if self.yLabel == "1/y":
1812                item.transformY(transform.toOneOverX, transform.errOneOverX)
1813                yunits = convert_unit(-1, yunits)
1814                self.graph._yaxis_transformed("1/%s" % yname, "%s" % yunits)
1815            if self.yLabel == "y*x^(4)":
1816                item.transformY(transform.toYX4, transform.errToYX4)
1817                xunits = convert_unit(4, self.xaxis_unit)
1818                self.graph._yaxis_transformed("%s \ \ %s^{4}" % (yname, xname),
1819                                              "%s%s" % (yunits, xunits))
1820            if self.yLabel == "1/sqrt(y)":
1821                item.transformY(transform.toOneOverSqrtX,
1822                                transform.errOneOverSqrtX)
1823                yunits = convert_unit(-0.5, yunits)
1824                self.graph._yaxis_transformed("1/\sqrt{%s}" % yname,
1825                                              "%s" % yunits)
1826            if self.yLabel == "ln(y*x)":
1827                item.transformY(transform.toLogXY, transform.errToLogXY)
1828                self.graph._yaxis_transformed("\ln (%s \ \ %s)" % (yname, xname),
1829                                              "%s%s" % (yunits, self.xaxis_unit))
1830            if self.yLabel == "ln(y*x^(2))":
1831                item.transformY(transform.toLogYX2, transform.errToLogYX2)
1832                xunits = convert_unit(2, self.xaxis_unit)
1833                self.graph._yaxis_transformed("\ln (%s \ \ %s^{2})" % (yname, xname),
1834                                              "%s%s" % (yunits, xunits))
1835            if self.yLabel == "ln(y*x^(4))":
1836                item.transformY(transform.toLogYX4, transform.errToLogYX4)
1837                xunits = convert_unit(4, self.xaxis_unit)
1838                self.graph._yaxis_transformed("\ln (%s \ \ %s^{4})" % (yname, xname),
1839                                              "%s%s" % (yunits, xunits))
1840            if self.yLabel == "log10(y*x^(4))":
1841                item.transformY(transform.toYX4, transform.errToYX4)
1842                xunits = convert_unit(4, self.xaxis_unit)
1843                _yscale = 'log'
1844                self.graph._yaxis_transformed("%s \ \ %s^{4}" % (yname, xname),
1845                                              "%s%s" % (yunits, xunits))
1846            if self.viewModel == "Guinier lny vs x^(2)":
1847                item.transformX(transform.toX2, transform.errToX2)
1848                xunits = convert_unit(2, xunits)
1849                self.graph._xaxis_transformed("%s^{2}" % xname, "%s" % xunits)
1850                item.transformY(transform.toLogX, transform.errToLogX)
1851                self.graph._yaxis_transformed("\ln\ \ %s" % yname, "%s" % yunits)
1852            if self.viewModel == "Porod y*x^(4) vs x^(4)":
1853                item.transformX(transform.toX4, transform.errToX4)
1854                xunits = convert_unit(4, self.xaxis_unit)
1855                self.graph._xaxis_transformed("%s^{4}" % xname, "%s" % xunits)
1856                item.transformY(transform.toYX4, transform.errToYX4)
1857                self.graph._yaxis_transformed("%s \ \ %s^{4}" % (yname, xname),
1858                                              "%s%s" % (yunits, xunits))
1859            item.transformView()
1860
1861        # set new label and units
1862        yname = self.graph.prop["ylabel"]
1863        yunits = ''
1864        xname = self.graph.prop["xlabel"]
1865        xunits = ''
1866
1867        self.resetFitView()
1868        self.prevXtrans = self.xLabel
1869        self.prevYtrans = self.yLabel
1870        self.graph.render(self)
1871        self.set_xscale(_xscale)
1872        self.set_yscale(_yscale)
1873
1874        self.xaxis(xname, xunits, self.xaxis_font,
1875                   self.xaxis_color, self.xaxis_tick)
1876        self.yaxis(yname, yunits, self.yaxis_font,
1877                   self.yaxis_color, self.yaxis_tick)
1878        self.subplot.texts = self.textList
1879        if show:
1880            self.subplot.figure.canvas.draw_idle()
1881
1882    def onFitDisplay(self, tempx, tempy, xminView,
1883                     xmaxView, xmin, xmax, func):
1884        """
1885        Add a new plottable into the graph .In this case this plottable
1886        will be used to fit some data
1887
1888        :param tempx: The x data of fit line
1889        :param tempy: The y data of fit line
1890        :param xminView: the lower bound of fitting range
1891        :param xminView: the upper bound of  fitting range
1892        :param xmin: the lowest value of data to fit to the line
1893        :param xmax: the highest value of data to fit to the line
1894
1895        """
1896        # Saving value to redisplay in Fit Dialog when it is opened again
1897        self.Avalue, self.Bvalue, self.ErrAvalue, \
1898                      self.ErrBvalue, self.Chivalue = func
1899        self.xminView = xminView
1900        self.xmaxView = xmaxView
1901        self.xmin = xmin
1902        self.xmax = xmax
1903        #In case need to change the range of data plotted
1904        for item in self.graph.returnPlottable():
1905            item.onFitRange(None, None)
1906        # Create new data plottable with result
1907        self.fit_result.x = []
1908        self.fit_result.y = []
1909        self.fit_result.x = tempx
1910        self.fit_result.y = tempy
1911        self.fit_result.dx = None
1912        self.fit_result.dy = None
1913        #Load the view with the new values
1914        self.fit_result.reset_view()
1915        # Add the new plottable to the graph
1916        self.graph.add(self.fit_result)
1917        self.graph.render(self)
1918        self._offset_graph()
1919        self.subplot.figure.canvas.draw_idle()
1920
1921    def onChangeCaption(self, event):
1922        """
1923        """
1924        if self.parent == None:
1925            return
1926        # get current caption
1927        old_caption = self.window_caption
1928        # Get new caption dialog
1929        dial = LabelDialog(None, -1, 'Modify Window Title', old_caption)
1930        if dial.ShowModal() == wx.ID_OK:
1931            new_caption = dial.getText()
1932
1933            # send to guiframe to change the panel caption
1934            caption = self.parent.on_change_caption(self.window_name,
1935                                                    old_caption, new_caption)
1936
1937            # also set new caption in plot_panels list
1938            self.parent.plot_panels[self.uid].window_caption = caption
1939            # set new caption
1940            self.window_caption = caption
1941
1942        dial.Destroy()
1943
1944    def onResetGraph(self, event):
1945        """
1946        Reset the graph by plotting the full range of data
1947        """
1948        for item in self.graph.returnPlottable():
1949            item.onReset()
1950        self.graph.render(self)
1951        self._onEVT_FUNC_PROPERTY(False)
1952        self.is_zoomed = False
1953        self.toolbar.update()
1954
1955    def onPrint(self, event=None):
1956        self.toolbar.print_figure(event)
1957
1958    def onPrinterSetup(self, event=None):
1959        """
1960        """
1961        self.canvas.Printer_Setup(event=event)
1962        self.Update()
1963
1964    def onPrinterPreview(self, event=None):
1965        """
1966        Matplotlib camvas can no longer print itself.  Thus need to do
1967        everything ourselves: need to create a printpreview frame to to
1968        see the preview but needs a parent frame object.  Also needs a
1969        printout object (just as any printing task).
1970        """
1971        try:
1972            #check if parent is a frame.  If not keep getting the higher
1973            #parent till we find a frame
1974            _plot = self
1975            while not isinstance(_plot, wx.Frame):
1976                _plot = _plot.GetParent()
1977                assert _plot is not None
1978
1979            #now create the printpeview object
1980            _preview = wx.PrintPreview(PlotPrintout(self.canvas),
1981                                       PlotPrintout(self.canvas))
1982            #and tie it to a printpreview frame then show it
1983            _frame = wx.PreviewFrame(_preview, _plot, "Print Preview", wx.Point(100, 100), wx.Size(600, 650))
1984            _frame.Centre(wx.BOTH)
1985            _frame.Initialize()
1986            _frame.Show(True)
1987        except:
1988            traceback.print_exc()
1989            pass
1990
1991    def OnCopyFigureMenu(self, evt):
1992        """
1993        Copy the current figure to clipboard
1994        """
1995        try:
1996            self.toolbar.copy_figure(self.canvas)
1997        except:
1998            print "Error in copy Image"
1999
2000
2001#---------------------------------------------------------------
2002class NoRepaintCanvas(FigureCanvasWxAgg):
2003    """
2004    We subclass FigureCanvasWxAgg, overriding the _onPaint method, so that
2005    the draw method is only called for the first two paint events. After that,
2006    the canvas will only be redrawn when it is resized.
2007
2008    """
2009    def __init__(self, *args, **kwargs):
2010        """
2011        """
2012        FigureCanvasWxAgg.__init__(self, *args, **kwargs)
2013        self._drawn = 0
2014
2015    def _onPaint(self, evt):
2016        """
2017        Called when wxPaintEvt is generated
2018
2019        """
2020        if not self._isRealized:
2021            self.realize()
2022        if self._drawn < 2:
2023            self.draw(repaint=False)
2024            self._drawn += 1
2025        self.gui_repaint(drawDC=wx.PaintDC(self))
Note: See TracBrowser for help on using the repository browser.