source: sasview/guiframe/local_perspectives/plotting/plotting.py @ ea5551f

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 ea5551f was be72312c, checked in by Mathieu Doucet <doucetm@…>, 16 years ago

Bug fix

  • Property mode set to 100644
File size: 14.3 KB
Line 
1"""
2This software was developed by the University of Tennessee as part of the
3Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
4project funded by the US National Science Foundation.
5
6See the license text in license.txt
7
8copyright 2008, University of Tennessee
9"""
10
11
12import wx
13import sys
14from sans.guitools.PlotPanel import PlotPanel
15from sans.guitools.plottables import Graph
16from sans.guicomm.events import EVT_NEW_PLOT
17
18class PanelMenu(wx.Menu):
19    plots = None
20    graph = None
21   
22    def set_plots(self, plots):
23        self.plots = plots
24   
25    def set_graph(self, graph):
26        self.graph = graph
27
28class View1DPanel(PlotPanel):
29    """
30        Plot panel for use with the GUI manager
31    """
32   
33    ## Internal name for the AUI manager
34    window_name = "plotpanel"
35    ## Title to appear on top of the window
36    window_caption = "Plot Panel"
37    ## Flag to tell the GUI manager that this panel is not
38    #  tied to any perspective
39    ALWAYS_ON = True
40    ## Group ID
41    group_id = None
42   
43    def __init__(self, parent, id = -1, color = None,\
44        dpi = None, style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
45        """
46            Initialize the panel
47        """
48        PlotPanel.__init__(self, parent, id = id, style = style, **kwargs)
49       
50        ## Reference to the parent window
51        self.parent = parent
52        ## Plottables
53        self.plots = {}
54       
55        ## Unique ID (from gui_manager)
56        self.uid = None
57       
58        ## Action IDs for internal call-backs
59        self.action_ids = {}
60       
61        ## Graph       
62        self.graph = Graph()
63        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
64        self.graph.yaxis("\\rm{Intensity} ","cm^{-1}")
65        self.graph.render(self)
66   
67    def _reset(self):
68        """
69            Resets internal data and graph
70        """   
71        self.graph.reset()
72        self.plots      = {}
73        self.action_ids = {}
74   
75    def _onEVT_1DREPLOT(self, event):
76        """
77            Data is ready to be displayed
78            @param event: data event
79        """
80        #TODO: Check for existence of plot attribute
81       
82        if hasattr(event, "reset"):
83            self._reset()
84       
85        is_new = True
86        if event.plot.name in self.plots.keys():
87            # Check whether the class of plottable changed
88            if not event.plot.__class__==self.plots[event.plot.name].__class__:
89                self.graph.delete(self.plots[event.plot.name])
90            else:
91                is_new = False
92       
93        if is_new:
94            self.plots[event.plot.name] = event.plot
95            self.graph.add(self.plots[event.plot.name])
96        else:
97            self.plots[event.plot.name].x = event.plot.x   
98            self.plots[event.plot.name].y = event.plot.y   
99            self.plots[event.plot.name].dy = event.plot.dy 
100            if hasattr(event.plot, 'dx') and hasattr(self.plots[event.plot.name], 'dx'):
101                self.plots[event.plot.name].dx = event.plot.dx   
102 
103       
104        # Check axis labels
105        #TODO: Should re-factor this
106        #if event.plot._xunit != self.graph.prop["xunit"]:
107        self.graph.xaxis(event.plot._xaxis, event.plot._xunit)
108           
109        #if event.plot._yunit != self.graph.prop["yunit"]:
110        self.graph.yaxis(event.plot._yaxis, event.plot._yunit)
111     
112        # Set the view scale for all plots
113        self._onEVT_FUNC_PROPERTY()
114     
115        self.graph.render(self)
116        self.subplot.figure.canvas.draw_idle()
117
118    def onLeftDown(self,event): 
119        """ left button down and ready to drag"""
120        from sans.guicomm.events import StatusEvent   
121        PlotPanel.onLeftDown(self, event)
122        ax = event.inaxes
123        if ax != None:
124            position = "x: %8.3g    y: %8.3g" % (event.xdata, event.ydata)
125            wx.PostEvent(self.parent, StatusEvent(status=position))
126
127
128    def onContextMenu(self, event):
129        """
130            1D plot context menu
131            @param event: wx context event
132        """
133        #slicerpop = wx.Menu()
134        slicerpop = PanelMenu()
135        slicerpop.set_plots(self.plots)
136        slicerpop.set_graph(self.graph)
137               
138        # Option to save the data displayed
139       
140        #for plot in self.graph.plottables:
141        if self.graph.selected_plottable in self.plots:
142            plot = self.plots[self.graph.selected_plottable]
143            id = wx.NewId()
144            name = plot.name
145            slicerpop.Append(id, "&Save %s points" % name)
146            self.action_ids[str(id)] = plot
147            wx.EVT_MENU(self, id, self._onSave)
148               
149        # Various plot options
150        id = wx.NewId()
151        slicerpop.Append(id,'&Save image', 'Save image as PNG')
152        wx.EVT_MENU(self, id, self.onSaveImage)
153       
154       
155        item_list = self.parent.get_context_menu(self.graph)
156        if (not item_list==None) and (not len(item_list)==0):
157                slicerpop.AppendSeparator()
158                for item in item_list:
159                    try:
160                        id = wx.NewId()
161                        slicerpop.Append(id, item[0], item[1])
162                        wx.EVT_MENU(self, id, item[2])
163                    except:
164                        print sys.exc_value
165                        print RuntimeError, "View1DPanel.onContextMenu: bad menu item"
166       
167        slicerpop.AppendSeparator()
168       
169        #id = wx.NewId()
170        #slicerpop.Append(id, '&Toggle Linear/Log scale')
171        #wx.EVT_MENU(self, id, self._onToggleScale)
172
173        if self.graph.selected_plottable in self.plots:
174            if self.plots[self.graph.selected_plottable].__class__.__name__=="Theory1D":
175                id = wx.NewId()
176                slicerpop.Append(id, '&Add errors to data')
177                wx.EVT_MENU(self, id, self._on_add_errors)
178
179        id = wx.NewId()
180        slicerpop.Append(id, '&Change scale')
181        wx.EVT_MENU(self, id, self._onProperties)
182       
183        id = wx.NewId()
184        #slicerpop.AppendSeparator()
185        slicerpop.Append(id, '&Reset Graph')
186        wx.EVT_MENU(self, id, self.onResetGraph)       
187
188        pos = event.GetPosition()
189        pos = self.ScreenToClient(pos)
190        self.PopupMenu(slicerpop, pos)
191   
192    def _on_add_errors(self, evt):
193        """
194            Compute reasonable errors for a data set without
195            errors and transorm the plottable to a Data1D
196        """
197        import math
198        import numpy
199        from sans.guitools.plottables import Data1D
200       
201        if not self.graph.selected_plottable == None:
202            length = len(self.plots[self.graph.selected_plottable].x)
203            dy = numpy.zeros(length)
204            for i in range(length):
205                dy[i] = math.sqrt(self.plots[self.graph.selected_plottable].y[i])
206               
207            new_plot = Data1D(self.plots[self.graph.selected_plottable].x,
208                              self.plots[self.graph.selected_plottable].y,
209                              dy=dy)
210            new_plot.interactive = True
211            new_plot.name = self.plots[self.graph.selected_plottable].name
212            label, unit = self.plots[self.graph.selected_plottable].get_xaxis()
213            new_plot.xaxis(label, unit)
214            label, unit = self.plots[self.graph.selected_plottable].get_yaxis()
215            new_plot.yaxis(label, unit)
216           
217            self.graph.delete(self.plots[self.graph.selected_plottable])
218           
219            self.graph.add(new_plot)
220            self.plots[self.graph.selected_plottable]=new_plot
221           
222            self.graph.render(self)
223            self.subplot.figure.canvas.draw_idle()   
224   
225    def _onSave(self, evt):
226        """
227            Save a data set to a text file
228            @param evt: Menu event
229        """
230        import os
231        id = str(evt.GetId())
232        if id in self.action_ids:         
233           
234            path = None
235            dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.txt", wx.SAVE)
236            if dlg.ShowModal() == wx.ID_OK:
237                path = dlg.GetPath()
238                mypath = os.path.basename(path)
239                print path
240            dlg.Destroy()
241           
242            if not path == None:
243                out = open(path, 'w')
244                has_errors = True
245                if self.action_ids[id].dy==None or self.action_ids[id].dy==[]:
246                    has_errors = False
247                   
248                # Sanity check
249                if has_errors:
250                    try:
251                        if len(self.action_ids[id].y) != len(self.action_ids[id].dy):
252                            print "Y and dY have different lengths"
253                            has_errors = False
254                    except:
255                        has_errors = False
256               
257                if has_errors:
258                    out.write("<X>   <Y>   <dY>\n")
259                else:
260                    out.write("<X>   <Y>\n")
261                   
262                for i in range(len(self.action_ids[id].x)):
263                    if has_errors:
264                        out.write("%g  %g  %g\n" % (self.action_ids[id].x[i], 
265                                                    self.action_ids[id].y[i],
266                                                    self.action_ids[id].dy[i]))
267                    else:
268                        out.write("%g  %g\n" % (self.action_ids[id].x[i], 
269                                                self.action_ids[id].y[i]))
270                       
271                out.close()
272   
273   
274    def _onToggleScale(self, event):
275        if self.get_yscale() == 'log':
276            self.set_yscale('linear')
277        else:
278            self.set_yscale('log')
279        self.subplot.figure.canvas.draw_idle()   
280   
281class Plugin:
282    """
283        Plug-in class to be instantiated by the GUI manager
284    """
285   
286    def __init__(self):
287        """
288            Initialize the plug-in
289        """
290        ## Plug-in name
291        self.sub_menu = "Plotting"
292       
293        ## Reference to the parent window
294        self.parent = None
295       
296        ## List of panels for the simulation perspective (names)
297        self.perspective = []
298       
299        ## Plot panels
300        self.plot_panels = []
301       
302
303    def populate_menu(self, id, parent):
304        """
305            Create a 'Plot' menu to list the panels
306            available for displaying
307            @param id: next available unique ID for wx events
308            @param parent: parent window
309        """
310        self.menu = wx.Menu()
311        return [(id, self.menu, "Plot")]
312   
313       
314    def get_panels(self, parent):
315        """
316            Create and return a list of panel objects
317        """
318        ## Save a reference to the parent
319        self.parent = parent
320        # Connect to plotting events
321        self.parent.Bind(EVT_NEW_PLOT, self._on_plot_event)
322       
323        # We have no initial panels for this plug-in
324        return []
325   
326    def get_perspective(self):
327        """
328            Get the list of panel names for this perspective
329        """
330        return self.perspective
331   
332    def on_perspective(self, event):
333        """
334            Call back function for the perspective menu item.
335            We notify the parent window that the perspective
336            has changed.
337            @param event: menu event
338        """
339        self.parent.set_perspective(self.perspective)
340   
341    def post_init(self):
342        """
343            Post initialization call back to close the loose ends
344            [Somehow openGL needs this call]
345        """
346        pass
347   
348    def _on_show_panel(self, event):
349        print "_on_show_panel"
350   
351    def _on_plot_event(self, event):
352        """
353            A new plottable is being shipped to the plotting plug-in.
354            Check whether we have a panel to put in on, or create
355            a new one
356            @param event: EVT_NEW_PLOT event
357        """
358        # Check whether we already have a graph with the same units
359        # as the plottable we just received.
360        is_available = False
361        for panel in self.plot_panels:
362            if event.plot._xunit == panel.graph.prop["xunit_base"] \
363            and event.plot._yunit == panel.graph.prop["yunit_base"]:
364                if hasattr(event.plot, "group_id"):
365                    if not event.plot.group_id==None \
366                        and event.plot.group_id==panel.group_id:
367                        is_available = True
368                        panel._onEVT_1DREPLOT(event)
369                        self.parent.show_panel(panel.uid)
370                else:
371                    # Check that the plot panel has no group ID
372                    if panel.group_id==None:
373                        is_available = True
374                        panel._onEVT_1DREPLOT(event)
375                        self.parent.show_panel(panel.uid)
376       
377        # Create a new plot panel if none was available       
378        if not is_available:
379            new_panel = View1DPanel(self.parent, -1, style=wx.RAISED_BORDER)
380            # Set group ID if available
381            group_id_str = ''
382            if hasattr(event.plot, "group_id"):
383                if not event.plot.group_id==None:
384                    new_panel.group_id = event.plot.group_id
385                    group_id_str = ' [%s]' % event.plot.group_id
386           
387            if hasattr(event, "title"):
388                new_panel.window_caption = event.title
389                new_panel.window_name = event.title
390                #new_panel.window_caption = event.title+group_id_str
391                #new_panel.window_name = event.title+group_id_str
392           
393            event_id = self.parent.popup_panel(new_panel)
394            self.menu.Append(event_id, new_panel.window_caption, 
395                             "Show %s plot panel" % new_panel.window_caption)
396           
397            # Set UID to allow us to reference the panel later
398            new_panel.uid = event_id
399           
400            # Ship the plottable to its panel
401            new_panel._onEVT_1DREPLOT(event)
402            self.plot_panels.append(new_panel)       
403           
404        return
405       
Note: See TracBrowser for help on using the repository browser.