source: sasview/guiframe/local_perspectives/plotting/plotting.py @ 3fd1ebc

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 3fd1ebc was fc2b91a, checked in by Mathieu Doucet <doucetm@…>, 17 years ago

Updated for interactive graphs.

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