source: sasview/sansguiframe/src/sans/guiframe/local_perspectives/plotting/Plotter2D.py @ c17760d

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 c17760d was c17760d, checked in by Jae Cho <jhjcho@…>, 13 years ago

consistently uses Upper case

  • Property mode set to 100644
File size: 25.8 KB
Line 
1
2################################################################################
3#This software was developed by the University of Tennessee as part of the
4#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
5#project funded by the US National Science Foundation.
6#
7#See the license text in license.txt
8#
9#copyright 2008, University of Tennessee
10################################################################################
11
12
13import wx
14import sys
15import os
16import math
17import numpy
18import pylab
19import danse.common.plottools
20from danse.common.plottools.PlotPanel import PlotPanel
21from danse.common.plottools.plottables import Graph
22from danse.common.plottools.TextDialog import TextDialog
23from sans.guiframe.events import EVT_NEW_PLOT
24from sans.guiframe.events import EVT_SLICER_PARS
25from sans.guiframe.events import StatusEvent
26from sans.guiframe.events import NewPlotEvent
27from sans.guiframe.events import PanelOnFocusEvent
28from sans.guiframe.events import SlicerEvent
29from sans.guiframe.utils import PanelMenu
30from binder import BindArtist
31from Plotter1D import ModelPanel1D
32from danse.common.plottools.toolbar import NavigationToolBar
33from sans.guiframe.dataFitting import Data1D
34from matplotlib.font_manager import FontProperties
35(InternalEvent, EVT_INTERNAL) = wx.lib.newevent.NewEvent()
36
37DEFAULT_QMAX = 0.05
38DEFAULT_QSTEP = 0.001
39DEFAULT_BEAM = 0.005
40BIN_WIDTH = 1.0
41
42
43class NavigationToolBar2D(NavigationToolBar):
44    """
45    """
46    def __init__(self, canvas, parent=None):
47        NavigationToolBar.__init__(self, canvas=canvas, parent=parent)
48       
49    def delete_option(self):
50        """
51        remove default toolbar item
52        """
53        #delete reset button
54        self.DeleteToolByPos(0) 
55        #delete dragging
56        self.DeleteToolByPos(2) 
57        #delete unwanted button that configures subplot parameters
58        self.DeleteToolByPos(4)
59       
60    def add_option(self):
61        """
62        add item to the toolbar
63        """
64        #add print button
65        id_print = wx.NewId()
66        print_bmp =  wx.ArtProvider.GetBitmap(wx.ART_PRINT, wx.ART_TOOLBAR)
67        self.AddSimpleTool(id_print, print_bmp,
68                           'Print', 'Activate printing')
69        wx.EVT_TOOL(self, id_print, self.on_print)
70       
71       
72class ModelPanel2D(ModelPanel1D):
73    """
74    Plot panel for use with the GUI manager
75    """
76   
77    ## Internal name for the AUI manager
78    window_name = "plotpanel"
79    ## Title to appear on top of the window
80    window_caption = "Plot Panel"
81    ## Flag to tell the GUI manager that this panel is not
82    #  tied to any perspective
83    ALWAYS_ON = True
84    ## Group ID
85    group_id = None
86   
87   
88    def __init__(self, parent, id=-1, data2d=None, color = None,
89                 dpi=None, style=wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
90        """
91        Initialize the panel
92        """
93        ModelPanel1D.__init__(self, parent, id=id, style=style, **kwargs)
94       
95        ## Reference to the parent window
96        self.parent = parent
97        ## Dictionary containing Plottables
98        self.plots = {}
99        ## Save reference of the current plotted
100        self.data2D = data2d
101        ## Unique ID (from gui_manager)
102        self.uid = None
103        ## Action IDs for internal call-backs
104        self.action_ids = {}
105        ## Create Artist and bind it
106        self.connect = BindArtist(self.subplot.figure)
107        ## Beam stop
108        self.beamstop_radius = DEFAULT_BEAM
109        ## to set the order of lines drawn first.
110        self.slicer_z = 5
111        ## Reference to the current slicer
112        self.slicer = None
113        ## event to send slicer info
114        self.Bind(EVT_INTERNAL, self._onEVT_INTERNAL)
115       
116        self.axes_frozen = False
117        ## panel that contains result from slicer motion (ex: Boxsum info)
118        self.panel_slicer = None
119        self.title_label = None
120        self.title_font = None
121        self.title_color = 'black'
122        ## Graph       
123        self.graph = Graph()
124        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
125        self.graph.yaxis("\\rm{Intensity} ", "cm^{-1}")
126        self.graph.render(self)
127        ## store default value of zmin and zmax
128        self.default_zmin_ctl = self.zmin_2D
129        self.default_zmax_ctl = self.zmax_2D
130       
131    def onLeftDown(self, event): 
132        """
133        left button down and ready to drag
134       
135        """
136        # Check that the LEFT button was pressed
137        if event.button == 1:
138            self.leftdown = True
139            ax = event.inaxes
140            if ax != None:
141                self.xInit, self.yInit = event.xdata, event.ydata
142        self.plottable_selected(self.data2D.id)
143       
144        self._manager.set_panel_on_focus(self)
145        wx.PostEvent(self.parent, PanelOnFocusEvent(panel=self))
146       
147    def add_toolbar(self):
148        """
149        add toolbar
150        """
151        self.enable_toolbar = True
152        self.toolbar = NavigationToolBar2D(parent=self, canvas=self.canvas)
153        self.toolbar.Realize()
154        # On Windows platform, default window size is incorrect, so set
155        # toolbar width to figure width.
156        tw, th = self.toolbar.GetSizeTuple()
157        fw, fh = self.canvas.GetSizeTuple()
158        self.toolbar.SetSize(wx.Size(fw, th))
159        self.sizer.Add(self.toolbar, 0, wx.LEFT|wx.EXPAND)
160        # update the axes menu on the toolbar
161        self.toolbar.update()
162         
163    def plot_data(self, data):
164        """
165        Data is ready to be displayed
166       
167        :TODO: this name should be changed to something more appropriate
168             Don't forget that changing this name will mean changing code
169             in plotting.py
170         
171        :param event: data event
172        """
173        xlo = None
174        xhi = None
175        ylo = None 
176        yhi = None
177        ## Update self.data2d with the current plot
178       
179        if data.id in self.plots.keys():
180            #replace
181            xlo, xhi = self.subplot.get_xlim()
182            ylo, yhi = self.subplot.get_ylim()
183            self.graph.replace(data)
184            self.plots[data.id] = data
185        else:
186            self.plots[data.id] = data
187            self.graph.add(self.plots[data.id]) 
188            # update qmax with the new xmax of data plotted
189            self.qmax = data.xmax
190           
191        self.slicer = None
192        # Check axis labels
193        #TODO: Should re-factor this
194        ## render the graph with its new content
195               
196        #data2D: put 'Pixel (Number)' for axis title and unit in case of having no detector info and none in _units
197        if len(data.detector) < 1: 
198            if len(data._xunit) < 1 and len(data._yunit) < 1:
199                data._xaxis = '\\rm{x}'
200                data._yaxis = '\\rm{y}'
201                data._xunit = 'pixel'
202                data._yunit = 'pixel'
203        self.graph.xaxis(data._xaxis, data._xunit)
204        self.graph.yaxis(data._yaxis, data._yunit)
205        data.label = self.title_label
206       
207        if data.label == None:
208            data.label = data.name
209           
210        if not self.title_font:
211            self.graph.title(data.label)
212            self.graph.render(self)
213            self.draw_plot()
214           
215        else:
216            self.graph.render(self)
217            self.draw_plot()
218            self.subplot.set_title(label=data.label,
219                                   fontproperties=self.title_font,
220                                   color=self.title_color)
221            self.subplot.figure.canvas.draw_idle() 
222       
223        self.data2D = data
224        ## store default value of zmin and zmax
225        self.default_zmin_ctl = self.zmin_2D
226        self.default_zmax_ctl = self.zmax_2D
227        # Recover the x,y limits
228        if (xlo and xhi and ylo and yhi) != None:
229            if (xlo > data.xmin and xhi < data.xmax and\
230                        ylo > data.ymin and yhi < data.ymax):
231                self.subplot.set_xlim((xlo, xhi))     
232                self.subplot.set_ylim((ylo, yhi)) 
233            else: 
234                self.toolbar.update()
235
236    def onContextMenu(self, event):
237        """
238        2D plot context menu
239       
240        :param event: wx context event
241       
242        """
243        slicerpop = PanelMenu()
244        slicerpop.set_plots(self.plots)
245        slicerpop.set_graph(self.graph)
246             
247        id = wx.NewId()
248        slicerpop.Append(id, '&Save Image')
249        wx.EVT_MENU(self, id, self.onSaveImage)
250       
251        id = wx.NewId()
252        slicerpop.Append(id,'&Print Image', 'Print image')
253        wx.EVT_MENU(self, id, self.onPrint)
254       
255        id = wx.NewId()
256        slicerpop.Append(id,'&Print Preview', 'Print preview')
257        wx.EVT_MENU(self, id, self.onPrinterPreview)
258
259        id = wx.NewId()
260        slicerpop.Append(id, '&Copy to Clipboard', 'Copy to the clipboard')
261        wx.EVT_MENU(self, id, self.OnCopyFigureMenu)
262        slicerpop.AppendSeparator()
263        # saving data
264        plot = self.data2D
265        id = wx.NewId()
266        name = plot.name
267        slicerpop.Append(id, "&Save as a File (DAT)" )
268        self.action_ids[str(id)] = plot
269        wx.EVT_MENU(self, id, self._onSave)
270
271        slicerpop.AppendSeparator()
272        if len(self.data2D.detector) == 1:       
273           
274            item_list = self.parent.get_context_menu(self)
275            if (not item_list == None) and (not len(item_list) == 0) and\
276                self.data2D.name.split(" ")[0] != 'Residuals': 
277                # The line above; Not for trunk
278                for item in item_list:
279                    try:
280                        id = wx.NewId()
281                        slicerpop.Append(id, item[0], item[1])
282                        wx.EVT_MENU(self, id, item[2])
283                    except:
284                        msg = "ModelPanel1D.onContextMenu: "
285                        msg += "bad menu item  %s"%sys.exc_value
286                        wx.PostEvent(self.parent, StatusEvent(status=msg))
287                        pass
288                slicerpop.AppendSeparator()
289           
290            id = wx.NewId()
291            slicerpop.Append(id, '&Perform Circular Average')
292            wx.EVT_MENU(self, id, self.onCircular) \
293            # For Masked Data
294            if not plot.mask.all():
295                id = wx.NewId()
296                slicerpop.Append(id, '&Masked Circular Average')
297                wx.EVT_MENU(self, id, self.onMaskedCircular) 
298            id = wx.NewId()
299            slicerpop.Append(id, '&Sector [Q View]')
300            wx.EVT_MENU(self, id, self.onSectorQ) 
301            id = wx.NewId()
302            slicerpop.Append(id, '&Annulus [Phi View ]')
303            wx.EVT_MENU(self, id, self.onSectorPhi) 
304            id = wx.NewId()
305            slicerpop.Append(id, '&Box Sum')
306            wx.EVT_MENU(self, id, self.onBoxSum) 
307            id = wx.NewId()
308            slicerpop.Append(id, '&Box Averaging in Qx')
309            wx.EVT_MENU(self, id, self.onBoxavgX) 
310            id = wx.NewId()
311            slicerpop.Append(id, '&Box Averaging in Qy')
312            wx.EVT_MENU(self, id, self.onBoxavgY) 
313            if self.slicer != None:
314                id = wx.NewId()
315                slicerpop.Append(id, '&Clear Slicer')
316                wx.EVT_MENU(self, id,  self.onClearSlicer) 
317                if self.slicer.__class__.__name__  != "BoxSum":
318                    id = wx.NewId()
319                    slicerpop.Append(id, '&Edit Slicer Parameters')
320                    wx.EVT_MENU(self, id, self._onEditSlicer) 
321            slicerpop.AppendSeparator() 
322           
323        id = wx.NewId()
324        slicerpop.Append(id, '&Edit Label', 'Edit Label')
325        wx.EVT_MENU(self, id, self.onEditLabels)
326        slicerpop.AppendSeparator()
327       
328        id = wx.NewId()
329        slicerpop.Append(id, '&2D Color Map')
330        wx.EVT_MENU(self, id, self._onEditDetector)
331        id = wx.NewId()
332        slicerpop.Append(id, '&Toggle Linear/Log Scale')
333        wx.EVT_MENU(self, id, self._onToggleScale) 
334        pos = event.GetPosition()
335        pos = self.ScreenToClient(pos)
336        self.PopupMenu(slicerpop, pos)
337           
338    def onEditLabels(self, event):
339        """
340        Edit legend label
341        """
342        selected_plot = self.plots[self.graph.selected_plottable]
343        label = selected_plot.label
344        dial = TextDialog(None, -1, 'Change Label', label)
345        if dial.ShowModal() == wx.ID_OK:
346            try:
347                FONT = FontProperties()
348                newlabel = dial.getText()
349                font = FONT.copy()
350                font.set_size(dial.getSize())
351                font.set_family(dial.getFamily())
352                font.set_style(dial.getStyle())
353                font.set_weight(dial.getWeight())
354                colour = dial.getColor()
355                if len(newlabel) > 0:
356                    selected_plot.label = newlabel
357                    self.title_label = selected_plot.label
358                    self.title_font = font
359                    self.title_color = colour
360                    ## render the graph
361                    self.subplot.set_title(label=self.title_label,
362                                           fontproperties=self.title_font,
363                                           color=self.title_color)
364                    self.subplot.figure.canvas.draw_idle() 
365            except:
366                if self.parent != None:
367                    from sans.guiframe.events import StatusEvent
368                    msg= "Add Text: Error. Check your property values..."
369                    wx.PostEvent(self.parent, StatusEvent(status = msg ))
370                else:
371                    raise
372        dial.Destroy()
373
374       
375    def _onEditDetector(self, event):
376        """
377        Allow to view and edits  detector parameters
378       
379        :param event: wx.menu event
380       
381        """
382        import detector_dialog
383        dialog = detector_dialog.DetectorDialog(self, -1,base=self.parent,
384                       reset_zmin_ctl =self.default_zmin_ctl,
385                       reset_zmax_ctl = self.default_zmax_ctl,cmap=self.cmap)
386        ## info of current detector and data2D
387        xnpts = len(self.data2D.x_bins)
388        ynpts = len(self.data2D.y_bins)
389        xmax = max(self.data2D.xmin, self.data2D.xmax)
390        ymax = max(self.data2D.ymin, self.data2D.ymax)
391        qmax = math.sqrt(math.pow(xmax, 2) + math.pow(ymax, 2))
392        beam = self.data2D.xmin
393        ## set dialog window content
394        dialog.setContent(xnpts=xnpts,ynpts=ynpts,qmax=qmax,
395                           beam=self.data2D.xmin,
396                           zmin = self.zmin_2D,
397                          zmax = self.zmax_2D)
398        if dialog.ShowModal() == wx.ID_OK:
399            evt = dialog.getContent()
400            self.zmin_2D = evt.zmin
401            self.zmax_2D = evt.zmax
402            self.cmap = evt.cmap
403        dialog.Destroy()
404        ## Redraw the current image
405        self.image(data=self.data2D.data,
406                   qx_data=self.data2D.qx_data,
407                   qy_data=self.data2D.qy_data,
408                   xmin= self.data2D.xmin,
409                   xmax= self.data2D.xmax,
410                   ymin= self.data2D.ymin,
411                   ymax= self.data2D.ymax,
412                   zmin= self.zmin_2D,
413                   zmax= self.zmax_2D,
414                   cmap= self.cmap,
415                   color=0, symbol=0, label=self.data2D.name)
416        self.subplot.figure.canvas.draw_idle()
417       
418    def freeze_axes(self):
419        """
420        """
421        self.axes_frozen = True
422       
423    def thaw_axes(self):
424        """
425        """
426        self.axes_frozen = False
427       
428    def onMouseMotion(self,event):
429        """
430        """
431        pass
432   
433    def onWheel(self, event):
434        """
435        """
436        pass 
437     
438    def update(self, draw=True):
439        """
440        Respond to changes in the model by recalculating the
441        profiles and resetting the widgets.
442        """
443        self.draw_plot()
444       
445    def _getEmptySlicerEvent(self):
446        """
447        create an empty slicervent
448        """
449        return SlicerEvent(type=None, params=None, obj_class=None)
450       
451    def _onEVT_INTERNAL(self, event):
452        """
453        Draw the slicer
454       
455        :param event: wx.lib.newevent (SlicerEvent) containing slicer
456            parameter
457           
458        """
459        self._setSlicer(event.slicer)
460           
461    def _setSlicer(self, slicer):
462        """
463        Clear the previous slicer and create a new one.Post an internal
464        event.
465       
466        :param slicer: slicer class to create
467       
468        """
469        ## Clear current slicer
470        if not self.slicer == None: 
471            self.slicer.clear()           
472        ## Create a new slicer   
473        self.slicer_z += 1
474        self.slicer = slicer(self, self.subplot, zorder=self.slicer_z)
475        self.subplot.set_ylim(self.data2D.ymin, self.data2D.ymax)
476        self.subplot.set_xlim(self.data2D.xmin, self.data2D.xmax)
477        ## Draw slicer
478        self.update()
479        self.slicer.update()
480        msg = "Plotter2D._setSlicer  %s"%self.slicer.__class__.__name__
481        wx.PostEvent(self.parent, StatusEvent(status=msg))
482        # Post slicer event
483        event = self._getEmptySlicerEvent()
484        event.type = self.slicer.__class__.__name__
485        event.obj_class = self.slicer.__class__
486        event.params = self.slicer.get_params()
487        wx.PostEvent(self, event)
488       
489    def onMaskedCircular(self, event):
490        """
491        perform circular averaging on Data2D with mask if it exists
492       
493        :param event: wx.menu event
494       
495        """
496        self.onCircular(event, True)
497       
498    def onCircular(self, event, ismask=False):
499        """
500        perform circular averaging on Data2D
501       
502        :param event: wx.menu event
503       
504        """
505        # Find the best number of bins
506        npt = math.sqrt(len(self.data2D.data[numpy.isfinite(self.data2D.data)]))
507        npt = math.floor(npt)
508        from sans.dataloader.manipulations import CircularAverage
509        ## compute the maximum radius of data2D
510        self.qmax = max(math.fabs(self.data2D.xmax), 
511                        math.fabs(self.data2D.xmin))
512        self.ymax = max(math.fabs(self.data2D.ymax),
513                        math.fabs(self.data2D.ymin))
514        self.radius = math.sqrt(math.pow(self.qmax, 2)+ math.pow(self.ymax, 2)) 
515        ##Compute beam width
516        bin_width = (self.qmax + self.qmax)/npt
517        ## Create data1D circular average of data2D
518        Circle = CircularAverage(r_min=0, r_max=self.radius, 
519                                 bin_width=bin_width)
520        circ = Circle(self.data2D, ismask=ismask)
521        from sans.guiframe.dataFitting import Data1D
522        if hasattr(circ, "dxl"):
523            dxl = circ.dxl
524        else:
525            dxl = None
526        if hasattr(circ, "dxw"):
527            dxw = circ.dxw
528        else:
529            dxw = None
530
531        new_plot = Data1D(x=circ.x, y=circ.y, dy=circ.dy, dx=circ.dx)
532        new_plot.dxl = dxl
533        new_plot.dxw = dxw
534        new_plot.name = "Circ avg " + self.data2D.name
535        new_plot.source = self.data2D.source
536        #new_plot.info = self.data2D.info
537        new_plot.interactive = True
538        new_plot.detector = self.data2D.detector
539       
540        ## If the data file does not tell us what the axes are, just assume...
541        new_plot.xaxis("\\rm{Q}", "A^{-1}")
542        if hasattr(self.data2D, "scale") and \
543                    self.data2D.scale == 'linear':
544            new_plot.ytransform = 'y'
545            new_plot.yaxis("\\rm{Residuals} ", "normalized")
546        else:
547            new_plot.yaxis("\\rm{Intensity} ", "cm^{-1}")
548
549        new_plot.group_id = "Circ avg " + self.data2D.name
550        new_plot.id = "Circ avg " + self.data2D.name
551        new_plot.is_data = True
552        wx.PostEvent(self.parent, 
553                     NewPlotEvent(plot=new_plot, title=new_plot.name))
554       
555    def _onEditSlicer(self, event):
556        """
557        Is available only when a slicer is drawn.Create a dialog
558        window where the user can enter value to reset slicer
559        parameters.
560       
561        :param event: wx.menu event
562       
563        """
564        if self.slicer != None:
565            from SlicerParameters import SlicerParameterPanel
566            dialog = SlicerParameterPanel(self, -1, "Slicer Parameters")
567            dialog.set_slicer(self.slicer.__class__.__name__,
568                            self.slicer.get_params())
569            if dialog.ShowModal() == wx.ID_OK:
570                dialog.Destroy() 
571       
572    def onSectorQ(self, event):
573        """
574        Perform sector averaging on Q and draw sector slicer
575        """
576        from SectorSlicer import SectorInteractor
577        self.onClearSlicer(event)
578        wx.PostEvent(self, InternalEvent(slicer=SectorInteractor))
579       
580    def onSectorPhi(self, event):
581        """
582        Perform sector averaging on Phi and draw annulus slicer
583        """
584        from AnnulusSlicer import AnnulusInteractor
585        self.onClearSlicer(event)
586        wx.PostEvent(self, InternalEvent(slicer=AnnulusInteractor))
587       
588    def onBoxSum(self, event):
589        """
590        """
591        from boxSum import BoxSum
592        self.onClearSlicer(event)
593        self.slicer_z += 1
594        self.slicer =  BoxSum(self, self.subplot, zorder=self.slicer_z)
595        self.subplot.set_ylim(self.data2D.ymin, self.data2D.ymax)
596        self.subplot.set_xlim(self.data2D.xmin, self.data2D.xmax)
597        self.update()
598        self.slicer.update()
599        ## Value used to initially set the slicer panel
600        type = self.slicer.__class__.__name__
601        params = self.slicer.get_params()
602        ## Create a new panel to display results of summation of Data2D
603        from slicerpanel import SlicerPanel
604        new_panel = SlicerPanel(parent=self.parent, id=-1,
605                                    base=self, type=type,
606                                    params=params, style=wx.RAISED_BORDER)
607       
608        new_panel.window_caption = self.slicer.__class__.__name__ + " " + \
609                                    str(self.data2D.name)
610        new_panel.window_name = self.slicer.__class__.__name__+ " " + \
611                                    str(self.data2D.name)
612        ## Store a reference of the new created panel
613        self.panel_slicer = new_panel
614        ## save the window_caption of the new panel in the current slicer
615        self.slicer.set_panel_name(name=new_panel.window_caption)
616        ## post slicer panel to guiframe to display it
617        from sans.guiframe.events import SlicerPanelEvent
618        wx.PostEvent(self.parent, SlicerPanelEvent(panel=self.panel_slicer,
619                                                    main_panel=self))
620
621    def onBoxavgX(self,event):
622        """
623        Perform 2D data averaging on Qx
624        Create a new slicer .
625       
626        :param event: wx.menu event
627        """
628        from boxSlicer import BoxInteractorX
629        self.onClearSlicer(event)
630        wx.PostEvent(self, InternalEvent(slicer=BoxInteractorX))
631       
632    def onBoxavgY(self,event):
633        """
634        Perform 2D data averaging on Qy
635        Create a new slicer .
636       
637        :param event: wx.menu event
638       
639        """
640        from boxSlicer import BoxInteractorY
641        self.onClearSlicer(event)
642        wx.PostEvent(self, InternalEvent(slicer=BoxInteractorY))
643       
644    def onClearSlicer(self, event):
645        """
646        Clear the slicer on the plot
647        """
648        if not self.slicer == None:
649            self.slicer.clear()
650            self.subplot.figure.canvas.draw()
651            self.slicer = None
652            # Post slicer None event
653            event = self._getEmptySlicerEvent()
654            wx.PostEvent(self, event)
655           
656    def _onSave(self, evt):
657        """
658        Save a data set to a dat(text) file
659       
660        :param evt: Menu event
661       
662        """
663        id = str(evt.GetId())
664        if id in self.action_ids:         
665           
666            path = None
667            wildcard = "IGOR/DAT 2D file in Q_map (*.dat)|*.DAT"
668            dlg = wx.FileDialog(self, "Choose a file",
669                                self._default_save_location,
670                                 "", wildcard , wx.SAVE)
671           
672            if dlg.ShowModal() == wx.ID_OK:
673                path = dlg.GetPath()
674                # ext_num = 0 for .txt, ext_num = 1 for .xml
675                # This is MAC Fix
676                ext_num = dlg.GetFilterIndex()
677                if ext_num == 0:
678                    format = '.dat'
679                else:
680                    format = ''
681                path = os.path.splitext(path)[0] + format
682                mypath = os.path.basename(path)
683               
684                #TODO: This is bad design. The DataLoader is designed
685                #to recognize extensions.
686                # It should be a simple matter of calling the .
687                #save(file, data, '.xml') method
688                # of the DataLoader.loader.Loader class.
689                from sans.dataloader.loader import  Loader
690                #Instantiate a loader
691                loader = Loader() 
692                data = self.data2D
693
694                format = ".dat"
695                if os.path.splitext(mypath)[1].lower() == format:
696                    # Make sure the ext included in the file name
697                    # especially on MAC
698                    fName = os.path.splitext(path)[0] + format
699                    loader.save(fName, data, format)
700                try:
701                    self._default_save_location = os.path.dirname(path)
702                except:
703                    pass   
704            dlg.Destroy()
Note: See TracBrowser for help on using the repository browser.