source: sasview/guiframe/local_perspectives/plotting/plotting.py @ 7bb61da

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

Making sure new plottables are added with the right scale.

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