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

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

removed extra evt skip

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