source: sasview/guiframe/local_perspectives/plotting/Plotter1D.py @ 957723f

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 957723f was a3c96f7a, checked in by Gervaise Alina <gervyh@…>, 14 years ago

updating plot display

  • Property mode set to 100644
File size: 14.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 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    def get_symbol_label(self):
85        """
86        Associates label to symbol
87        """
88        _labels = {}
89        i = 0
90        _labels['Points'] = i
91        i += 1
92        _labels['X '] = i
93        i += 1
94        _labels['Triangle Down'] = i
95        i += 1
96        _labels['Triangle Up'] = i
97        i += 1
98        _labels['Triangle Left'] = i
99        i += 1
100        _labels['Triangle Right'] = i
101        i += 1
102        _labels['Plus'] = i
103        i += 1
104        _labels['Square'] = i
105        i += 1
106        _labels['Thin Diamond'] = i
107        i += 1
108        _labels['Diamond'] = i
109        i += 1
110        _labels['Hexagon1'] = i
111        i += 1
112        _labels['Hexagon2'] = i
113        i += 1
114        _labels['Pentagon'] = i
115        i += 1
116        _labels['Curve'] = i
117        return _labels
118
119   
120    def set_data(self, list=None):
121        """
122        """
123        pass
124   
125    def _reset(self):
126        """
127        Resets internal data and graph
128        """   
129        self.graph.reset()
130        self.plots      = {}
131   
132    def remove_data_by_id(self, id):
133        """'
134        remove data from plot
135        """
136        if id in self.plots.keys():
137            data =  self.plots[id]
138            self.graph.delete(data)
139            data_manager = self._manager.parent.get_data_manager()
140            data_list, theory_list = data_manager.get_by_id(id_list=[id])
141           
142            if id in data_list.keys():
143                data = data_list[id]
144            else:
145                data = theory_list[id]
146           
147            del self.plots[id]
148            self.graph.render(self)
149            self.subplot.figure.canvas.draw_idle()   
150            event = RemoveDataEvent(data=data)
151            wx.PostEvent(self.parent, event)
152            if len(self.graph.plottables) == 0:
153                #onRemove: graph is empty must be the panel must be destroyed
154                self.parent.delete_panel(self.uid)
155       
156    def plot_data(self, data):
157        """
158        Data is ready to be displayed
159       
160        :param event: data event
161        """
162        if data.id in self.plots.keys():
163            #replace
164            self.graph.replace(data)
165            self.plots[data.id] = data
166        else:
167            self.plots[data.id] = data
168            self.graph.add(self.plots[data.id]) 
169       
170        x_label, x_unit = data.get_xaxis()
171        y_label, y_unit = data.get_yaxis()
172        self.graph.xaxis(x_unit, x_label)
173        self.graph.yaxis(y_unit, y_label)
174        ## Set the view scale for all plots
175        self._onEVT_FUNC_PROPERTY()
176        ## render the graph
177        self.graph.render(self)
178        self.subplot.figure.canvas.draw_idle()
179
180       
181    def onLeftDown(self,event): 
182        """
183        left button down and ready to drag
184        Display the position of the mouse on the statusbar
185        """
186        PlotPanel.onLeftDown(self, event)
187        ax = event.inaxes
188        if ax != None:
189            position = "x: %8.3g    y: %8.3g" % (event.xdata, event.ydata)
190            wx.PostEvent(self.parent, StatusEvent(status=position))
191           
192        #post nd event to notify guiframe that this panel is on focus
193        wx.PostEvent(self.parent, PanelOnFocusEvent(panel=self))
194        self._manager.set_panel_on_focus(self)
195       
196    def _ontoggle_hide_error(self, event):
197        """
198        Toggle error display to hide or show
199        """
200       
201        selected_plot = self.plots[self.graph.selected_plottable]
202        if self.hide_menu.GetText() == "Hide Error":
203            selected_plot.hide_error = True
204        else:
205            selected_plot.hide_error = False
206        ## increment graph color
207        self.graph.render(self)
208        self.subplot.figure.canvas.draw_idle() 
209         
210    def _onRemove(self, event):
211        """
212        Remove a plottable from the graph and render the graph
213       
214        :param event: Menu event
215       
216        """
217        ## Check if there is a selected graph to remove
218        if self.graph.selected_plottable in self.plots.keys():
219            selected_plot = self.plots[self.graph.selected_plottable]
220            id = self.graph.selected_plottable
221            self.remove_data_by_id(id)
222           
223    def onContextMenu(self, event):
224        """
225        1D plot context menu
226       
227        :param event: wx context event
228       
229        """
230        self._slicerpop = PanelMenu()
231        self._slicerpop.set_plots(self.plots)
232        self._slicerpop.set_graph(self.graph)     
233        # Various plot options
234        id = wx.NewId()
235        self._slicerpop.Append(id, '&Save image', 'Save image as PNG')
236        wx.EVT_MENU(self, id, self.onSaveImage)
237        id = wx.NewId()
238        self._slicerpop.Append(id, '&Print image', 'Print image ')
239        wx.EVT_MENU(self, id, self.onPrint)
240        id = wx.NewId()
241        self._slicerpop.Append(id, '&Print Preview', 'image preview for print')
242        wx.EVT_MENU(self, id, self.onPrinterPreview)
243       
244       
245        #add menu of other plugins
246        item_list = self.parent.get_context_menu(self)
247        if (not item_list == None) and (not len(item_list) == 0):
248            for item in item_list:
249                try:
250                    id = wx.NewId()
251                    self._slicerpop.Append(id, item[0], item[1])
252                    wx.EVT_MENU(self, id, item[2])
253                except:
254                    msg = "ModelPanel1D.onContextMenu: "
255                    msg += "bad menu item  %s" % sys.exc_value
256                    wx.PostEvent(self.parent, StatusEvent(status=msg))
257                    pass
258            self._slicerpop.AppendSeparator()
259        id = wx.NewId()
260       
261        self._slicerpop.Append(id, '&Print image', 'Print image')
262        if self.graph.selected_plottable in self.plots:
263            plot = self.plots[self.graph.selected_plottable]
264            if not plot.is_data:
265                id = wx.NewId()
266                self._slicerpop.Append(id, '&Freeze', 'Freeze')
267                wx.EVT_MENU(self, id, self.onFreeze)
268            symbol_menu = wx.Menu()
269            for label in self._symbol_labels:
270                id = wx.NewId()
271                symbol_menu.Append(id, str(label), str(label))
272                wx.EVT_MENU(self, id, self.onChangeSymbol)
273            id = wx.NewId()
274            self._slicerpop.AppendMenu(id,'&Modify Symbol',  symbol_menu)
275            self._slicerpop.AppendSeparator()
276            #else:
277            id = wx.NewId()
278            self._slicerpop.Append(id, '&Linear Fit')
279            wx.EVT_MENU(self, id, self.onFitting)
280            id = wx.NewId()
281            name = plot.name
282            self._slicerpop.Append(id, "&Save points")
283            wx.EVT_MENU(self, id, self._onSave)
284            id = wx.NewId()
285            self._slicerpop.Append(id, "Remove %s curve" % name)
286            wx.EVT_MENU(self, id, self._onRemove)
287            id = wx.NewId()
288            self.hide_menu = self._slicerpop.Append(id, "Hide Error")
289            if plot.dy is not None or plot.dy != []:
290                if plot.hide_error :
291                    self.hide_menu.SetText('Show Error')
292                else:
293                    self.hide_menu.SetText('Hide Error')
294            else:
295                self.hide_menu.Disable()
296            wx.EVT_MENU(self, id, self._ontoggle_hide_error)
297           
298            self._slicerpop.AppendSeparator()
299            # Option to hide
300            #TODO: implement functionality to hide a plottable (legend click)
301       
302       
303        id = wx.NewId()
304        self._slicerpop.Append(id, '&Change scale')
305        wx.EVT_MENU(self, id, self._onProperties)
306        id = wx.NewId()
307        self._slicerpop.Append(id, '&Reset Graph')
308        wx.EVT_MENU(self, id, self.onResetGraph) 
309        pos = event.GetPosition()
310        pos = self.ScreenToClient(pos)
311        self.PopupMenu(self._slicerpop, pos)
312     
313    def onFreeze(self, event):
314        """
315        """
316        plot = self.plots[self.graph.selected_plottable]
317        self.parent.onfreeze([plot.id])
318       
319    def onChangeSymbol(self, event):
320        """
321        """
322        menu = event.GetEventObject()
323        id = event.GetId()
324        label =  menu.GetLabel(id)
325        selected_plot = self.plots[self.graph.selected_plottable]
326        selected_plot.symbol = self._symbol_labels[label]
327        ## Set the view scale for all plots
328        self._onEVT_FUNC_PROPERTY()
329        ## render the graph
330        self.graph.render(self)
331        self.subplot.figure.canvas.draw_idle()
332       
333    def _onsaveTXT(self, path):
334        """
335        Save file as txt
336           
337        :TODO: Refactor and remove this method. See TODO in _onSave.
338       
339        """
340        data = self.plots[self.graph.selected_plottable]
341       
342        if not path == None:
343            out = open(path, 'w')
344            has_errors = True
345            if data.dy == None or data.dy == []:
346                has_errors = False
347            # Sanity check
348            if has_errors:
349                try:
350                    if len(data.y) != len(data.dy):
351                        has_errors = False
352                except:
353                    has_errors = False
354            if has_errors:
355                out.write("<X>   <Y>   <dY>\n")
356            else:
357                out.write("<X>   <Y>\n")
358               
359            for i in range(len(data.x)):
360                if has_errors:
361                    out.write("%g  %g  %g\n" % (data.x[i], 
362                                                data.y[i],
363                                               data.dy[i]))
364                else:
365                    out.write("%g  %g\n" % (data.x[i], 
366                                            data.y[i]))
367            out.close()                 
368            try:
369                self._default_save_location = os.path.dirname(path)
370            except:
371                pass   
372               
373    def _onSave(self, evt):
374        """
375        Save a data set to a text file
376       
377        :param evt: Menu event
378       
379        """
380       
381        path = None
382        wildcard = "Text files (*.txt)|*.txt|"\
383        "CanSAS 1D files(*.xml)|*.xml" 
384        dlg = wx.FileDialog(self, "Choose a file",
385                            self._default_save_location,
386                             "", wildcard , wx.SAVE)
387       
388        if dlg.ShowModal() == wx.ID_OK:
389            path = dlg.GetPath()
390            mypath = os.path.basename(path)
391           
392            #TODO: This is bad design. The DataLoader is designed
393            #to recognize extensions.
394            # It should be a simple matter of calling the .
395            #save(file, data, '.xml') method
396            # of the DataLoader.loader.Loader class.
397            from DataLoader.loader import  Loader
398            #Instantiate a loader
399            loader = Loader() 
400            data = self.plots[self.graph.selected_plottable]
401            format = ".txt"
402            if os.path.splitext(mypath)[1].lower() == format:
403                 self._onsaveTXT( path)
404            format = ".xml"
405            if os.path.splitext(mypath)[1].lower() == format:
406                loader.save(path, data, format)
407            try:
408                self._default_save_location = os.path.dirname(path)
409            except:
410                pass   
411        dlg.Destroy()
Note: See TracBrowser for help on using the repository browser.