source: sasview/guiframe/local_perspectives/plotting/plotting.py @ 43723aa

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

Propagate group ID when decorating theory curve with errors

  • Property mode set to 100644
File size: 14.4 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            new_plot.group_id = self.plots[self.graph.selected_plottable].group_id
213            label, unit = self.plots[self.graph.selected_plottable].get_xaxis()
214            new_plot.xaxis(label, unit)
215            label, unit = self.plots[self.graph.selected_plottable].get_yaxis()
216            new_plot.yaxis(label, unit)
217           
218            self.graph.delete(self.plots[self.graph.selected_plottable])
219           
220            self.graph.add(new_plot)
221            self.plots[self.graph.selected_plottable]=new_plot
222           
223            self.graph.render(self)
224            self.subplot.figure.canvas.draw_idle()   
225   
226    def _onSave(self, evt):
227        """
228            Save a data set to a text file
229            @param evt: Menu event
230        """
231        import os
232        id = str(evt.GetId())
233        if id in self.action_ids:         
234           
235            path = None
236            dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.txt", wx.SAVE)
237            if dlg.ShowModal() == wx.ID_OK:
238                path = dlg.GetPath()
239                mypath = os.path.basename(path)
240                print path
241            dlg.Destroy()
242           
243            if not path == None:
244                out = open(path, 'w')
245                has_errors = True
246                if self.action_ids[id].dy==None or self.action_ids[id].dy==[]:
247                    has_errors = False
248                   
249                # Sanity check
250                if has_errors:
251                    try:
252                        if len(self.action_ids[id].y) != len(self.action_ids[id].dy):
253                            print "Y and dY have different lengths"
254                            has_errors = False
255                    except:
256                        has_errors = False
257               
258                if has_errors:
259                    out.write("<X>   <Y>   <dY>\n")
260                else:
261                    out.write("<X>   <Y>\n")
262                   
263                for i in range(len(self.action_ids[id].x)):
264                    if has_errors:
265                        out.write("%g  %g  %g\n" % (self.action_ids[id].x[i], 
266                                                    self.action_ids[id].y[i],
267                                                    self.action_ids[id].dy[i]))
268                    else:
269                        out.write("%g  %g\n" % (self.action_ids[id].x[i], 
270                                                self.action_ids[id].y[i]))
271                       
272                out.close()
273   
274   
275    def _onToggleScale(self, event):
276        if self.get_yscale() == 'log':
277            self.set_yscale('linear')
278        else:
279            self.set_yscale('log')
280        self.subplot.figure.canvas.draw_idle()   
281   
282class Plugin:
283    """
284        Plug-in class to be instantiated by the GUI manager
285    """
286   
287    def __init__(self):
288        """
289            Initialize the plug-in
290        """
291        ## Plug-in name
292        self.sub_menu = "Plotting"
293       
294        ## Reference to the parent window
295        self.parent = None
296       
297        ## List of panels for the simulation perspective (names)
298        self.perspective = []
299       
300        ## Plot panels
301        self.plot_panels = []
302       
303
304    def populate_menu(self, id, parent):
305        """
306            Create a 'Plot' menu to list the panels
307            available for displaying
308            @param id: next available unique ID for wx events
309            @param parent: parent window
310        """
311        self.menu = wx.Menu()
312        return [(id, self.menu, "Plot")]
313   
314       
315    def get_panels(self, parent):
316        """
317            Create and return a list of panel objects
318        """
319        ## Save a reference to the parent
320        self.parent = parent
321        # Connect to plotting events
322        self.parent.Bind(EVT_NEW_PLOT, self._on_plot_event)
323       
324        # We have no initial panels for this plug-in
325        return []
326   
327    def get_perspective(self):
328        """
329            Get the list of panel names for this perspective
330        """
331        return self.perspective
332   
333    def on_perspective(self, event):
334        """
335            Call back function for the perspective menu item.
336            We notify the parent window that the perspective
337            has changed.
338            @param event: menu event
339        """
340        self.parent.set_perspective(self.perspective)
341   
342    def post_init(self):
343        """
344            Post initialization call back to close the loose ends
345            [Somehow openGL needs this call]
346        """
347        pass
348   
349    def _on_show_panel(self, event):
350        print "_on_show_panel"
351   
352    def _on_plot_event(self, event):
353        """
354            A new plottable is being shipped to the plotting plug-in.
355            Check whether we have a panel to put in on, or create
356            a new one
357            @param event: EVT_NEW_PLOT event
358        """
359        # Check whether we already have a graph with the same units
360        # as the plottable we just received.
361        is_available = False
362        for panel in self.plot_panels:
363            if event.plot._xunit == panel.graph.prop["xunit_base"] \
364            and event.plot._yunit == panel.graph.prop["yunit_base"]:
365                if hasattr(event.plot, "group_id"):
366                    if not event.plot.group_id==None \
367                        and event.plot.group_id==panel.group_id:
368                        is_available = True
369                        panel._onEVT_1DREPLOT(event)
370                        self.parent.show_panel(panel.uid)
371                else:
372                    # Check that the plot panel has no group ID
373                    if panel.group_id==None:
374                        is_available = True
375                        panel._onEVT_1DREPLOT(event)
376                        self.parent.show_panel(panel.uid)
377       
378        # Create a new plot panel if none was available       
379        if not is_available:
380            new_panel = View1DPanel(self.parent, -1, style=wx.RAISED_BORDER)
381            # Set group ID if available
382            group_id_str = ''
383            if hasattr(event.plot, "group_id"):
384                if not event.plot.group_id==None:
385                    new_panel.group_id = event.plot.group_id
386                    group_id_str = ' [%s]' % event.plot.group_id
387           
388            if hasattr(event, "title"):
389                new_panel.window_caption = event.title
390                new_panel.window_name = event.title
391                #new_panel.window_caption = event.title+group_id_str
392                #new_panel.window_name = event.title+group_id_str
393           
394            event_id = self.parent.popup_panel(new_panel)
395            self.menu.Append(event_id, new_panel.window_caption, 
396                             "Show %s plot panel" % new_panel.window_caption)
397           
398            # Set UID to allow us to reference the panel later
399            new_panel.uid = event_id
400           
401            # Ship the plottable to its panel
402            new_panel._onEVT_1DREPLOT(event)
403            self.plot_panels.append(new_panel)       
404           
405        return
406       
Note: See TracBrowser for help on using the repository browser.