source: sasview/guiframe/local_perspectives/plotting/plotting.py @ 3931a24e

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 3931a24e was 671d14e1, checked in by Mathieu Doucet <doucetm@…>, 16 years ago

Added proper group id to theory curves

  • Property mode set to 100644
File size: 15.7 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        # Check whether this is a replot. If we ask for a replot
83        # and the plottable no longer exists, ignore the event.
84        if hasattr(event, "update") and event.update==True \
85            and event.plot.name not in self.plots.keys():
86            return
87       
88        if hasattr(event, "reset"):
89            self._reset()
90       
91        is_new = True
92        if event.plot.name in self.plots.keys():
93            # Check whether the class of plottable changed
94            if not event.plot.__class__==self.plots[event.plot.name].__class__:
95                self.graph.delete(self.plots[event.plot.name])
96            else:
97                is_new = False
98       
99        if is_new:
100            self.plots[event.plot.name] = event.plot
101            self.graph.add(self.plots[event.plot.name])
102        else:
103            self.plots[event.plot.name].x = event.plot.x   
104            self.plots[event.plot.name].y = event.plot.y   
105            self.plots[event.plot.name].dy = event.plot.dy 
106            if hasattr(event.plot, 'dx') and hasattr(self.plots[event.plot.name], 'dx'):
107                self.plots[event.plot.name].dx = event.plot.dx   
108 
109       
110        # Check axis labels
111        #TODO: Should re-factor this
112        #if event.plot._xunit != self.graph.prop["xunit"]:
113        self.graph.xaxis(event.plot._xaxis, event.plot._xunit)
114           
115        #if event.plot._yunit != self.graph.prop["yunit"]:
116        self.graph.yaxis(event.plot._yaxis, event.plot._yunit)
117     
118        # Set the view scale for all plots
119        self._onEVT_FUNC_PROPERTY()
120     
121        self.graph.render(self)
122        self.subplot.figure.canvas.draw_idle()
123
124    def onLeftDown(self,event): 
125        """ left button down and ready to drag"""
126        from sans.guicomm.events import StatusEvent   
127        PlotPanel.onLeftDown(self, event)
128        ax = event.inaxes
129        if ax != None:
130            position = "x: %8.3g    y: %8.3g" % (event.xdata, event.ydata)
131            wx.PostEvent(self.parent, StatusEvent(status=position))
132
133    def _onRemove(self, event):
134        """
135        """
136        if not self.graph.selected_plottable == None:
137            print self.graph.selected_plottable
138           
139           
140            self.graph.delete(self.plots[self.graph.selected_plottable])
141            del self.plots[self.graph.selected_plottable]
142            self.graph.render(self)
143            self.subplot.figure.canvas.draw_idle()   
144           
145           
146
147    def onContextMenu(self, event):
148        """
149            1D plot context menu
150            @param event: wx context event
151        """
152        #slicerpop = wx.Menu()
153        slicerpop = PanelMenu()
154        slicerpop.set_plots(self.plots)
155        slicerpop.set_graph(self.graph)
156               
157        # Option to save the data displayed
158       
159        #for plot in self.graph.plottables:
160        if self.graph.selected_plottable in self.plots:
161            plot = self.plots[self.graph.selected_plottable]
162            id = wx.NewId()
163            name = plot.name
164            slicerpop.Append(id, "&Save %s points" % name)
165            self.action_ids[str(id)] = plot
166            wx.EVT_MENU(self, id, self._onSave)
167               
168            # Option to delete plottable
169            id = wx.NewId()
170            slicerpop.Append(id, "Remove %s curve" % name)
171            self.action_ids[str(id)] = plot
172            wx.EVT_MENU(self, id, self._onRemove)
173           
174            # Option to hide
175            #TODO: implement functionality to hide a plottable (legend click)
176            slicerpop.AppendSeparator()
177               
178        # Various plot options
179        id = wx.NewId()
180        slicerpop.Append(id,'&Save image', 'Save image as PNG')
181        wx.EVT_MENU(self, id, self.onSaveImage)
182       
183       
184        item_list = self.parent.get_context_menu(self.graph)
185        if (not item_list==None) and (not len(item_list)==0):
186                slicerpop.AppendSeparator()
187                for item in item_list:
188                    try:
189                        id = wx.NewId()
190                        slicerpop.Append(id, item[0], item[1])
191                        wx.EVT_MENU(self, id, item[2])
192                    except:
193                        print sys.exc_value
194                        print RuntimeError, "View1DPanel.onContextMenu: bad menu item"
195       
196        slicerpop.AppendSeparator()
197       
198        #id = wx.NewId()
199        #slicerpop.Append(id, '&Toggle Linear/Log scale')
200        #wx.EVT_MENU(self, id, self._onToggleScale)
201
202        if self.graph.selected_plottable in self.plots:
203            if self.plots[self.graph.selected_plottable].__class__.__name__=="Theory1D":
204                id = wx.NewId()
205                slicerpop.Append(id, '&Add errors to data')
206                wx.EVT_MENU(self, id, self._on_add_errors)
207
208        id = wx.NewId()
209        slicerpop.Append(id, '&Change scale')
210        wx.EVT_MENU(self, id, self._onProperties)
211       
212        id = wx.NewId()
213        #slicerpop.AppendSeparator()
214        slicerpop.Append(id, '&Reset Graph')
215        wx.EVT_MENU(self, id, self.onResetGraph)       
216
217        pos = event.GetPosition()
218        pos = self.ScreenToClient(pos)
219        self.PopupMenu(slicerpop, pos)
220   
221    def _on_add_errors(self, evt):
222        """
223            Compute reasonable errors for a data set without
224            errors and transorm the plottable to a Data1D
225        """
226        import math
227        import numpy
228        from sans.guitools.plottables import Data1D
229        import time
230       
231        if not self.graph.selected_plottable == None:
232            length = len(self.plots[self.graph.selected_plottable].x)
233            dy = numpy.zeros(length)
234            for i in range(length):
235                dy[i] = math.sqrt(self.plots[self.graph.selected_plottable].y[i])
236               
237            new_plot = Data1D(self.plots[self.graph.selected_plottable].x,
238                              self.plots[self.graph.selected_plottable].y,
239                              dy=dy)
240            new_plot.interactive = True
241            new_plot.name = self.plots[self.graph.selected_plottable].name+" data"
242            if hasattr(self.plots[self.graph.selected_plottable], "group_id"):
243                new_plot.group_id = self.plots[self.graph.selected_plottable].group_id
244            else:
245                new_plot.group_id = str(time.time())
246           
247            label, unit = self.plots[self.graph.selected_plottable].get_xaxis()
248            new_plot.xaxis(label, unit)
249            label, unit = self.plots[self.graph.selected_plottable].get_yaxis()
250            new_plot.yaxis(label, unit)
251           
252            self.graph.delete(self.plots[self.graph.selected_plottable])
253           
254            self.graph.add(new_plot)
255            self.plots[self.graph.selected_plottable]=new_plot
256           
257            self.graph.render(self)
258            self.subplot.figure.canvas.draw_idle()   
259   
260    def _onSave(self, evt):
261        """
262            Save a data set to a text file
263            @param evt: Menu event
264        """
265        import os
266        id = str(evt.GetId())
267        if id in self.action_ids:         
268           
269            path = None
270            dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.txt", wx.SAVE)
271            if dlg.ShowModal() == wx.ID_OK:
272                path = dlg.GetPath()
273                mypath = os.path.basename(path)
274                print path
275            dlg.Destroy()
276           
277            if not path == None:
278                out = open(path, 'w')
279                has_errors = True
280                if self.action_ids[id].dy==None or self.action_ids[id].dy==[]:
281                    has_errors = False
282                   
283                # Sanity check
284                if has_errors:
285                    try:
286                        if len(self.action_ids[id].y) != len(self.action_ids[id].dy):
287                            print "Y and dY have different lengths"
288                            has_errors = False
289                    except:
290                        has_errors = False
291               
292                if has_errors:
293                    out.write("<X>   <Y>   <dY>\n")
294                else:
295                    out.write("<X>   <Y>\n")
296                   
297                for i in range(len(self.action_ids[id].x)):
298                    if has_errors:
299                        out.write("%g  %g  %g\n" % (self.action_ids[id].x[i], 
300                                                    self.action_ids[id].y[i],
301                                                    self.action_ids[id].dy[i]))
302                    else:
303                        out.write("%g  %g\n" % (self.action_ids[id].x[i], 
304                                                self.action_ids[id].y[i]))
305                       
306                out.close()
307   
308   
309    def _onToggleScale(self, event):
310        if self.get_yscale() == 'log':
311            self.set_yscale('linear')
312        else:
313            self.set_yscale('log')
314        self.subplot.figure.canvas.draw_idle()   
315   
316class Plugin:
317    """
318        Plug-in class to be instantiated by the GUI manager
319    """
320   
321    def __init__(self):
322        """
323            Initialize the plug-in
324        """
325        ## Plug-in name
326        self.sub_menu = "Plotting"
327       
328        ## Reference to the parent window
329        self.parent = None
330       
331        ## List of panels for the simulation perspective (names)
332        self.perspective = []
333       
334        ## Plot panels
335        self.plot_panels = []
336       
337
338    def populate_menu(self, id, parent):
339        """
340            Create a 'Plot' menu to list the panels
341            available for displaying
342            @param id: next available unique ID for wx events
343            @param parent: parent window
344        """
345        self.menu = wx.Menu()
346        return [(id, self.menu, "Plot")]
347   
348       
349    def get_panels(self, parent):
350        """
351            Create and return a list of panel objects
352        """
353        ## Save a reference to the parent
354        self.parent = parent
355        # Connect to plotting events
356        self.parent.Bind(EVT_NEW_PLOT, self._on_plot_event)
357       
358        # We have no initial panels for this plug-in
359        return []
360   
361    def get_perspective(self):
362        """
363            Get the list of panel names for this perspective
364        """
365        return self.perspective
366   
367    def on_perspective(self, event):
368        """
369            Call back function for the perspective menu item.
370            We notify the parent window that the perspective
371            has changed.
372            @param event: menu event
373        """
374        self.parent.set_perspective(self.perspective)
375   
376    def post_init(self):
377        """
378            Post initialization call back to close the loose ends
379            [Somehow openGL needs this call]
380        """
381        pass
382   
383    def _on_show_panel(self, event):
384        print "_on_show_panel"
385   
386    def _on_plot_event(self, event):
387        """
388            A new plottable is being shipped to the plotting plug-in.
389            Check whether we have a panel to put in on, or create
390            a new one
391            @param event: EVT_NEW_PLOT event
392        """
393        # Check whether we already have a graph with the same units
394        # as the plottable we just received.
395        is_available = False
396        for panel in self.plot_panels:
397            if event.plot._xunit == panel.graph.prop["xunit_base"] \
398            and event.plot._yunit == panel.graph.prop["yunit_base"]:
399                if hasattr(event.plot, "group_id"):
400                    if not event.plot.group_id==None \
401                        and event.plot.group_id==panel.group_id:
402                        is_available = True
403                        panel._onEVT_1DREPLOT(event)
404                        self.parent.show_panel(panel.uid)
405                else:
406                    # Check that the plot panel has no group ID
407                    if panel.group_id==None:
408                        is_available = True
409                        panel._onEVT_1DREPLOT(event)
410                        self.parent.show_panel(panel.uid)
411       
412        # Create a new plot panel if none was available       
413        if not is_available:
414            new_panel = View1DPanel(self.parent, -1, style=wx.RAISED_BORDER)
415            # Set group ID if available
416            group_id_str = ''
417            if hasattr(event.plot, "group_id"):
418                if not event.plot.group_id==None:
419                    new_panel.group_id = event.plot.group_id
420                    group_id_str = ' [%s]' % event.plot.group_id
421           
422            if hasattr(event, "title"):
423                new_panel.window_caption = event.title
424                new_panel.window_name = event.title
425                #new_panel.window_caption = event.title+group_id_str
426                #new_panel.window_name = event.title+group_id_str
427           
428            event_id = self.parent.popup_panel(new_panel)
429            self.menu.Append(event_id, new_panel.window_caption, 
430                             "Show %s plot panel" % new_panel.window_caption)
431           
432            # Set UID to allow us to reference the panel later
433            new_panel.uid = event_id
434           
435            # Ship the plottable to its panel
436            new_panel._onEVT_1DREPLOT(event)
437            self.plot_panels.append(new_panel)       
438           
439        return
440       
Note: See TracBrowser for help on using the repository browser.