Index: guiframe/.project
===================================================================
--- guiframe/.project	(revision 41d466f6e37304375ace192af8fba17283851239)
+++ guiframe/.project	(revision 41d466f6e37304375ace192af8fba17283851239)
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>guiframe</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.python.pydev.PyDevBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.python.pydev.pythonNature</nature>
+	</natures>
+</projectDescription>
Index: guiframe/.pydevproject
===================================================================
--- guiframe/.pydevproject	(revision 41d466f6e37304375ace192af8fba17283851239)
+++ guiframe/.pydevproject	(revision 41d466f6e37304375ace192af8fba17283851239)
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?eclipse-pydev version="1.0"?>
+
+<pydev_project>
+<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
+<path>/guiframe/src</path>
+</pydev_pathproperty>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.5</pydev_property>
+</pydev_project>
Index: guiframe/aboutbox.py
===================================================================
--- guiframe/aboutbox.py	(revision 41d466f6e37304375ace192af8fba17283851239)
+++ guiframe/aboutbox.py	(revision 41d466f6e37304375ace192af8fba17283851239)
@@ -0,0 +1,236 @@
+#!/usr/bin/env python
+########################################################################
+#
+# PDFgui            by DANSE Diffraction group
+#                   Simon J. L. Billinge
+#                   (c) 2006 trustees of the Michigan State University.
+#                   All rights reserved.
+#
+# File coded by:    Dmitriy Bryndin
+#
+# See AUTHORS.txt for a list of people who contributed.
+# See LICENSE.txt for license information.
+#
+# Modified by U. Tennessee for DANSE/SANS
+########################################################################
+
+# version
+__id__ = "$Id: aboutdialog.py 1193 2007-05-03 17:29:59Z dmitriy $"
+__revision__ = "$Revision: 1193 $"
+
+import wx
+import wx.lib.hyperlink
+import random
+import os.path
+import os
+try:
+    # Try to find a local config
+    import imp
+    path = os.getcwd()
+    if(os.path.isfile("%s/%s.py" % (path, 'config'))):
+            fObj, path, descr = imp.find_module('config', [path])
+            config = imp.load_module('config', fObj, path, descr)     
+except:
+    import config
+
+def launchBrowser(url):
+    '''Launches browser and opens specified url
+    
+    In some cases may require BROWSER environment variable to be set up.
+    
+    @param url: URL to open
+    '''
+    import webbrowser
+    webbrowser.open(url)
+
+
+class DialogAbout(wx.Dialog):
+    '''"About" Dialog
+    
+    Shows product name, current version, authors, and link to the product page.
+    Current version is taken from version.py
+    '''
+    
+    def __init__(self, *args, **kwds):
+
+        # begin wxGlade: DialogAbout.__init__
+        kwds["style"] = wx.DEFAULT_DIALOG_STYLE
+        wx.Dialog.__init__(self, *args, **kwds)
+        
+        file_dir = os.path.dirname(__file__)
+        
+        # Mac doesn't display images with transparent background so well, keep it for Windows
+        image = file_dir+"/images/angles_flat.png"
+        if os.path.isfile(config._corner_image):
+            image = config._corner_image
+
+        if os.name == 'nt':
+            self.bitmap_logo = wx.StaticBitmap(self, -1, wx.Bitmap(image))
+        else:
+            self.bitmap_logo = wx.StaticBitmap(self, -1, wx.Bitmap(image))
+        
+        self.label_title = wx.StaticText(self, -1, config.__appname__)
+        self.label_version = wx.StaticText(self, -1, "")
+        self.label_build = wx.StaticText(self, -1, "Build:")
+        self.label_svnrevision = wx.StaticText(self, -1, "")
+        self.label_copyright = wx.StaticText(self, -1, config._copyright)
+        self.label_author = wx.StaticText(self, -1, "authors")
+        self.hyperlink = wx.lib.hyperlink.HyperLinkCtrl(self, -1, config._homepage, URL=config._homepage)
+        #self.hyperlink_license = wx.lib.hyperlink.HyperLinkCtrl(self, -1, "Comments? Bugs? Requests?", URL=config._paper)
+        self.hyperlink_license = wx.StaticText(self, -1, "Comments? Bugs? Requests?")
+        self.hyperlink_paper = wx.lib.hyperlink.HyperLinkCtrl(self, -1, "Send us a ticket", URL=config._license)
+        self.hyperlink_download = wx.lib.hyperlink.HyperLinkCtrl(self, -1, "Get the latest version", URL=config._download)
+        self.static_line_1 = wx.StaticLine(self, -1)
+        self.label_acknowledgement = wx.StaticText(self, -1, config._acknowledgement)
+        self.static_line_2 = wx.StaticLine(self, -1)
+        self.bitmap_button_nsf = wx.BitmapButton(self, -1, wx.NullBitmap)
+        self.bitmap_button_danse = wx.BitmapButton(self, -1, wx.NullBitmap)
+        self.bitmap_button_msu = wx.BitmapButton(self, -1, wx.NullBitmap)
+        self.static_line_3 = wx.StaticLine(self, -1)
+        self.button_OK = wx.Button(self, wx.ID_OK, "OK")
+
+        self.__set_properties()
+        self.__do_layout()
+
+        self.Bind(wx.EVT_BUTTON, self.onNsfLogo, self.bitmap_button_nsf)
+        self.Bind(wx.EVT_BUTTON, self.onDanseLogo, self.bitmap_button_danse)
+        self.Bind(wx.EVT_BUTTON, self.onUTLogo, self.bitmap_button_msu)
+        # end wxGlade
+        
+        # fill in acknowledgements
+#       self.text_ctrl_acknowledgement.SetValue(__acknowledgement__)
+
+        # randomly shuffle authors' names
+        random.shuffle(config._authors)
+        strLabel = ", ".join(config._authors)
+        
+        # display version and svn revison numbers
+        verwords = config.__version__.split('.')
+        version = '.'.join(verwords[:-1])
+        revision = verwords[-1]
+        
+        self.label_author.SetLabel(strLabel)
+        self.label_version.SetLabel(version)
+        self.label_svnrevision.SetLabel(config.__version__)
+        
+        # set bitmaps for logo buttons
+        image = file_dir+"/images/nsf_logo.png"
+        if os.path.isfile(config._nsf_logo):
+            image = config._nsf_logo
+        logo = wx.Bitmap(image)        
+        self.bitmap_button_nsf.SetBitmapLabel(logo)
+
+        image = file_dir+"/images/danse_logo.png"
+        if os.path.isfile(config._danse_logo):
+            image = config._danse_logo
+        logo = wx.Bitmap(image)
+        self.bitmap_button_danse.SetBitmapLabel(logo)
+        
+        image = file_dir+"/images/utlogo.gif"
+        if os.path.isfile(config._inst_logo):
+            image = config._inst_logo
+        logo = wx.Bitmap(image)
+        self.bitmap_button_msu.SetBitmapLabel(logo)
+        
+        # resize dialog window to fit version number nicely
+        if wx.VERSION >= (2,7,2,0):
+            size = [self.GetEffectiveMinSize()[0], self.GetSize()[1]]
+        else:
+            size = [self.GetBestFittingSize()[0], self.GetSize()[1]]
+
+        self.Fit()
+#        self.SetSize(size)
+#       self.FitInside()
+        
+
+    def __set_properties(self):
+        # begin wxGlade: DialogAbout.__set_properties
+        self.SetTitle("About")
+        self.SetSize((600, 595))
+        self.label_title.SetFont(wx.Font(26, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
+        self.label_version.SetFont(wx.Font(26, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, ""))
+        #self.hyperlink_license.Enable(False)
+        #self.hyperlink_license.Hide()
+        self.hyperlink_paper.Enable(True)
+        #self.hyperlink_paper.Hide()
+        self.bitmap_button_nsf.SetSize(self.bitmap_button_nsf.GetBestSize())
+        self.bitmap_button_danse.SetSize(self.bitmap_button_danse.GetBestSize())
+        self.bitmap_button_msu.SetSize(self.bitmap_button_msu.GetBestSize())
+        # end wxGlade
+
+    def __do_layout(self):
+        # begin wxGlade: DialogAbout.__do_layout
+        sizer_main = wx.BoxSizer(wx.VERTICAL)
+        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
+        sizer_logos = wx.BoxSizer(wx.HORIZONTAL)
+        sizer_header = wx.BoxSizer(wx.HORIZONTAL)
+        sizer_titles = wx.BoxSizer(wx.VERTICAL)
+        sizer_build = wx.BoxSizer(wx.HORIZONTAL)
+        sizer_title = wx.BoxSizer(wx.HORIZONTAL)
+        sizer_header.Add(self.bitmap_logo, 0, wx.EXPAND, 0)
+        sizer_title.Add(self.label_title, 0, wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 10)
+        sizer_title.Add((20, 20), 0, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        sizer_title.Add(self.label_version, 0, wx.RIGHT|wx.ALIGN_BOTTOM|wx.ADJUST_MINSIZE, 10)
+        sizer_titles.Add(sizer_title, 0, wx.EXPAND, 0)
+        sizer_build.Add(self.label_build, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
+        sizer_build.Add(self.label_svnrevision, 0, wx.ADJUST_MINSIZE, 0)
+        sizer_titles.Add(sizer_build, 0, wx.TOP|wx.EXPAND, 5)
+        sizer_titles.Add(self.label_copyright, 0, wx.LEFT|wx.RIGHT|wx.TOP|wx.ADJUST_MINSIZE, 10)
+        sizer_titles.Add(self.label_author, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
+        sizer_titles.Add(self.hyperlink, 0, wx.LEFT|wx.RIGHT, 10)
+        sizer_titles.Add((20, 20), 0, wx.ADJUST_MINSIZE, 0)
+        sizer_titles.Add(self.hyperlink_license, 0, wx.LEFT|wx.RIGHT, 10)
+        sizer_titles.Add(self.hyperlink_paper, 0, wx.LEFT|wx.RIGHT, 10)
+        sizer_titles.Add((20, 20), 0, wx.ADJUST_MINSIZE, 0)
+        sizer_titles.Add(self.hyperlink_download, 0, wx.LEFT|wx.RIGHT, 10)
+        sizer_header.Add(sizer_titles, 0, wx.EXPAND, 0)
+        sizer_main.Add(sizer_header, 0, wx.BOTTOM|wx.EXPAND, 3)
+        sizer_main.Add(self.static_line_1, 0, wx.EXPAND, 0)
+        sizer_main.Add(self.label_acknowledgement, 0, wx.LEFT|wx.TOP|wx.BOTTOM|wx.ADJUST_MINSIZE, 7)
+        sizer_main.Add(self.static_line_2, 0, wx.EXPAND, 0)
+        sizer_logos.Add(self.bitmap_button_nsf, 0, wx.LEFT|wx.ADJUST_MINSIZE, 2)
+        sizer_logos.Add(self.bitmap_button_danse, 0, wx.LEFT|wx.ADJUST_MINSIZE, 2)
+        sizer_logos.Add(self.bitmap_button_msu, 0, wx.LEFT|wx.ADJUST_MINSIZE, 2)
+        sizer_logos.Add((50, 50), 0, wx.ADJUST_MINSIZE, 0)
+        sizer_main.Add(sizer_logos, 0, wx.EXPAND, 0)
+        sizer_main.Add(self.static_line_3, 0, wx.EXPAND, 0)
+        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        sizer_button.Add(self.button_OK, 0, wx.RIGHT|wx.ADJUST_MINSIZE, 10)
+        sizer_main.Add(sizer_button, 0, wx.EXPAND, 0)
+        self.SetAutoLayout(True)
+        self.SetSizer(sizer_main)
+        self.Layout()
+        self.Centre()
+        # end wxGlade
+
+    def onNsfLogo(self, event): # wxGlade: DialogAbout.<event_handler>
+        launchBrowser(config._nsf_url)
+        event.Skip()
+
+    def onDanseLogo(self, event): # wxGlade: DialogAbout.<event_handler>
+        launchBrowser(config._danse_url)
+        event.Skip()
+
+    def onUTLogo(self, event): # wxGlade: DialogAbout.<event_handler>
+        launchBrowser(config._inst_url)
+        event.Skip()
+
+# end of class DialogAbout
+
+##### testing code ############################################################
+class MyApp(wx.App):
+    def OnInit(self):
+        wx.InitAllImageHandlers()
+        dialog = DialogAbout(None, -1, "")
+        self.SetTopWindow(dialog)
+        dialog.ShowModal()
+        dialog.Destroy()
+        return 1
+
+# end of class MyApp
+
+if __name__ == "__main__":
+    app = MyApp(0)
+    app.MainLoop()
+    
+##### end of testing code #####################################################    
Index: guiframe/config.py
===================================================================
--- guiframe/config.py	(revision 41d466f6e37304375ace192af8fba17283851239)
+++ guiframe/config.py	(revision 41d466f6e37304375ace192af8fba17283851239)
@@ -0,0 +1,55 @@
+"""
+    Application settings
+"""
+import time
+
+# Version of the application
+__appname__ = "DummyView"
+__version__ = '0.1.0'
+__download_page__ = 'http://danse.chem.utk.edu'
+
+
+# Debug message flag
+__EVT_DEBUG__ = True
+
+# Flag for automated testing
+__TEST__ = False
+
+# Debug message should be written to a file?
+__EVT_DEBUG_2_FILE__   = False
+__EVT_DEBUG_FILENAME__ = "debug.log"
+
+# About box info
+_do_aboutbox = True
+_acknowledgement =  \
+'''This software was developed by the University of Tennessee as part of the
+Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
+project funded by the US National Science Foundation. 
+
+'''
+_homepage = "http://danse.chem.utk.edu"
+_download = "http://danse.chem.utk.edu/sansview.html"
+_authors = []
+_paper = "http://danse.us/trac/sans/newticket"
+_license = "mailto:sansdanse@gmail.com"
+_nsf_logo = "images/nsf_logo.png"
+_danse_logo = "images/danse_logo.png"
+_inst_logo = "images/utlogo.gif"
+_nsf_url = "http://www.nsf.gov"
+_danse_url = "http://www.cacr.caltech.edu/projects/danse/release/index.html"
+_inst_url = "http://www.utk.edu"
+_corner_image = "images/angles_flat.png"
+_copyright = "(c) 2008, University of Tennessee"
+
+import wx.lib.newevent
+(StatusBarEvent, EVT_STATUS) = wx.lib.newevent.NewEvent()
+
+def printEVT(message):
+    if __EVT_DEBUG__:
+        print "%g:  %s" % (time.clock(), message)
+        
+        if __EVT_DEBUG_2_FILE__:
+            out = open(__EVT_DEBUG_FILENAME__, 'a')
+            out.write("%10g:  %s\n" % (time.clock(), message))
+            out.close()
+            
Index: guiframe/dummyapp.py
===================================================================
--- guiframe/dummyapp.py	(revision 41d466f6e37304375ace192af8fba17283851239)
+++ guiframe/dummyapp.py	(revision 41d466f6e37304375ace192af8fba17283851239)
@@ -0,0 +1,23 @@
+"""
+    Dummy application.
+    Allows the user to set an external data manager
+"""
+import gui_manager
+
+class SansView():
+    
+    def __init__(self):
+        """
+            Initialization
+        """
+        from gui_manager import ViewApp
+        self.gui = ViewApp(0)
+        
+        # Set the application manager for the GUI
+        self.gui.set_manager(self)
+        
+        # Start the main loop
+        self.gui.MainLoop()  
+
+if __name__ == "__main__": 
+    sansview = SansView()
Index: guiframe/gui_manager.py
===================================================================
--- guiframe/gui_manager.py	(revision 41d466f6e37304375ace192af8fba17283851239)
+++ guiframe/gui_manager.py	(revision 41d466f6e37304375ace192af8fba17283851239)
@@ -0,0 +1,499 @@
+"""
+This software was developed by the University of Tennessee as part of the
+Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
+project funded by the US National Science Foundation. 
+
+See the license text in license.txt
+
+copyright 2008, University of Tennessee
+"""
+import wx
+import wx.aui
+import os, sys
+try:
+    # Try to find a local config
+    import imp
+    path = os.getcwd()
+    if(os.path.isfile("%s/%s.py" % (path, 'config'))):
+            fObj, path, descr = imp.find_module('config', [path])
+            config = imp.load_module('config', fObj, path, descr)     
+except:
+    # Didn't find local config, load the default 
+    import config
+from sans.guicomm.events import EVT_STATUS
+
+class ViewerFrame(wx.Frame):
+    """
+        Main application frame
+    """
+    def __init__(self, parent, id, title):
+        """
+            Initialize the Frame object
+        """
+        from local_perspectives.plotting import plotting
+        wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, size=(1000, 1000))
+        
+        path = os.path.dirname(__file__)
+        self.SetIcon(wx.Icon(os.path.join(path,'images/ball.ico'), wx.BITMAP_TYPE_ICO))
+        
+        ## Application manager
+        self.app_manager = None
+        
+        ## Find plug-ins
+        # Modify this so that we can specify the directory to look into
+        self.plugins = self._find_plugins()
+        self.plugins.append(plotting.Plugin())
+
+        ## List of panels
+        self.panels = {}
+
+        ## Next available ID for wx gui events 
+        self.next_id = 20000
+
+        ## Default welcome panel
+        self.defaultPanel    = DefaultPanel(self, -1, style=wx.RAISED_BORDER)
+
+        # Set up the layout
+        self._setup_layout()
+        
+        # Set up the menu
+        self._setup_menus()
+        
+        self.Fit()
+       
+        # Register the close event so it calls our own method
+        wx.EVT_CLOSE(self, self._onClose)
+        # Register to status events
+        self.Bind(EVT_STATUS, self._on_status_event)
+             
+    def _setup_layout(self):
+        """
+            Set up the layout
+        """
+        # Status bar
+        self.sb = self.CreateStatusBar()
+        self.SetStatusText("")
+        
+        # Add panel
+        self._mgr = wx.aui.AuiManager(self)
+        
+        # Load panels
+        self._load_panels()
+        
+        self._mgr.Update()
+      
+    def _find_plugins(self, dir="perspectives"):
+        """
+            Find available perspective plug-ins
+            @param dir: directory in which to look for plug-ins
+            @return: list of plug-ins
+        """
+        import imp
+        print "Looking for plug-ins in %s" % dir
+        # List of plug-in objects
+        plugins = []
+        # Go through files in panels directory
+        try:
+            list = os.listdir(dir)
+            for item in list:
+                print item
+                toks = os.path.splitext(os.path.basename(item))
+                name = None
+                if not toks[0] == '__init__':
+                    
+                    if toks[1]=='.py' or toks[1]=='':
+                        name = toks[0]
+                
+                    path = [os.path.abspath(dir)]
+                    file = None
+                    try:
+                        if toks[1]=='':
+                            mod_path = '.'.join([dir, name])
+                            module = __import__(mod_path, globals(), locals(), [name])
+                        else:
+                            (file, path, info) = imp.find_module(name, path)
+                            print path
+                            module = imp.load_module( name, file, item, info )
+                        if hasattr(module, "PLUGIN_ID"):
+                            try:
+                                plugins.append(module.Plugin())
+                                print "Found plug-in: %s" % module.PLUGIN_ID
+                            except:
+                                config.printEVT("Error accessing PluginPanel in %s\n  %s" % (name, sys.exc_value))
+                        
+                    except:
+                        print sys.exc_value
+                    finally:
+                        if not file==None:
+                            file.close()
+        except:
+            # Should raise and catch at a higher level and display error on status bar
+            pass   
+        return plugins
+    
+        
+      
+    def _load_panels(self):
+        """
+            Load all panels in the panels directory
+        """
+        
+        # Look for plug-in panels
+        panels = []        
+        for item in self.plugins:
+            if hasattr(item, "get_panels"):
+                ps = item.get_panels(self)
+                panels.extend(ps)
+
+        # Show a default panel with some help information
+        # It also sets the size of the application windows
+        self.panels["default"] = self.defaultPanel
+        self._mgr.AddPane(self.defaultPanel, wx.aui.AuiPaneInfo().
+                              Name("default").
+                              CenterPane().
+                              BestSize(wx.Size(900,800)).
+                              MinSize(wx.Size(900,800)).
+                              Show())
+
+        # Add the panels to the AUI manager
+        for panel_class in panels:
+            p = panel_class
+            id = wx.NewId()
+            
+            # Check whether we need to put this panel
+            # in the center pane
+            if hasattr(p, "CENTER_PANE"):
+                if p.CENTER_PANE:
+                    self.panels[str(id)] = p
+                    self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
+                                          Name(p.window_name).Caption(p.window_caption).
+                                          CenterPane().
+                                          BestSize(wx.Size(600,600)).
+                                          MinSize(wx.Size(600,600)).
+                                          Hide())
+                
+            else:
+                self.panels[str(id)] = p
+                self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
+                                  Name(p.window_name).Caption(p.window_caption).
+                                  #Floatable().
+                                  #Float().
+                                  Right().
+                                  Dock().
+                                  TopDockable().
+                                  BottomDockable().
+                                  LeftDockable().
+                                  RightDockable().
+                                  MinimizeButton().
+                                  Hide().
+                                  #Show().
+                                  BestSize(wx.Size(400,400)).
+                                  MinSize(wx.Size(100,100)))
+                
+        
+    def get_context_menu(self):
+        """
+            Get the context menu items made available 
+            by the different plug-ins. 
+            This function is used by the plotting module
+        """
+        menu_list = []
+        for item in self.plugins:
+            if hasattr(item, "get_context_menu"):
+                menu_list.extend(item.get_context_menu())
+            
+        return menu_list
+        
+    def popup_panel(self, p):
+        """
+            Add a panel object to the AUI manager
+            @param p: panel object to add to the AUI manager
+            @return: ID of the event associated with the new panel [int]
+        """
+        
+        ID = wx.NewId()
+        self.panels[str(ID)] = p
+        
+        count = 0
+        for item in self.panels:
+            if self.panels[item].window_name == p.window_name:
+                count += 1
+                
+        windowname = p.window_name
+        caption = p.window_caption
+        if count>0:
+            windowname += str(count+1)
+            caption += (' '+str(count))
+            
+        p.window_name = windowname
+        p.window_caption = caption
+            
+        self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
+                          Name(windowname).Caption(caption).
+                          Floatable().
+                          #Float().
+                          Right().
+                          Dock().
+                          TopDockable().
+                          BottomDockable().
+                          LeftDockable().
+                          RightDockable().
+                          MinimizeButton().
+                          #Hide().
+                          #Show().
+                          BestSize(wx.Size(400,400)).
+                          MinSize(wx.Size(200,200)))
+        
+        # Register for showing/hiding the panel
+        wx.EVT_MENU(self, ID, self._on_view)
+        
+        self._mgr.Update()
+        return ID
+        
+    def _setup_menus(self):
+        """
+            Set up the application menus
+        """
+        # Menu
+        menubar = wx.MenuBar()
+        
+        # File menu
+        filemenu = wx.Menu()
+        filemenu.Append(101,'&Quit', 'Exit') 
+        
+        # Plot menu
+        # Attach a menu item for each panel in our
+        # panel list that also appears in a plug-in.
+        # TODO: clean this up. We should just identify
+        # plug-in panels and add them all.
+        viewmenu = wx.Menu()
+        
+        for plug in self.plugins:
+            plugmenu = wx.Menu()
+            pers = plug.get_perspective()
+            if len(pers)>0:
+                for item in self.panels:
+                    if item == 'default':
+                        continue
+                    panel = self.panels[item]
+                    if panel.window_name in pers:
+                        plugmenu.Append(int(item), panel.window_caption, "Show %s window" % panel.window_caption)
+                        wx.EVT_MENU(self, int(item), self._on_view)
+                
+                viewmenu.AppendMenu(wx.NewId(), plug.sub_menu, plugmenu, plug.sub_menu)
+            
+        # Perspective
+        # Attach a menu item for each defined perspective.
+        p_menu = wx.Menu()
+        
+        for plug in self.plugins:
+            if len(plug.get_perspective()) > 0:
+                id = wx.NewId()
+                p_menu.Append(id, plug.sub_menu, "Switch to %s perspective" % plug.sub_menu)
+                wx.EVT_MENU(self, id, plug.on_perspective)
+ 
+        # Help menu
+        helpmenu = wx.Menu()
+        if config._do_aboutbox:
+            id = wx.NewId()
+            helpmenu.Append(id,'&About', 'Software information')
+            wx.EVT_MENU(self, id, self._onAbout)
+        id = wx.NewId()
+        helpmenu.Append(id,'&Check for update', 'Check for the latest version of %s' % config.__appname__)
+        wx.EVT_MENU(self, id, self._check_update)
+        
+        
+        # Add sub menus
+        menubar.Append(filemenu,  '&File')
+        
+        # Look for plug-in menus
+        # Add available plug-in sub-menus. 
+        for item in self.plugins:
+            if hasattr(item, "populate_menu"):
+                for (self.next_id, menu, name) in item.populate_menu(self.next_id, self):
+                    menubar.Append(menu, name)
+        
+
+        menubar.Append(viewmenu, '&Panel')
+        menubar.Append(p_menu,   '&Perspective')
+        menubar.Append(helpmenu, '&Help')
+         
+        self.SetMenuBar(menubar)
+        
+        # Bind handlers       
+        wx.EVT_MENU(self, 101, self.Close)
+        
+    def _on_status_event(self, evt):
+        """
+            Display status message
+        """
+        self.SetStatusText(str(evt.status))
+
+        
+    def _on_view(self, evt):
+        """
+            A panel was selected to be shown. If it's not already
+            shown, display it.
+            @param evt: menu event
+        """
+        self.show_panel(evt.GetId())
+
+    def show_panel(self, uid):
+        """
+            Shows the panel with the given id
+            @param uid: unique ID number of the panel to show
+        """
+        ID = str(uid)
+        config.printEVT("show_panel: %s" % ID)
+        if ID in self.panels.keys():
+            if not self._mgr.GetPane(self.panels[ID].window_name).IsShown():
+                self._mgr.GetPane(self.panels[ID].window_name).Show()
+            self._mgr.Update()
+        
+
+    def _onClose(self, event):
+        import sys
+        wx.Exit()
+        sys.exit()
+                   
+    def Close(self, event=None):
+        """
+            Quit the application
+        """
+        import sys
+        wx.Frame.Close(self)
+        wx.Exit()
+        sys.exit()
+
+  
+    def _check_update(self, event=None): 
+        """
+            Check with the deployment server whether a new version
+            of the application is available
+        """
+        import urllib
+        try: 
+            h = urllib.urlopen(config.__update_URL__)
+            lines = h.readlines()
+            line = ''
+            if len(lines)>0:
+                line = lines[0]
+                
+                toks = line.lstrip().rstrip().split('.')
+                toks_current = config.__version__.split('.')
+                update_available = False
+                for i in range(len(toks)):
+                    if int(toks[i])>int(toks_current[i]):
+                        update_available = True
+                if update_available:
+                    #print "Version %s is available" % line.rstrip().lstrip()
+                    self.SetStatusText("Version %s is available! See the Help menu to download it." % line.rstrip().lstrip())
+                    if event != None:
+                        import webbrowser
+                        webbrowser.open(config.__download_page__)
+                else:
+                    self.SetStatusText("You have the latest version of %s" % config.__appname__)
+                    #print "Server version = %s"  % line.rstrip().lstrip()
+        except:
+            self.SetStatusText("You have the latest version of %s" % config.__appname__)
+            
+            
+    def _onAbout(self, evt):
+        """
+            Pop up the about dialog
+            @param evt: menu event
+        """
+        if config._do_aboutbox:
+            import aboutbox 
+            dialog = aboutbox.DialogAbout(None, -1, "")
+            dialog.ShowModal()
+            
+    def set_manager(self, manager):
+        """
+            Sets the application manager for this frame
+            @param manager: frame manager
+        """
+        self.app_manager = manager
+        
+    def post_init(self):
+        """
+            This initialization method is called after the GUI 
+            has been created and all plug-ins loaded. It calls
+            the post_init() method of each plug-in (if it exists)
+            so that final initialization can be done.
+        """
+        for item in self.plugins:
+            if hasattr(item, "post_init"):
+                item.post_init()
+        
+    def set_perspective(self, panels):
+        """
+            Sets the perspective of the GUI.
+            Opens all the panels in the list, and closes
+            all the others.
+            
+            @param panels: list of panels
+        """
+        print "gui_mng.set_perspective"
+        for item in self.panels:
+            # Check whether this is a sticky panel
+            if hasattr(self.panels[item], "ALWAYS_ON"):
+                if self.panels[item].ALWAYS_ON:
+                    continue 
+            
+            if self.panels[item].window_name in panels:
+                if not self._mgr.GetPane(self.panels[item].window_name).IsShown():
+                    self._mgr.GetPane(self.panels[item].window_name).Show()
+            else:
+                if self._mgr.GetPane(self.panels[item].window_name).IsShown():
+                    self._mgr.GetPane(self.panels[item].window_name).Hide()
+                 
+        self._mgr.Update()
+        
+    def choose_file(self):
+        """ 
+            Functionality that belongs elsewhere
+            Should add a hook to specify the preferred file type/extension.
+        """
+        #TODO: clean this up
+        from data_loader import choose_data_file
+        return choose_data_file(self)
+                  
+class DefaultPanel(wx.Panel):
+    """
+        Defines the API for a panels to work with
+        the GUI manager
+    """
+    ## Internal nickname for the window, used by the AUI manager
+    window_name = "default"
+    ## Name to appear on the window title bar
+    window_caption = "Welcome panel"
+    ## Flag to tell the AUI manager to put this panel in the center pane
+    CENTER_PANE = True
+
+  
+# Toy application to test this Frame
+class ViewApp(wx.App):
+    def OnInit(self):
+        #from gui_manager import ViewerFrame
+        self.frame = ViewerFrame(None, -1, config.__appname__)    
+        self.frame.Show(True)
+
+        if hasattr(self.frame, 'special'):
+            print "Special?", self.frame.special.__class__.__name__
+            self.frame.special.SetCurrent()
+        self.frame.post_init()
+        self.SetTopWindow(self.frame)
+        return True
+    
+    def set_manager(self, manager):
+        """
+            Sets a reference to the application manager
+            of the GUI manager (Frame) 
+        """
+        self.frame.set_manager(manager)
+        
+
+if __name__ == "__main__": 
+    app = ViewApp(0)
+    app.MainLoop()              
Index: guiframe/license.txt
===================================================================
--- guiframe/license.txt	(revision 41d466f6e37304375ace192af8fba17283851239)
+++ guiframe/license.txt	(revision 41d466f6e37304375ace192af8fba17283851239)
@@ -0,0 +1,15 @@
+This software was developed by the University of Tennessee as part of the
+Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
+project funded by the US National Science Foundation. 
+
+If you use this program to do productive scientific research that leads
+to publication, we ask that you acknowledge use of the program by citing
+the following paper in your publication:
+
+P. Butler, M. Doucet, G. Alina, J. Zhou, "SANS analysis software", 
+to be published.
+
+See the DANSE project web site for license information:
+http://wiki.cacr.caltech.edu/danse/index.php/Main_Page
+
+copyright 2008, University of Tennessee for the DANSE project
Index: guiframe/local_perspectives/plotting/__init__.py
===================================================================
--- guiframe/local_perspectives/plotting/__init__.py	(revision 41d466f6e37304375ace192af8fba17283851239)
+++ guiframe/local_perspectives/plotting/__init__.py	(revision 41d466f6e37304375ace192af8fba17283851239)
@@ -0,0 +1,2 @@
+PLUGIN_ID = "Plotting plug-in 1.0"
+from plotting import *
Index: guiframe/local_perspectives/plotting/plotting.py
===================================================================
--- guiframe/local_perspectives/plotting/plotting.py	(revision 41d466f6e37304375ace192af8fba17283851239)
+++ guiframe/local_perspectives/plotting/plotting.py	(revision 41d466f6e37304375ace192af8fba17283851239)
@@ -0,0 +1,241 @@
+"""
+This software was developed by the University of Tennessee as part of the
+Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
+project funded by the US National Science Foundation. 
+
+See the license text in license.txt
+
+copyright 2008, University of Tennessee
+"""
+
+
+import wx
+import sys
+from sans.guitools.PlotPanel import PlotPanel
+from sans.guitools.plottables import Graph
+from sans.guicomm.events import EVT_NEW_PLOT
+
+class PanelMenu(wx.Menu):
+    plots = None
+    
+    def set_plots(self, plots):
+        self.plots = plots
+    
+
+class View1DPanel(PlotPanel):
+    """
+        Plot panel for use with the GUI manager
+    """
+    
+    ## Internal name for the AUI manager
+    window_name = "plotpanel"
+    ## Title to appear on top of the window
+    window_caption = "Plot Panel"
+    ## Flag to tell the GUI manager that this panel is not
+    #  tied to any perspective
+    ALWAYS_ON = True
+    
+    def __init__(self, parent, id = -1, color = None,\
+        dpi = None, style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
+        """
+            Initialize the panel
+        """
+        PlotPanel.__init__(self, parent, id = id, style = style, **kwargs)
+        
+        ## Reference to the parent window
+        self.parent = parent
+        ## Plottables
+        self.plots = {}
+        
+        ## Unique ID (from gui_manager)
+        self.uid = None
+        
+        ## Graph        
+        self.graph = Graph()
+        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
+        self.graph.yaxis("\\rm{Intensity} ","cm^{-1}")
+        self.graph.render(self)
+        
+    def _onEVT_1DREPLOT(self, event):
+        """
+            Data is ready to be displayed
+            @param event: data event
+        """
+        #TODO: Check for existence of plot attribute
+        
+        is_new = True
+        if event.plot.name in self.plots.keys():
+            is_new = False
+        
+        if is_new:
+            self.plots[event.plot.name] = event.plot
+            self.graph.add(self.plots[event.plot.name])
+        else:
+            self.plots[event.plot.name].x = event.plot.x    
+            self.plots[event.plot.name].y = event.plot.y    
+            self.plots[event.plot.name].dy = event.plot.dy  
+            if hasattr(event.plot, 'dx') and hasattr(self.plots[event.plot.name], 'dx'):
+                self.plots[event.plot.name].dx = event.plot.dx    
+ 
+        
+        # Check axis labels
+        #TODO: Should re-factor this
+        if event.plot._xunit != self.graph.prop["xunit"]:
+            self.graph.xaxis(event.plot._xaxis, event.plot._xunit)
+            
+        if event.plot._yunit != self.graph.prop["yunit"]:
+            self.graph.yaxis(event.plot._yaxis, event.plot._yunit)
+      
+        self.graph.render(self)
+        self.subplot.figure.canvas.draw_idle()
+
+    def onContextMenu(self, event):
+        """
+            1D plot context menu
+            @param event: wx context event
+        """
+        #slicerpop = wx.Menu()
+        slicerpop = PanelMenu()
+        slicerpop.set_plots(self.plots)
+                
+        # Various plot options
+        id = wx.NewId()
+        slicerpop.Append(id,'&Save image', 'Save image as PNG')
+        wx.EVT_MENU(self, id, self.onSaveImage)
+        
+        slicerpop.AppendSeparator()
+        item_list = self.parent.get_context_menu()
+        if not item_list==None:
+            for item in item_list:
+                try:
+                    id = wx.NewId()
+                    slicerpop.Append(id, item[0], item[1])
+                    wx.EVT_MENU(self, id, item[2])
+                except:
+                    print sys.exc_value
+                    print RuntimeError, "View1DPanel.onContextMenu: bad menu item"
+        
+        slicerpop.AppendSeparator()
+        
+        id = wx.NewId()
+        slicerpop.Append(id, '&Toggle Linear/Log scale')
+        wx.EVT_MENU(self, id, self._onToggleScale)
+
+        pos = event.GetPosition()
+        pos = self.ScreenToClient(pos)
+        self.PopupMenu(slicerpop, pos)
+    
+    def _onToggleScale(self, event):
+        if self.get_yscale() == 'log':
+            self.set_yscale('linear')
+        else:
+            self.set_yscale('log')
+        self.subplot.figure.canvas.draw_idle()    
+    
+class Plugin:
+    """
+        Plug-in class to be instantiated by the GUI manager
+    """
+    
+    def __init__(self):
+        """
+            Initialize the plug-in
+        """
+        ## Plug-in name
+        self.sub_menu = "Plotting"
+        
+        ## Reference to the parent window
+        self.parent = None
+        
+        ## List of panels for the simulation perspective (names)
+        self.perspective = []
+        
+        ## Plot panels
+        self.plot_panels = []
+        
+
+    def populate_menu(self, id, parent):
+        """
+            Create a 'Plot' menu to list the panels
+            available for displaying
+            @param id: next available unique ID for wx events
+            @param parent: parent window
+        """
+        self.menu = wx.Menu()
+        return [(id, self.menu, "Plot")]
+    
+        
+    def get_panels(self, parent):
+        """
+            Create and return a list of panel objects
+        """
+        ## Save a reference to the parent
+        self.parent = parent
+        # Connect to plotting events
+        self.parent.Bind(EVT_NEW_PLOT, self._on_plot_event)
+        
+        # We have no initial panels for this plug-in
+        return []
+    
+    def get_perspective(self):
+        """
+            Get the list of panel names for this perspective
+        """
+        return self.perspective
+    
+    def on_perspective(self, event):
+        """
+            Call back function for the perspective menu item.
+            We notify the parent window that the perspective
+            has changed.
+            @param event: menu event
+        """
+        self.parent.set_perspective(self.perspective)
+    
+    def post_init(self):
+        """
+            Post initialization call back to close the loose ends
+            [Somehow openGL needs this call]
+        """
+        pass
+    
+    def _on_show_panel(self, event):
+        print "_on_show_panel"
+    
+    def _on_plot_event(self, event):
+        """
+            A new plottable is being shipped to the plotting plug-in.
+            Check whether we have a panel to put in on, or create
+            a new one
+            @param event: EVT_NEW_PLOT event
+        """
+        # Check whether we already have a graph with the same units
+        # as the plottable we just received. 
+        is_available = False
+        for panel in self.plot_panels:
+            if event.plot._xunit == panel.graph.prop["xunit"] \
+            and event.plot._yunit == panel.graph.prop["yunit"]:
+                is_available = True
+                panel._onEVT_1DREPLOT(event)
+                self.parent.show_panel(panel.uid)
+        
+        # Create a new plot panel if none was available        
+        if not is_available:
+            new_panel = View1DPanel(self.parent, -1, style=wx.RAISED_BORDER)
+            if hasattr(event, "title"):
+                new_panel.window_caption = event.title
+                new_panel.window_name = event.title
+            
+            event_id = self.parent.popup_panel(new_panel)
+            self.menu.Append(event_id, new_panel.window_caption, 
+                             "Show %s plot panel" % new_panel.window_caption)
+            
+            # Set UID to allow us to reference the panel later
+            new_panel.uid = event_id
+            
+            # Ship the plottable to its panel
+            new_panel._onEVT_1DREPLOT(event)
+            self.plot_panels.append(new_panel)        
+            
+        return
+        
Index: guiframe/release_notes.txt
===================================================================
--- guiframe/release_notes.txt	(revision 41d466f6e37304375ace192af8fba17283851239)
+++ guiframe/release_notes.txt	(revision 41d466f6e37304375ace192af8fba17283851239)
@@ -0,0 +1,18 @@
+First cut at a GUI framework for SANS applications. 
+
+Modules should be put in the "perspectives" directory
+of their main application when using this framework.
+
+Documentation to come...
+
+Dependencies:
+	- Needs sans.guitools version 0.1.1
+	- Needs sans.guicomm
+	
+To-do list:
+	- Clean up about box
+	- Clean up the _find_plugins() function to allow user-defined directory
+	- Modify the get_context_menu() function to allow a plug-in to recognize
+		its own plots.
+	- Provide a hook for a general file loader for the various data formats
+	
Index: guiframe/setup.py
===================================================================
--- guiframe/setup.py	(revision 41d466f6e37304375ace192af8fba17283851239)
+++ guiframe/setup.py	(revision 41d466f6e37304375ace192af8fba17283851239)
@@ -0,0 +1,24 @@
+"""
+    Installs the guiframe package
+"""
+
+from distutils.core import setup, Extension
+
+setup(
+    name="guiframe",
+    version = "0.1",
+    description = "Python module for SANS gui framework",
+    author = "University of Tennessee",
+    url = "http://danse.chem.utk.edu",
+    
+    package_dir = { "sans.guiframe":".",
+                    "sans.guiframe.local_perspectives":"local_perspectives",
+                    "sans.guiframe.local_perspectives.plotting":"local_perspectives/plotting"},
+    
+    packages = ["sans.guiframe", 
+                "sans.guiframe.local_perspectives",
+                "sans.guiframe.local_perspectives.plotting"],
+    
+    package_data={"sans.guiframe": ['images/*']},
+    )
+        
