source: sasview/guiframe/local_perspectives/plotting/plotting.py @ 2310d69

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

Improved context menu functionality

  • Property mode set to 100644
File size: 11.1 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   
21    def set_plots(self, plots):
22        self.plots = plots
23   
24
25class View1DPanel(PlotPanel):
26    """
27        Plot panel for use with the GUI manager
28    """
29   
30    ## Internal name for the AUI manager
31    window_name = "plotpanel"
32    ## Title to appear on top of the window
33    window_caption = "Plot Panel"
34    ## Flag to tell the GUI manager that this panel is not
35    #  tied to any perspective
36    ALWAYS_ON = True
37    ## Group ID
38    group_id = None
39   
40    def __init__(self, parent, id = -1, color = None,\
41        dpi = None, style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
42        """
43            Initialize the panel
44        """
45        PlotPanel.__init__(self, parent, id = id, style = style, **kwargs)
46       
47        ## Reference to the parent window
48        self.parent = parent
49        ## Plottables
50        self.plots = {}
51       
52        ## Unique ID (from gui_manager)
53        self.uid = None
54       
55        ## Action IDs for internal call-backs
56        self.action_ids = {}
57       
58        ## Graph       
59        self.graph = Graph()
60        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
61        self.graph.yaxis("\\rm{Intensity} ","cm^{-1}")
62        self.graph.render(self)
63       
64    def _onEVT_1DREPLOT(self, event):
65        """
66            Data is ready to be displayed
67            @param event: data event
68        """
69        #TODO: Check for existence of plot attribute
70       
71        is_new = True
72        if event.plot.name in self.plots.keys():
73            is_new = False
74       
75        if is_new:
76            self.plots[event.plot.name] = event.plot
77            self.graph.add(self.plots[event.plot.name])
78        else:
79            self.plots[event.plot.name].x = event.plot.x   
80            self.plots[event.plot.name].y = event.plot.y   
81            self.plots[event.plot.name].dy = event.plot.dy 
82            if hasattr(event.plot, 'dx') and hasattr(self.plots[event.plot.name], 'dx'):
83                self.plots[event.plot.name].dx = event.plot.dx   
84 
85       
86        # Check axis labels
87        #TODO: Should re-factor this
88        #if event.plot._xunit != self.graph.prop["xunit"]:
89        self.graph.xaxis(event.plot._xaxis, event.plot._xunit)
90           
91        #if event.plot._yunit != self.graph.prop["yunit"]:
92        self.graph.yaxis(event.plot._yaxis, event.plot._yunit)
93     
94        self.graph.render(self)
95        self.subplot.figure.canvas.draw_idle()
96
97    def onContextMenu(self, event):
98        """
99            1D plot context menu
100            @param event: wx context event
101        """
102        #slicerpop = wx.Menu()
103        slicerpop = PanelMenu()
104        slicerpop.set_plots(self.plots)
105               
106        # Option to save the data displayed
107        id = wx.NewId()
108        for plot in self.graph.plottables:
109            name = plot.name
110            slicerpop.Append(id, "&Save %s points" % name)
111            self.action_ids[str(id)] = plot
112            wx.EVT_MENU(self, id, self._onSave)
113               
114        # Various plot options
115        id = wx.NewId()
116        slicerpop.Append(id,'&Save image', 'Save image as PNG')
117        wx.EVT_MENU(self, id, self.onSaveImage)
118       
119        slicerpop.AppendSeparator()
120        item_list = self.parent.get_context_menu(self.graph)
121        if not item_list==None:
122            for item in item_list:
123                try:
124                    id = wx.NewId()
125                    slicerpop.Append(id, item[0], item[1])
126                    wx.EVT_MENU(self, id, item[2])
127                except:
128                    print sys.exc_value
129                    print RuntimeError, "View1DPanel.onContextMenu: bad menu item"
130       
131        slicerpop.AppendSeparator()
132       
133        #id = wx.NewId()
134        #slicerpop.Append(id, '&Toggle Linear/Log scale')
135        #wx.EVT_MENU(self, id, self._onToggleScale)
136
137        id = wx.NewId()
138        slicerpop.Append(id, '&Change scale')
139        wx.EVT_MENU(self, id, self._onProperties)
140
141        pos = event.GetPosition()
142        pos = self.ScreenToClient(pos)
143        self.PopupMenu(slicerpop, pos)
144   
145    def _onSave(self, evt):
146        """
147            Save a data set to a text file
148            @param evt: Menu event
149        """
150        import os
151        id = str(evt.GetId())
152        if id in self.action_ids:         
153           
154            path = None
155            dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*.txt", wx.SAVE)
156            if dlg.ShowModal() == wx.ID_OK:
157                path = dlg.GetPath()
158                mypath = os.path.basename(path)
159                print path
160            dlg.Destroy()
161           
162            if not path == None:
163                out = open(path, 'w')
164                has_errors = True
165                if self.action_ids[id].dy==None or self.action_ids[id].dy==[]:
166                    has_errors = False
167                   
168                # Sanity check
169                if has_errors:
170                    try:
171                        if len(self.action_ids[id].y) != len(self.action_ids[id].dy):
172                            print "Y and dY have different lengths"
173                            has_errors = False
174                    except:
175                        has_errors = False
176               
177                if has_errors:
178                    out.write("<X>   <Y>   <dY>\n")
179                else:
180                    out.write("<X>   <Y>\n")
181                   
182                for i in range(len(self.action_ids[id].x)):
183                    if has_errors:
184                        out.write("%g  %g  %g\n" % (self.action_ids[id].x[i], 
185                                                    self.action_ids[id].y[i],
186                                                    self.action_ids[id].dy[i]))
187                    else:
188                        out.write("%g  %g\n" % (self.action_ids[id].x[i], 
189                                                self.action_ids[id].y[i]))
190                       
191                out.close()
192   
193   
194    def _onToggleScale(self, event):
195        if self.get_yscale() == 'log':
196            self.set_yscale('linear')
197        else:
198            self.set_yscale('log')
199        self.subplot.figure.canvas.draw_idle()   
200   
201class Plugin:
202    """
203        Plug-in class to be instantiated by the GUI manager
204    """
205   
206    def __init__(self):
207        """
208            Initialize the plug-in
209        """
210        ## Plug-in name
211        self.sub_menu = "Plotting"
212       
213        ## Reference to the parent window
214        self.parent = None
215       
216        ## List of panels for the simulation perspective (names)
217        self.perspective = []
218       
219        ## Plot panels
220        self.plot_panels = []
221       
222
223    def populate_menu(self, id, parent):
224        """
225            Create a 'Plot' menu to list the panels
226            available for displaying
227            @param id: next available unique ID for wx events
228            @param parent: parent window
229        """
230        self.menu = wx.Menu()
231        return [(id, self.menu, "Plot")]
232   
233       
234    def get_panels(self, parent):
235        """
236            Create and return a list of panel objects
237        """
238        ## Save a reference to the parent
239        self.parent = parent
240        # Connect to plotting events
241        self.parent.Bind(EVT_NEW_PLOT, self._on_plot_event)
242       
243        # We have no initial panels for this plug-in
244        return []
245   
246    def get_perspective(self):
247        """
248            Get the list of panel names for this perspective
249        """
250        return self.perspective
251   
252    def on_perspective(self, event):
253        """
254            Call back function for the perspective menu item.
255            We notify the parent window that the perspective
256            has changed.
257            @param event: menu event
258        """
259        self.parent.set_perspective(self.perspective)
260   
261    def post_init(self):
262        """
263            Post initialization call back to close the loose ends
264            [Somehow openGL needs this call]
265        """
266        pass
267   
268    def _on_show_panel(self, event):
269        print "_on_show_panel"
270   
271    def _on_plot_event(self, event):
272        """
273            A new plottable is being shipped to the plotting plug-in.
274            Check whether we have a panel to put in on, or create
275            a new one
276            @param event: EVT_NEW_PLOT event
277        """
278        # Check whether we already have a graph with the same units
279        # as the plottable we just received.
280        is_available = False
281        for panel in self.plot_panels:
282            if event.plot._xunit == panel.graph.prop["xunit"] \
283            and event.plot._yunit == panel.graph.prop["yunit"]:
284                if hasattr(event.plot, "group_id"):
285                    if not event.plot.group_id==None \
286                        and event.plot.group_id==panel.group_id:
287                        is_available = True
288                        panel._onEVT_1DREPLOT(event)
289                        self.parent.show_panel(panel.uid)
290                else:
291                    # Check that the plot panel has no group ID
292                    if panel.group_id==None:
293                        is_available = True
294                        panel._onEVT_1DREPLOT(event)
295                        self.parent.show_panel(panel.uid)
296       
297        # Create a new plot panel if none was available       
298        if not is_available:
299            new_panel = View1DPanel(self.parent, -1, style=wx.RAISED_BORDER)
300            # Set group ID if available
301            group_id_str = ''
302            if hasattr(event.plot, "group_id"):
303                if not event.plot.group_id==None:
304                    new_panel.group_id = event.plot.group_id
305                    group_id_str = ' [%s]' % event.plot.group_id
306           
307            if hasattr(event, "title"):
308                new_panel.window_caption = event.title
309                new_panel.window_name = event.title
310                #new_panel.window_caption = event.title+group_id_str
311                #new_panel.window_name = event.title+group_id_str
312           
313            event_id = self.parent.popup_panel(new_panel)
314            self.menu.Append(event_id, new_panel.window_caption, 
315                             "Show %s plot panel" % new_panel.window_caption)
316           
317            # Set UID to allow us to reference the panel later
318            new_panel.uid = event_id
319           
320            # Ship the plottable to its panel
321            new_panel._onEVT_1DREPLOT(event)
322            self.plot_panels.append(new_panel)       
323           
324        return
325       
Note: See TracBrowser for help on using the repository browser.