source: sasview/guiframe/local_perspectives/plotting/Plotter1D.py @ a3fc33d

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

changing tip for delete

  • Property mode set to 100644
File size: 17.5 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       
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        # ready for another event
143        event.Skip() 
144        # set the resizing flag
145        self.resizing = True
146        self.canvas.set_resizing(self.resizing)
147        self.parent.set_schedule(True)
148        pos_x, pos_y = self.GetPositionTuple()
149        if pos_x != 0 and pos_y != 0:
150            self.size, _ = self.GetClientSizeTuple()
151       
152    def set_resizing(self, resizing=False):
153        """
154        Set the resizing (True/False)
155        """
156        self.resizing = resizing
157        #self.canvas.set_resizing(resizing)
158   
159    def schedule_full_draw(self, func='append'):   
160        """
161        Put self in schedule to full redraw list
162        """
163        # append/del this panel in the schedule list
164        self.parent.set_schedule_full_draw(self, func)
165       
166
167    def remove_data_by_id(self, id):
168        """'
169        remove data from plot
170        """
171        if id in self.plots.keys():
172            data =  self.plots[id]
173            self.graph.delete(data)
174            data_manager = self._manager.parent.get_data_manager()
175            data_list, theory_list = data_manager.get_by_id(id_list=[id])
176           
177            if id in data_list.keys():
178                data = data_list[id]
179            if id in theory_list.keys():
180                data = theory_list[id]
181           
182            del self.plots[id]
183            self.graph.render(self)
184            self.subplot.figure.canvas.draw_idle()   
185            if len(self.graph.plottables) == 0:
186                #onRemove: graph is empty must be the panel must be destroyed
187                self.parent.delete_panel(self.uid)
188       
189    def plot_data(self, data):
190        """
191        Data is ready to be displayed
192       
193        :param event: data event
194        """
195        if data.id in self.plots.keys():
196            #replace
197            self.graph.replace(data)
198            self.plots[data.id] = data
199        else:
200            self.plots[data.id] = data
201            self.graph.add(self.plots[data.id]) 
202       
203        x_label, x_unit = data.get_xaxis()
204        y_label, y_unit = data.get_yaxis()
205        self.graph.xaxis(x_unit, x_label)
206        self.graph.yaxis(y_unit, y_label)
207        ## Set the view scale for all plots
208        self._onEVT_FUNC_PROPERTY()
209        ## render the graph<=No need this done in canvas
210        #self.graph.render(self)
211        #self.subplot.figure.canvas.draw_idle()
212   
213    def draw_plot(self):
214        """
215        Draw plot
216        """
217        self.draw() 
218
219
220       
221    def onLeftDown(self,event): 
222        """
223        left button down and ready to drag
224        Display the position of the mouse on the statusbar
225        """
226        PlotPanel.onLeftDown(self, event)
227        ax = event.inaxes
228        if ax != None:
229            position = "x: %8.3g    y: %8.3g" % (event.xdata, event.ydata)
230            wx.PostEvent(self.parent, StatusEvent(status=position))
231        # unfocus all
232        self.parent.set_plot_unfocus() 
233        #post nd event to notify guiframe that this panel is on focus
234        wx.PostEvent(self.parent, PanelOnFocusEvent(panel=self))
235
236       
237    def _ontoggle_hide_error(self, event):
238        """
239        Toggle error display to hide or show
240        """
241       
242        selected_plot = self.plots[self.graph.selected_plottable]
243        if self.hide_menu.GetText() == "Hide Error":
244            selected_plot.hide_error = True
245        else:
246            selected_plot.hide_error = False
247        ## increment graph color
248        self.graph.render(self)
249        self.subplot.figure.canvas.draw_idle() 
250         
251    def _onRemove(self, event):
252        """
253        Remove a plottable from the graph and render the graph
254       
255        :param event: Menu event
256       
257        """
258        ## Check if there is a selected graph to remove
259        if self.graph.selected_plottable in self.plots.keys():
260            selected_plot = self.plots[self.graph.selected_plottable]
261            id = self.graph.selected_plottable
262            self.remove_data_by_id(id)
263           
264    def onContextMenu(self, event):
265        """
266        1D plot context menu
267       
268        :param event: wx context event
269       
270        """
271        self._slicerpop = PanelMenu()
272        self._slicerpop.set_plots(self.plots)
273        self._slicerpop.set_graph(self.graph)     
274        # Various plot options
275        id = wx.NewId()
276        self._slicerpop.Append(id, '&Save Image', 'Save image as PNG')
277        wx.EVT_MENU(self, id, self.onSaveImage)
278        id = wx.NewId()
279        self._slicerpop.Append(id, '&Print Image', 'Print image ')
280        wx.EVT_MENU(self, id, self.onPrint)
281        id = wx.NewId()
282        self._slicerpop.Append(id, '&Print Preview', 'Print preview')
283        wx.EVT_MENU(self, id, self.onPrinterPreview)
284       
285        id = wx.NewId()
286        self._slicerpop.Append(id, '&Copy to Clipboard', 'Copy to the clipboard')
287        wx.EVT_MENU(self, id, self.OnCopyFigureMenu)
288       
289        self._slicerpop.AppendSeparator()
290
291        #add menu of other plugins
292        item_list = self.parent.get_context_menu(self)
293
294        if (not item_list == None) and (not len(item_list) == 0):
295            for item in item_list:
296                try:
297                    id = wx.NewId()
298                    self._slicerpop.Append(id, item[0], item[1])
299                    wx.EVT_MENU(self, id, item[2])
300                except:
301                    msg = "ModelPanel1D.onContextMenu: "
302                    msg += "bad menu item  %s" % sys.exc_value
303                    wx.PostEvent(self.parent, StatusEvent(status=msg))
304                    pass
305            self._slicerpop.AppendSeparator()
306        #id = wx.NewId()
307        #self._slicerpop.Append(id, '&Print image', 'Print image')
308        if self.graph.selected_plottable in self.plots:
309            plot = self.plots[self.graph.selected_plottable]
310           
311            id = wx.NewId()
312            name = plot.name
313            self._slicerpop.Append(id, "&Save Points as a File")
314            self._slicerpop.AppendSeparator()
315            if plot.name != 'SLD':
316                wx.EVT_MENU(self, id, self._onSave)
317                id = wx.NewId()
318                self._slicerpop.Append(id, '&Linear Fit')
319                wx.EVT_MENU(self, id, self.onFitting)
320                self._slicerpop.AppendSeparator()
321   
322                id = wx.NewId()
323                self._slicerpop.Append(id, "Remove %s Curve" % name)
324                wx.EVT_MENU(self, id, self._onRemove)
325                if not plot.is_data:
326                    id = wx.NewId()
327                    self._slicerpop.Append(id, '&Freeze', 'Freeze')
328                    wx.EVT_MENU(self, id, self.onFreeze)
329                symbol_menu = wx.Menu()
330                for label in self._symbol_labels:
331                    id = wx.NewId()
332                    symbol_menu.Append(id, str(label), str(label))
333                    wx.EVT_MENU(self, id, self.onChangeSymbol)
334                id = wx.NewId()
335                self._slicerpop.AppendMenu(id,'&Modify Symbol',  symbol_menu)
336                self._slicerpop.AppendSeparator()
337   
338                id = wx.NewId()
339                self.hide_menu = self._slicerpop.Append(id, "Hide Error")
340   
341                if plot.dy is not None and plot.dy != []:
342                    if plot.hide_error :
343                        self.hide_menu.SetText('Show Error')
344                    else:
345                        self.hide_menu.SetText('Hide Error')
346                else:
347                    self.hide_menu.Enable(False)
348                wx.EVT_MENU(self, id, self._ontoggle_hide_error)
349               
350                self._slicerpop.AppendSeparator()
351                # Option to hide
352                #TODO: implement functionality to hide a plottable (legend click)
353       
354       
355        id = wx.NewId()
356        self._slicerpop.Append(id, '&Change scale')
357        wx.EVT_MENU(self, id, self._onProperties)
358        id = wx.NewId()
359        self._slicerpop.Append(id, '&Reset Graph')
360        wx.EVT_MENU(self, id, self.onResetGraph) 
361        pos = event.GetPosition()
362        pos = self.ScreenToClient(pos)
363        self.PopupMenu(self._slicerpop, pos)
364     
365    def onFreeze(self, event):
366        """
367        """
368        plot = self.plots[self.graph.selected_plottable]
369        self.parent.onfreeze([plot.id])
370       
371    def onChangeSymbol(self, event):
372        """
373        """
374        menu = event.GetEventObject()
375        id = event.GetId()
376        label =  menu.GetLabel(id)
377        selected_plot = self.plots[self.graph.selected_plottable]
378        selected_plot.symbol = self._symbol_labels[label]
379        ## Set the view scale for all plots
380        self._onEVT_FUNC_PROPERTY()
381        ## render the graph
382        #self.graph.render(self)
383        #self.subplot.figure.canvas.draw_idle()
384       
385    def _onsaveTXT(self, path):
386        """
387        Save file as txt
388           
389        :TODO: Refactor and remove this method. See TODO in _onSave.
390       
391        """
392        data = self.plots[self.graph.selected_plottable]
393       
394        if not path == None:
395            out = open(path, 'w')
396            has_errors = True
397            if data.dy == None or data.dy == []:
398                has_errors = False
399            # Sanity check
400            if has_errors:
401                try:
402                    if len(data.y) != len(data.dy):
403                        has_errors = False
404                except:
405                    has_errors = False
406            if has_errors:
407                if data.dx != None:
408                    out.write("<X>   <Y>   <dY>   <dX>\n")
409                else:
410                    out.write("<X>   <Y>   <dY>\n")
411            else:
412                out.write("<X>   <Y>\n")
413               
414            for i in range(len(data.x)):
415                if has_errors:
416                    if data.dx != None:
417                        out.write("%g  %g  %g  %g\n" % (data.x[i], 
418                                                    data.y[i],
419                                                    data.dy[i],
420                                                    data.dx[i]))
421                    else:
422                        out.write("%g  %g  %g\n" % (data.x[i], 
423                                                    data.y[i],
424                                                    data.dy[i]))
425                else:
426                    out.write("%g  %g\n" % (data.x[i], 
427                                            data.y[i]))
428            out.close()                 
429            try:
430                self._default_save_location = os.path.dirname(path)
431            except:
432                pass   
433               
434    def _onSave(self, evt):
435        """
436        Save a data set to a text file
437       
438        :param evt: Menu event
439       
440        """
441       
442        path = None
443        wildcard = "Text files (*.txt)|*.txt|"\
444        "CanSAS 1D files(*.xml)|*.xml" 
445        dlg = wx.FileDialog(self, "Choose a file",
446                            self._default_save_location,
447                             "", wildcard , wx.SAVE)
448       
449        if dlg.ShowModal() == wx.ID_OK:
450            path = dlg.GetPath()
451            # ext_num = 0 for .txt, ext_num = 1 for .xml
452            # This is MAC Fix
453            ext_num = dlg.GetFilterIndex()
454            if ext_num == 0:
455                format = '.txt'
456            else:
457                format = '.xml'
458            path = os.path.splitext(path)[0] + format
459            mypath = os.path.basename(path)
460           
461            #TODO: This is bad design. The DataLoader is designed
462            #to recognize extensions.
463            # It should be a simple matter of calling the .
464            #save(file, data, '.xml') method
465            # of the DataLoader.loader.Loader class.
466            from DataLoader.loader import  Loader
467            #Instantiate a loader
468            loader = Loader() 
469            data = self.plots[self.graph.selected_plottable]
470            format = ".txt"
471            if os.path.splitext(mypath)[1].lower() == format:
472                # Make sure the ext included in the file name
473                # especially on MAC
474                fName = os.path.splitext(path)[0] + format
475                self._onsaveTXT(fName)
476            format = ".xml"
477            if os.path.splitext(mypath)[1].lower() == format:
478                # Make sure the ext included in the file name
479                # especially on MAC
480                fName = os.path.splitext(path)[0] + format
481                loader.save(fName, data, format)
482            try:
483                self._default_save_location = os.path.dirname(path)
484            except:
485                pass   
486        dlg.Destroy()
487
488    def _add_more_tool(self):
489        """
490        Add refresh button in the tool bar
491        """
492        if self.parent.__class__.__name__ != 'ViewerFrame':
493            return
494        self.toolbar.AddSeparator()
495        id_delete = wx.NewId()
496        delete =  wx.ArtProvider.GetBitmap(wx.ART_DELETE, wx.ART_TOOLBAR)
497        self.toolbar.AddSimpleTool(id_delete, delete,
498                           'Delete', 'permanently Delete')
499
500        self.toolbar.Realize()
501        wx.EVT_TOOL(self, id_delete,  self._on_delete)
502
503    def _on_delete(self, event): 
504        """
505        Refreshes the plotpanel on refresh tollbar button
506        """
507       
508        if self.parent is not None:
509            wx.PostEvent(self.parent, 
510                         NewPlotEvent(group_id=self.group_id,
511                                      action="delete"))
512           
Note: See TracBrowser for help on using the repository browser.