source: sasview/guiframe/gui_manager.py @ 0954398

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

Added help menu entry for plug-ins and updated requirements

  • Property mode set to 100644
File size: 17.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"""
10import wx
11import wx.aui
12import os, sys
13try:
14    # Try to find a local config
15    import imp
16    path = os.getcwd()
17    if(os.path.isfile("%s/%s.py" % (path, 'config'))):
18            fObj, path, descr = imp.find_module('config', [path])
19            config = imp.load_module('config', fObj, path, descr)     
20except:
21    # Didn't find local config, load the default
22    import config
23from sans.guicomm.events import EVT_STATUS
24
25class ViewerFrame(wx.Frame):
26    """
27        Main application frame
28    """
29    def __init__(self, parent, id, title):
30        """
31            Initialize the Frame object
32        """
33        from local_perspectives.plotting import plotting
34        wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, size=(1000, 1000))
35       
36        path = os.path.dirname(__file__)
37        self.SetIcon(wx.Icon(os.path.join(path,'images/ball.ico'), wx.BITMAP_TYPE_ICO))
38       
39        ## Application manager
40        self.app_manager = None
41       
42        ## Find plug-ins
43        # Modify this so that we can specify the directory to look into
44        self.plugins = self._find_plugins()
45        self.plugins.append(plotting.Plugin())
46
47        ## List of panels
48        self.panels = {}
49
50        ## Next available ID for wx gui events
51        self.next_id = 20000
52
53        ## Default welcome panel
54        self.defaultPanel    = DefaultPanel(self, -1, style=wx.RAISED_BORDER)
55
56        # Set up the layout
57        self._setup_layout()
58       
59        # Set up the menu
60        self._setup_menus()
61       
62        self.Fit()
63       
64        # Register the close event so it calls our own method
65        wx.EVT_CLOSE(self, self._onClose)
66        # Register to status events
67        self.Bind(EVT_STATUS, self._on_status_event)
68             
69    def _setup_layout(self):
70        """
71            Set up the layout
72        """
73        # Status bar
74        self.sb = self.CreateStatusBar()
75        self.SetStatusText("")
76       
77        # Add panel
78        self._mgr = wx.aui.AuiManager(self)
79       
80        # Load panels
81        self._load_panels()
82       
83        self._mgr.Update()
84     
85    def _find_plugins(self, dir="perspectives"):
86        """
87            Find available perspective plug-ins
88            @param dir: directory in which to look for plug-ins
89            @return: list of plug-ins
90        """
91        import imp
92        print "Looking for plug-ins in %s" % dir
93        # List of plug-in objects
94        plugins = []
95        # Go through files in panels directory
96        try:
97            list = os.listdir(dir)
98            for item in list:
99                print item
100                toks = os.path.splitext(os.path.basename(item))
101                name = None
102                if not toks[0] == '__init__':
103                   
104                    if toks[1]=='.py' or toks[1]=='':
105                        name = toks[0]
106               
107                    path = [os.path.abspath(dir)]
108                    file = None
109                    try:
110                        if toks[1]=='':
111                            mod_path = '.'.join([dir, name])
112                            module = __import__(mod_path, globals(), locals(), [name])
113                        else:
114                            (file, path, info) = imp.find_module(name, path)
115                            print path
116                            module = imp.load_module( name, file, item, info )
117                        if hasattr(module, "PLUGIN_ID"):
118                            try:
119                                plugins.append(module.Plugin())
120                                print "Found plug-in: %s" % module.PLUGIN_ID
121                            except:
122                                config.printEVT("Error accessing PluginPanel in %s\n  %s" % (name, sys.exc_value))
123                       
124                    except:
125                        print sys.exc_value
126                    finally:
127                        if not file==None:
128                            file.close()
129        except:
130            # Should raise and catch at a higher level and display error on status bar
131            pass   
132        return plugins
133   
134       
135     
136    def _load_panels(self):
137        """
138            Load all panels in the panels directory
139        """
140       
141        # Look for plug-in panels
142        panels = []       
143        for item in self.plugins:
144            if hasattr(item, "get_panels"):
145                ps = item.get_panels(self)
146                panels.extend(ps)
147
148        # Show a default panel with some help information
149        # It also sets the size of the application windows
150        self.panels["default"] = self.defaultPanel
151        self._mgr.AddPane(self.defaultPanel, wx.aui.AuiPaneInfo().
152                              Name("default").
153                              CenterPane().
154                              BestSize(wx.Size(900,800)).
155                              MinSize(wx.Size(900,800)).
156                              Show())
157
158        # Add the panels to the AUI manager
159        for panel_class in panels:
160            p = panel_class
161            id = wx.NewId()
162           
163            # Check whether we need to put this panel
164            # in the center pane
165            if hasattr(p, "CENTER_PANE"):
166                if p.CENTER_PANE:
167                    self.panels[str(id)] = p
168                    self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
169                                          Name(p.window_name).Caption(p.window_caption).
170                                          CenterPane().
171                                          BestSize(wx.Size(600,600)).
172                                          MinSize(wx.Size(600,600)).
173                                          Hide())
174               
175            else:
176                self.panels[str(id)] = p
177                self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
178                                  Name(p.window_name).Caption(p.window_caption).
179                                  #Floatable().
180                                  #Float().
181                                  Right().
182                                  Dock().
183                                  TopDockable().
184                                  BottomDockable().
185                                  LeftDockable().
186                                  RightDockable().
187                                  MinimizeButton().
188                                  Hide().
189                                  #Show().
190                                  BestSize(wx.Size(400,400)).
191                                  MinSize(wx.Size(100,100)))
192               
193       
194    def get_context_menu(self):
195        """
196            Get the context menu items made available
197            by the different plug-ins.
198            This function is used by the plotting module
199        """
200        menu_list = []
201        for item in self.plugins:
202            if hasattr(item, "get_context_menu"):
203                menu_list.extend(item.get_context_menu())
204           
205        return menu_list
206       
207    def popup_panel(self, p):
208        """
209            Add a panel object to the AUI manager
210            @param p: panel object to add to the AUI manager
211            @return: ID of the event associated with the new panel [int]
212        """
213       
214        ID = wx.NewId()
215        self.panels[str(ID)] = p
216       
217        count = 0
218        for item in self.panels:
219            if self.panels[item].window_name.startswith(p.window_name): 
220                count += 1
221               
222        windowname = p.window_name
223        caption = p.window_caption
224        if count>0:
225            windowname += str(count+1)
226            caption += (' '+str(count))
227           
228        p.window_name = windowname
229        p.window_caption = caption
230           
231        self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
232                          Name(windowname).Caption(caption).
233                          Floatable().
234                          #Float().
235                          Right().
236                          Dock().
237                          TopDockable().
238                          BottomDockable().
239                          LeftDockable().
240                          RightDockable().
241                          MinimizeButton().
242                          #Hide().
243                          #Show().
244                          BestSize(wx.Size(400,400)).
245                          MinSize(wx.Size(200,200)))
246       
247        # Register for showing/hiding the panel
248        wx.EVT_MENU(self, ID, self._on_view)
249       
250        self._mgr.Update()
251        return ID
252       
253    def _setup_menus(self):
254        """
255            Set up the application menus
256        """
257        # Menu
258        menubar = wx.MenuBar()
259       
260        # File menu
261        filemenu = wx.Menu()
262        filemenu.Append(101,'&Quit', 'Exit') 
263       
264        # Plot menu
265        # Attach a menu item for each panel in our
266        # panel list that also appears in a plug-in.
267        # TODO: clean this up. We should just identify
268        # plug-in panels and add them all.
269        viewmenu = wx.Menu()
270       
271        for plug in self.plugins:
272            plugmenu = wx.Menu()
273            pers = plug.get_perspective()
274            if len(pers)>0:
275                for item in self.panels:
276                    if item == 'default':
277                        continue
278                    panel = self.panels[item]
279                    if panel.window_name in pers:
280                        plugmenu.Append(int(item), panel.window_caption, "Show %s window" % panel.window_caption)
281                        wx.EVT_MENU(self, int(item), self._on_view)
282               
283                viewmenu.AppendMenu(wx.NewId(), plug.sub_menu, plugmenu, plug.sub_menu)
284           
285        # Perspective
286        # Attach a menu item for each defined perspective.
287        p_menu = wx.Menu()
288       
289        for plug in self.plugins:
290            if len(plug.get_perspective()) > 0:
291                id = wx.NewId()
292                p_menu.Append(id, plug.sub_menu, "Switch to %s perspective" % plug.sub_menu)
293                wx.EVT_MENU(self, id, plug.on_perspective)
294 
295        # Help menu
296        helpmenu = wx.Menu()
297
298        # Look for help item in plug-ins
299        for item in self.plugins:
300            if hasattr(item, "help"):
301                id = wx.NewId()
302                helpmenu.Append(id,'&%s help' % item.sub_menu, '')
303                wx.EVT_MENU(self, id, item.help)
304       
305        if config._do_aboutbox:
306            id = wx.NewId()
307            helpmenu.Append(id,'&About', 'Software information')
308            wx.EVT_MENU(self, id, self._onAbout)
309        id = wx.NewId()
310        helpmenu.Append(id,'&Check for update', 'Check for the latest version of %s' % config.__appname__)
311        wx.EVT_MENU(self, id, self._check_update)
312       
313       
314        # Add sub menus
315        menubar.Append(filemenu,  '&File')
316       
317        # Look for plug-in menus
318        # Add available plug-in sub-menus.
319        for item in self.plugins:
320            if hasattr(item, "populate_menu"):
321                for (self.next_id, menu, name) in item.populate_menu(self.next_id, self):
322                    menubar.Append(menu, name)
323       
324
325        menubar.Append(viewmenu, '&Panel')
326        menubar.Append(p_menu,   '&Perspective')
327        menubar.Append(helpmenu, '&Help')
328         
329        self.SetMenuBar(menubar)
330       
331        # Bind handlers       
332        wx.EVT_MENU(self, 101, self.Close)
333       
334    def _on_status_event(self, evt):
335        """
336            Display status message
337        """
338        self.SetStatusText(str(evt.status))
339
340       
341    def _on_view(self, evt):
342        """
343            A panel was selected to be shown. If it's not already
344            shown, display it.
345            @param evt: menu event
346        """
347        self.show_panel(evt.GetId())
348
349    def show_panel(self, uid):
350        """
351            Shows the panel with the given id
352            @param uid: unique ID number of the panel to show
353        """
354        ID = str(uid)
355        config.printEVT("show_panel: %s" % ID)
356        if ID in self.panels.keys():
357            if not self._mgr.GetPane(self.panels[ID].window_name).IsShown():
358                self._mgr.GetPane(self.panels[ID].window_name).Show()
359                # Hide default panel
360                self._mgr.GetPane(self.panels["default"].window_name).Hide()
361               
362               
363            self._mgr.Update()
364       
365
366    def _onClose(self, event):
367        import sys
368        wx.Exit()
369        sys.exit()
370                   
371    def Close(self, event=None):
372        """
373            Quit the application
374        """
375        import sys
376        wx.Frame.Close(self)
377        wx.Exit()
378        sys.exit()
379
380 
381    def _check_update(self, event=None): 
382        """
383            Check with the deployment server whether a new version
384            of the application is available
385        """
386        import urllib
387        try: 
388            h = urllib.urlopen(config.__update_URL__)
389            lines = h.readlines()
390            line = ''
391            if len(lines)>0:
392                line = lines[0]
393               
394                toks = line.lstrip().rstrip().split('.')
395                toks_current = config.__version__.split('.')
396                update_available = False
397                for i in range(len(toks)):
398                    if int(toks[i])>int(toks_current[i]):
399                        update_available = True
400                if update_available:
401                    #print "Version %s is available" % line.rstrip().lstrip()
402                    self.SetStatusText("Version %s is available! See the Help menu to download it." % line.rstrip().lstrip())
403                    if event != None:
404                        import webbrowser
405                        webbrowser.open(config.__download_page__)
406                else:
407                    self.SetStatusText("You have the latest version of %s" % config.__appname__)
408                    #print "Server version = %s"  % line.rstrip().lstrip()
409        except:
410            self.SetStatusText("You have the latest version of %s" % config.__appname__)
411           
412           
413    def _onAbout(self, evt):
414        """
415            Pop up the about dialog
416            @param evt: menu event
417        """
418        if config._do_aboutbox:
419            import aboutbox 
420            dialog = aboutbox.DialogAbout(None, -1, "")
421            dialog.ShowModal()
422           
423    def set_manager(self, manager):
424        """
425            Sets the application manager for this frame
426            @param manager: frame manager
427        """
428        self.app_manager = manager
429       
430    def post_init(self):
431        """
432            This initialization method is called after the GUI
433            has been created and all plug-ins loaded. It calls
434            the post_init() method of each plug-in (if it exists)
435            so that final initialization can be done.
436        """
437        for item in self.plugins:
438            if hasattr(item, "post_init"):
439                item.post_init()
440       
441    def set_perspective(self, panels):
442        """
443            Sets the perspective of the GUI.
444            Opens all the panels in the list, and closes
445            all the others.
446           
447            @param panels: list of panels
448        """
449        print "gui_mng.set_perspective"
450        for item in self.panels:
451            # Check whether this is a sticky panel
452            if hasattr(self.panels[item], "ALWAYS_ON"):
453                if self.panels[item].ALWAYS_ON:
454                    continue 
455           
456            if self.panels[item].window_name in panels:
457                if not self._mgr.GetPane(self.panels[item].window_name).IsShown():
458                    self._mgr.GetPane(self.panels[item].window_name).Show()
459            else:
460                if self._mgr.GetPane(self.panels[item].window_name).IsShown():
461                    self._mgr.GetPane(self.panels[item].window_name).Hide()
462                 
463        self._mgr.Update()
464       
465    def choose_file(self):
466        """
467            Functionality that belongs elsewhere
468            Should add a hook to specify the preferred file type/extension.
469        """
470        #TODO: clean this up
471        from data_loader import choose_data_file
472        return choose_data_file(self)
473                 
474class DefaultPanel(wx.Panel):
475    """
476        Defines the API for a panels to work with
477        the GUI manager
478    """
479    ## Internal nickname for the window, used by the AUI manager
480    window_name = "default"
481    ## Name to appear on the window title bar
482    window_caption = "Welcome panel"
483    ## Flag to tell the AUI manager to put this panel in the center pane
484    CENTER_PANE = True
485
486 
487# Toy application to test this Frame
488class ViewApp(wx.App):
489    def OnInit(self):
490        #from gui_manager import ViewerFrame
491        self.frame = ViewerFrame(None, -1, config.__appname__)   
492        self.frame.Show(True)
493
494        if hasattr(self.frame, 'special'):
495            print "Special?", self.frame.special.__class__.__name__
496            self.frame.special.SetCurrent()
497        self.frame.post_init()
498        self.SetTopWindow(self.frame)
499        return True
500   
501    def set_manager(self, manager):
502        """
503            Sets a reference to the application manager
504            of the GUI manager (Frame)
505        """
506        self.frame.set_manager(manager)
507       
508
509if __name__ == "__main__": 
510    app = ViewApp(0)
511    app.MainLoop()             
Note: See TracBrowser for help on using the repository browser.