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