source: sasview/guiframe/local_perspectives/plotting/Plotter1D.py @ 18a8359

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

moving a new feature (fast reacting plot panels) from branch

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