source: sasview/guiframe/local_perspectives/plotting/Plotter1D.py @ 309a1f0

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

removed print statement

  • Property mode set to 100644
File size: 18.0 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 pylab
17import math
18import numpy
19import time
20
21from danse.common.plottools.PlotPanel import PlotPanel
22from danse.common.plottools.plottables import Graph
23from sans.guiframe import dataFitting
24from sans.guiframe.events import EVT_NEW_PLOT
25from sans.guiframe.events import StatusEvent
26from sans.guiframe.events import NewPlotEvent
27from sans.guiframe.events import SlicerEvent
28from sans.guiframe.events import PanelOnFocusEvent
29from sans.guiframe.events import EVT_NEW_LOADED_DATA
30from sans.guiframe.utils import PanelMenu
31from sans.guiframe.dataFitting import Data1D
32from sans.guiframe.panel_base import PanelBase
33from binder import BindArtist
34
35DEFAULT_QMAX = 0.05
36DEFAULT_QSTEP = 0.001
37DEFAULT_BEAM = 0.005
38BIN_WIDTH = 1
39
40
41class ModelPanel1D(PlotPanel, PanelBase):
42    """
43    Plot panel for use with the GUI manager
44    """
45   
46    ## Internal name for the AUI manager
47    window_name = "plotpanel"
48    ## Title to appear on top of the window
49    window_caption = "Plot Panel"
50    ## Flag to tell the GUI manager that this panel is not
51    #  tied to any perspective
52    ALWAYS_ON = True
53    ## Group ID
54    group_id = None
55   
56    def __init__(self, parent, id=-1, color = None,
57                 dpi=None, style=wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
58        PlotPanel.__init__(self, parent, id=id, style=style, **kwargs)
59        PanelBase.__init__(self, parent)
60        ## Reference to the parent window
61        self.parent = parent
62        ## Plottables
63        self.plots = {}
64        #context menu
65        self._slicerpop = None
66       
67        self._available_data = []
68        self._menu_add_ids = []
69        self._symbol_labels = self.get_symbol_label()
70     
71        self.hide_menu = None
72        ## Unique ID (from gui_manager)
73        self.uid = None
74        self.x_size = None
75        ## Default locations
76        self._default_save_location = os.getcwd() 
77        self.size = None       
78        ## Graph       
79        self.graph = Graph()
80        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
81        self.graph.yaxis("\\rm{Intensity} ", "cm^{-1}")
82        self.graph.render(self)
83       
84        # In resizing event
85        self.resizing = False
86        self.canvas.set_resizing(self.resizing)
87        self.Bind(wx.EVT_SIZE, self._OnReSize)
88        self._add_more_tool()
89       
90    def get_symbol_label(self):
91        """
92        Associates label to symbol
93        """
94        _labels = {}
95        i = 0
96        _labels['Circle'] = i
97        i += 1
98        _labels['Cross X '] = i
99        i += 1
100        _labels['Triangle Down'] = i
101        i += 1
102        _labels['Triangle Up'] = i
103        i += 1
104        _labels['Triangle Left'] = i
105        i += 1
106        _labels['Triangle Right'] = i
107        i += 1
108        _labels['Cross +'] = i
109        i += 1
110        _labels['Square'] = i
111        i += 1
112        _labels['Diamond'] = i
113        i += 1
114        _labels['Diamond'] = i
115        i += 1
116        _labels['Hexagon1'] = i
117        i += 1
118        _labels['Hexagon2'] = i
119        i += 1
120        _labels['Pentagon'] = i
121        i += 1
122        _labels['Line'] = i
123        return _labels
124
125   
126    def set_data(self, list=None):
127        """
128        """
129        pass
130   
131    def _reset(self):
132        """
133        Resets internal data and graph
134        """   
135        self.graph.reset()
136        self.plots      = {}
137       
138    def _OnReSize(self, event):   
139        """
140        On response of the resize of a panel, set axes_visiable False
141        """
142        # It was found that wx >= 2.9.3 sends an event even if no size changed.
143        # So manually recode the size (=x_size) and compare here.
144        if self.x_size != None:
145            if self.x_size == self.GetSize():
146                self.resizing = False
147                self.canvas.set_resizing(self.resizing)
148                return
149        self.x_size = self.GetSize()
150        # Ready for another event
151        # Do not remove this Skip. Otherwise it will get runtime error.
152        event.Skip() 
153        # set the resizing flag
154        self.resizing = True
155        self.canvas.set_resizing(self.resizing)
156        self.parent.set_schedule(True)
157        pos_x, pos_y = self.GetPositionTuple()
158        if pos_x != 0 and pos_y != 0:
159            self.size, _ = self.GetClientSizeTuple()
160       
161    def set_resizing(self, resizing=False):
162        """
163        Set the resizing (True/False)
164        """
165        self.resizing = resizing
166        #self.canvas.set_resizing(resizing)
167   
168    def schedule_full_draw(self, func='append'):   
169        """
170        Put self in schedule to full redraw list
171        """
172        # append/del this panel in the schedule list
173        self.parent.set_schedule_full_draw(self, func)
174       
175
176    def remove_data_by_id(self, id):
177        """'
178        remove data from plot
179        """
180        if id in self.plots.keys():
181            data =  self.plots[id]
182            self.graph.delete(data)
183            data_manager = self._manager.parent.get_data_manager()
184            data_list, theory_list = data_manager.get_by_id(id_list=[id])
185           
186            if id in data_list.keys():
187                data = data_list[id]
188            if id in theory_list.keys():
189                data = theory_list[id]
190           
191            del self.plots[id]
192            self.graph.render(self)
193            self.subplot.figure.canvas.draw_idle()   
194            if len(self.graph.plottables) == 0:
195                #onRemove: graph is empty must be the panel must be destroyed
196                self.parent.delete_panel(self.uid)
197       
198    def plot_data(self, data):
199        """
200        Data is ready to be displayed
201       
202        :param event: data event
203        """
204        if data.id in self.plots.keys():
205            #replace
206            self.graph.replace(data)
207            self.plots[data.id] = data
208        else:
209            self.plots[data.id] = data
210            self.graph.add(self.plots[data.id]) 
211       
212        x_label, x_unit = data.get_xaxis()
213        y_label, y_unit = data.get_yaxis()
214        self.graph.xaxis(x_unit, x_label)
215        self.graph.yaxis(y_unit, y_label)
216        ## Set the view scale for all plots
217        self._onEVT_FUNC_PROPERTY()
218        ## render the graph<=No need this done in canvas
219        #self.graph.render(self)
220        #self.subplot.figure.canvas.draw_idle()
221   
222    def draw_plot(self):
223        """
224        Draw plot
225        """
226        self.draw() 
227
228
229       
230    def onLeftDown(self,event): 
231        """
232        left button down and ready to drag
233        Display the position of the mouse on the statusbar
234        """
235        PlotPanel.onLeftDown(self, event)
236        ax = event.inaxes
237        if ax != None:
238            position = "x: %8.3g    y: %8.3g" % (event.xdata, event.ydata)
239            wx.PostEvent(self.parent, StatusEvent(status=position))
240        # unfocus all
241        self.parent.set_plot_unfocus() 
242        #post nd event to notify guiframe that this panel is on focus
243        wx.PostEvent(self.parent, PanelOnFocusEvent(panel=self))
244
245       
246    def _ontoggle_hide_error(self, event):
247        """
248        Toggle error display to hide or show
249        """
250       
251        selected_plot = self.plots[self.graph.selected_plottable]
252        if self.hide_menu.GetText() == "Hide Error":
253            selected_plot.hide_error = True
254        else:
255            selected_plot.hide_error = False
256        ## increment graph color
257        self.graph.render(self)
258        self.subplot.figure.canvas.draw_idle() 
259         
260    def _onRemove(self, event):
261        """
262        Remove a plottable from the graph and render the graph
263       
264        :param event: Menu event
265       
266        """
267        ## Check if there is a selected graph to remove
268        if self.graph.selected_plottable in self.plots.keys():
269            selected_plot = self.plots[self.graph.selected_plottable]
270            id = self.graph.selected_plottable
271            self.remove_data_by_id(id)
272           
273    def onContextMenu(self, event):
274        """
275        1D plot context menu
276       
277        :param event: wx context event
278       
279        """
280        self._slicerpop = PanelMenu()
281        self._slicerpop.set_plots(self.plots)
282        self._slicerpop.set_graph(self.graph)     
283        # Various plot options
284        id = wx.NewId()
285        self._slicerpop.Append(id, '&Save Image', 'Save image as PNG')
286        wx.EVT_MENU(self, id, self.onSaveImage)
287        id = wx.NewId()
288        self._slicerpop.Append(id, '&Print Image', 'Print image ')
289        wx.EVT_MENU(self, id, self.onPrint)
290        id = wx.NewId()
291        self._slicerpop.Append(id, '&Print Preview', 'Print preview')
292        wx.EVT_MENU(self, id, self.onPrinterPreview)
293       
294        id = wx.NewId()
295        self._slicerpop.Append(id, '&Copy to Clipboard', 'Copy to the clipboard')
296        wx.EVT_MENU(self, id, self.OnCopyFigureMenu)
297       
298        self._slicerpop.AppendSeparator()
299
300        #add menu of other plugins
301        item_list = self.parent.get_context_menu(self)
302
303        if (not item_list == None) and (not len(item_list) == 0):
304            for item in item_list:
305                try:
306                    id = wx.NewId()
307                    self._slicerpop.Append(id, item[0], item[1])
308                    wx.EVT_MENU(self, id, item[2])
309                except:
310                    msg = "ModelPanel1D.onContextMenu: "
311                    msg += "bad menu item  %s" % sys.exc_value
312                    wx.PostEvent(self.parent, StatusEvent(status=msg))
313                    pass
314            self._slicerpop.AppendSeparator()
315        #id = wx.NewId()
316        #self._slicerpop.Append(id, '&Print image', 'Print image')
317        if self.graph.selected_plottable in self.plots:
318            plot = self.plots[self.graph.selected_plottable]
319           
320            id = wx.NewId()
321            name = plot.name
322            self._slicerpop.Append(id, "&Save Points as a File")
323            self._slicerpop.AppendSeparator()
324            if plot.name != 'SLD':
325                wx.EVT_MENU(self, id, self._onSave)
326                id = wx.NewId()
327                self._slicerpop.Append(id, '&Linear Fit')
328                wx.EVT_MENU(self, id, self.onFitting)
329                self._slicerpop.AppendSeparator()
330   
331                id = wx.NewId()
332                self._slicerpop.Append(id, "Remove %s Curve" % name)
333                wx.EVT_MENU(self, id, self._onRemove)
334                if not plot.is_data:
335                    id = wx.NewId()
336                    self._slicerpop.Append(id, '&Freeze', 'Freeze')
337                    wx.EVT_MENU(self, id, self.onFreeze)
338                symbol_menu = wx.Menu()
339                for label in self._symbol_labels:
340                    id = wx.NewId()
341                    symbol_menu.Append(id, str(label), str(label))
342                    wx.EVT_MENU(self, id, self.onChangeSymbol)
343                id = wx.NewId()
344                self._slicerpop.AppendMenu(id,'&Modify Symbol',  symbol_menu)
345                self._slicerpop.AppendSeparator()
346   
347                id = wx.NewId()
348                self.hide_menu = self._slicerpop.Append(id, "Hide Error")
349   
350                if plot.dy is not None and plot.dy != []:
351                    if plot.hide_error :
352                        self.hide_menu.SetText('Show Error')
353                    else:
354                        self.hide_menu.SetText('Hide Error')
355                else:
356                    self.hide_menu.Enable(False)
357                wx.EVT_MENU(self, id, self._ontoggle_hide_error)
358               
359                self._slicerpop.AppendSeparator()
360                # Option to hide
361                #TODO: implement functionality to hide a plottable (legend click)
362       
363       
364        id = wx.NewId()
365        self._slicerpop.Append(id, '&Change scale')
366        wx.EVT_MENU(self, id, self._onProperties)
367        id = wx.NewId()
368        self._slicerpop.Append(id, '&Reset Graph')
369        wx.EVT_MENU(self, id, self.onResetGraph) 
370        pos = event.GetPosition()
371        pos = self.ScreenToClient(pos)
372        self.PopupMenu(self._slicerpop, pos)
373     
374    def onFreeze(self, event):
375        """
376        """
377        plot = self.plots[self.graph.selected_plottable]
378        self.parent.onfreeze([plot.id])
379       
380    def onChangeSymbol(self, event):
381        """
382        """
383        menu = event.GetEventObject()
384        id = event.GetId()
385        label =  menu.GetLabel(id)
386        selected_plot = self.plots[self.graph.selected_plottable]
387        selected_plot.symbol = self._symbol_labels[label]
388        ## Set the view scale for all plots
389        self._onEVT_FUNC_PROPERTY()
390        ## render the graph
391        #self.graph.render(self)
392        #self.subplot.figure.canvas.draw_idle()
393       
394    def _onsaveTXT(self, path):
395        """
396        Save file as txt
397           
398        :TODO: Refactor and remove this method. See TODO in _onSave.
399       
400        """
401        data = self.plots[self.graph.selected_plottable]
402       
403        if not path == None:
404            out = open(path, 'w')
405            has_errors = True
406            if data.dy == None or data.dy == []:
407                has_errors = False
408            # Sanity check
409            if has_errors:
410                try:
411                    if len(data.y) != len(data.dy):
412                        has_errors = False
413                except:
414                    has_errors = False
415            if has_errors:
416                if data.dx != None:
417                    out.write("<X>   <Y>   <dY>   <dX>\n")
418                else:
419                    out.write("<X>   <Y>   <dY>\n")
420            else:
421                out.write("<X>   <Y>\n")
422               
423            for i in range(len(data.x)):
424                if has_errors:
425                    if data.dx != None:
426                        out.write("%g  %g  %g  %g\n" % (data.x[i], 
427                                                    data.y[i],
428                                                    data.dy[i],
429                                                    data.dx[i]))
430                    else:
431                        out.write("%g  %g  %g\n" % (data.x[i], 
432                                                    data.y[i],
433                                                    data.dy[i]))
434                else:
435                    out.write("%g  %g\n" % (data.x[i], 
436                                            data.y[i]))
437            out.close()                 
438            try:
439                self._default_save_location = os.path.dirname(path)
440            except:
441                pass   
442               
443    def _onSave(self, evt):
444        """
445        Save a data set to a text file
446       
447        :param evt: Menu event
448       
449        """
450       
451        path = None
452        wildcard = "Text files (*.txt)|*.txt|"\
453        "CanSAS 1D files(*.xml)|*.xml" 
454        dlg = wx.FileDialog(self, "Choose a file",
455                            self._default_save_location,
456                             "", wildcard , wx.SAVE)
457       
458        if dlg.ShowModal() == wx.ID_OK:
459            path = dlg.GetPath()
460            # ext_num = 0 for .txt, ext_num = 1 for .xml
461            # This is MAC Fix
462            ext_num = dlg.GetFilterIndex()
463            if ext_num == 0:
464                format = '.txt'
465            else:
466                format = '.xml'
467            path = os.path.splitext(path)[0] + format
468            mypath = os.path.basename(path)
469           
470            #TODO: This is bad design. The DataLoader is designed
471            #to recognize extensions.
472            # It should be a simple matter of calling the .
473            #save(file, data, '.xml') method
474            # of the DataLoader.loader.Loader class.
475            from DataLoader.loader import  Loader
476            #Instantiate a loader
477            loader = Loader() 
478            data = self.plots[self.graph.selected_plottable]
479            format = ".txt"
480            if os.path.splitext(mypath)[1].lower() == format:
481                # Make sure the ext included in the file name
482                # especially on MAC
483                fName = os.path.splitext(path)[0] + format
484                self._onsaveTXT(fName)
485            format = ".xml"
486            if os.path.splitext(mypath)[1].lower() == format:
487                # Make sure the ext included in the file name
488                # especially on MAC
489                fName = os.path.splitext(path)[0] + format
490                loader.save(fName, data, format)
491            try:
492                self._default_save_location = os.path.dirname(path)
493            except:
494                pass   
495        dlg.Destroy()
496
497    def _add_more_tool(self):
498        """
499        Add refresh button in the tool bar
500        """
501        if self.parent.__class__.__name__ != 'ViewerFrame':
502            return
503        self.toolbar.AddSeparator()
504        id_delete = wx.NewId()
505        delete =  wx.ArtProvider.GetBitmap(wx.ART_DELETE, wx.ART_TOOLBAR)
506        self.toolbar.AddSimpleTool(id_delete, delete,
507                           'Delete', 'permanently Delete')
508
509        self.toolbar.Realize()
510        wx.EVT_TOOL(self, id_delete,  self._on_delete)
511
512    def _on_delete(self, event): 
513        """
514        Refreshes the plotpanel on refresh tollbar button
515        """
516       
517        if self.parent is not None:
518            wx.PostEvent(self.parent, 
519                         NewPlotEvent(group_id=self.group_id,
520                                      action="delete"))
521           
Note: See TracBrowser for help on using the repository browser.