source: sasview/guiframe/local_perspectives/plotting/plotting.py @ 357b79b

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

Handle over application window size. Allow plot clearing.

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