source: sasview/guiframe/gui_manager.py @ b35d3d1

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 b35d3d1 was b35d3d1, checked in by Jae Cho <jhjcho@…>, 14 years ago

save state file: working, but more work to do

  • Property mode set to 100644
File size: 36.8 KB
RevLine 
[41d466f]1
[d955bf19]2################################################################################
3#This software was developed by the University of Tennessee as part of the
4#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
5#project funded by the US National Science Foundation.
6#
7#See the license text in license.txt
8#
9#copyright 2008, University of Tennessee
10################################################################################
[7681bac]11
[d955bf19]12"""
[7681bac]13How-to build an application using guiframe:
14
[d955bf19]151- Write a main application script along the lines of dummyapp.py
162- Write a config script along the lines of config.py, and name it local_config.py
173- Write your plug-ins and place them in a directory called "perspectives".
18    - Look at local_perspectives/plotting for an example of a plug-in.
19    - A plug-in should define a class called Plugin. See abstract class below.
[7681bac]20
[41d466f]21"""
[d68c655]22#TODO: rewrite the status bar monstrosity
23
[41d466f]24import wx
25import wx.aui
26import os, sys
[b0eee0f0]27import xml
[db10f97]28
[41d466f]29try:
30    # Try to find a local config
31    import imp
32    path = os.getcwd()
[cbb2e40]33    if(os.path.isfile("%s/%s.py" % (path, 'local_config'))) or \
34      (os.path.isfile("%s/%s.pyc" % (path, 'local_config'))):
35            fObj, path, descr = imp.find_module('local_config', [path])
36            config = imp.load_module('local_config', fObj, path, descr) 
[278cc25]37    else:
[cbb2e40]38        # Try simply importing local_config
39        import local_config as config
[41d466f]40except:
41    # Didn't find local config, load the default
42    import config
[4e9583c]43   
44import warnings
45warnings.simplefilter("ignore")
[3c44c66]46
[4e9583c]47import logging
[aebc4cc]48from sans.guicomm.events import NewLoadedDataEvent
49from sans.guicomm.events import EVT_STATUS
50from sans.guicomm.events import EVT_NEW_PLOT,EVT_SLICER_PARS_UPDATE
51from sans.guicomm.events import EVT_ADD_MANY_DATA
52
53
54from data_manager import DataManager
[adfcab3]55
[ba535a6]56def quit_guiframe(parent=None):
57    """
[d955bf19]58    Pop up message to make sure the user wants to quit the application
[ba535a6]59    """
60    message = "Do you really want to quit \n"
61    message += "this application?"
62    dial = wx.MessageDialog(parent, message, 'Question',
63                       wx.YES_NO|wx.NO_DEFAULT|wx.ICON_QUESTION)
64    if dial.ShowModal() == wx.ID_YES:
65        return True
66    else:
67        return False
68   
[7681bac]69class Plugin:
70    """
[d955bf19]71    This class defines the interface for a Plugin class
72    that can be used by the gui_manager.
73   
74    Plug-ins should be placed in a sub-directory called "perspectives".
75    For example, a plug-in called Foo should be place in "perspectives/Foo".
76    That directory contains at least two files:
77        perspectives/Foo/__init.py contains two lines:
[7681bac]78       
[d955bf19]79            PLUGIN_ID = "Foo plug-in 1.0"
80            from Foo import *
[d68c655]81           
[d955bf19]82        perspectives/Foo/Foo.py contains the definition of the Plugin
83        class for the Foo plug-in. The interface of that Plugin class
84        should follow the interface of the class you are looking at.
85       
86    See dummyapp.py for a plugin example.
[7681bac]87    """
88   
[d68c655]89    def __init__(self, name="Test_plugin"):
[7681bac]90        """
91            Abstract class for gui_manager Plugins.
92        """
93        ## Plug-in name. It will appear on the application menu.
[d68c655]94        self.sub_menu = name     
[7681bac]95       
96        ## Reference to the parent window. Filled by get_panels() below.
97        self.parent = None
98       
99        ## List of panels that you would like to open in AUI windows
100        #  for your plug-in. This defines your plug-in "perspective"
101        self.perspective = []
102       
103       
104    def populate_menu(self, id, parent):
105        """
[d955bf19]106        Create and return the list of application menu
107        items for the plug-in.
108       
109        :param id: deprecated. Un-used.
110        :param parent: parent window
111       
112        :return: plug-in menu
113       
[7681bac]114        """
[d68c655]115        return []
[7681bac]116   
117    def get_panels(self, parent):
118        """
[d955bf19]119        Create and return the list of wx.Panels for your plug-in.
120        Define the plug-in perspective.
121       
122        Panels should inherit from DefaultPanel defined below,
123        or should present the same interface. They must define
124        "window_caption" and "window_name".
125       
126        :param parent: parent window
127       
128        :return: list of panels
129       
[7681bac]130        """
131        ## Save a reference to the parent
132        self.parent = parent
133       
134        # Return the list of panels
[d68c655]135        return []
136   
137    def get_tools(self):
138        """
[d955bf19]139        Returns a set of menu entries for tools
[d68c655]140        """
141        return []
142       
[7681bac]143   
144    def get_context_menu(self, graph=None):
145        """
[d955bf19]146        This method is optional.
147   
148        When the context menu of a plot is rendered, the
149        get_context_menu method will be called to give you a
150        chance to add a menu item to the context menu.
151       
152        A ref to a Graph object is passed so that you can
153        investigate the plot content and decide whether you
154        need to add items to the context menu. 
155       
156        This method returns a list of menu items.
157        Each item is itself a list defining the text to
158        appear in the menu, a tool-tip help text, and a
159        call-back method.
160       
161        :param graph: the Graph object to which we attach the context menu
162       
163        :return: a list of menu items with call-back function
[7681bac]164       
165        """
[d68c655]166        return []
[7681bac]167   
168    def get_perspective(self):
169        """
[d955bf19]170        Get the list of panel names for this perspective
[7681bac]171        """
172        return self.perspective
173   
[4e9583c]174    def on_perspective(self, event):
[7681bac]175        """
[d955bf19]176        Call back function for the perspective menu item.
177        We notify the parent window that the perspective
178        has changed.
179       
180        :param event: menu event
181       
[7681bac]182        """
183        self.parent.set_perspective(self.perspective)
184   
185    def post_init(self):
186        """
[d955bf19]187        Post initialization call back to close the loose ends
[7681bac]188        """
189        pass
[b28278e]190   
191    def set_default_perspective(self):
192        """
[d955bf19]193       Call back method that True to notify the parent that the current plug-in
194       can be set as default  perspective.
195       when returning False, the plug-in is not candidate for an automatic
196       default perspective setting
[b28278e]197        """
198        return False
[7681bac]199
[41d466f]200class ViewerFrame(wx.Frame):
201    """
[d955bf19]202    Main application frame
[41d466f]203    """
[c9454bb]204    def __init__(self, parent, id, title, window_height=300, window_width=300):
[41d466f]205        """
[d955bf19]206        Initialize the Frame object
[41d466f]207        """
208        from local_perspectives.plotting import plotting
[c9454bb]209        wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, size=(window_width, window_height))
[d0802c3]210        # Preferred window size
211        self._window_height = window_height
212        self._window_width  = window_width
[41d466f]213       
[fc2b91a]214        # Logging info
215        logging.basicConfig(level=logging.DEBUG,
216                    format='%(asctime)s %(levelname)s %(message)s',
217                    filename='sans_app.log',
218                    filemode='w')       
[c44e7cc]219        path = os.path.dirname(__file__)
220        temp_path= os.path.join(path,'images')
221        ico_file = os.path.join(temp_path,'ball.ico')
[278cc25]222        if os.path.isfile(ico_file):
223            self.SetIcon(wx.Icon(ico_file, wx.BITMAP_TYPE_ICO))
[cbb2e40]224        else:
[c44e7cc]225            temp_path= os.path.join(os.getcwd(),'images')
226            ico_file = os.path.join(temp_path,'ball.ico')
[cbb2e40]227            if os.path.isfile(ico_file):
228                self.SetIcon(wx.Icon(ico_file, wx.BITMAP_TYPE_ICO))
[41d466f]229       
230        ## Application manager
231        self.app_manager = None
[4e9583c]232       
[41d466f]233        ## Find plug-ins
234        # Modify this so that we can specify the directory to look into
[a88ac04]235        self.plugins =[]
[41d466f]236        self.plugins.append(plotting.Plugin())
[a88ac04]237        self.plugins += self._find_plugins()
238     
[41d466f]239        ## List of panels
240        self.panels = {}
241
242        ## Next available ID for wx gui events
[2310d69]243        #TODO:  No longer used - remove all calls to this
[41d466f]244        self.next_id = 20000
245
[2310d69]246        # Default locations
247        self._default_save_location = os.getcwd()       
248
[f9e803e]249        # Welcome panel
250        self.defaultPanel = None
[b91c736]251        #panel on focus
252        self.panel_on_focus = None
[a0d56d5]253        # Check for update
[af20f6b]254        #self._check_update(None)
[b0eee0f0]255        ## maximum number of opened files' paths to store
256        self.n_maxfileopen =  2
257        ## number of file open
258        self.n_fileOpen=0
259        ## list of path of open files
260        self.filePathList=[]
261        ## list of open file with name form menu
262        #self._saveOpenData()
[25ccf33]263        ## Dictionary of open file where keys are filename  and values are number of copy of data plotted
264        ## using the same loaded file
265        self.indice_load_data={}
[278cc25]266        # Register the close event so it calls our own method
267        wx.EVT_CLOSE(self, self._onClose)
268        # Register to status events
269        self.Bind(EVT_STATUS, self._on_status_event)
[b91c736]270        #Register add extra data on the same panel event on load
271        self.Bind(EVT_ADD_MANY_DATA, self.set_panel_on_focus)
272       
273    def set_panel_on_focus(self, event):
274        """
[d955bf19]275        Store reference to the last panel on focus
[b91c736]276        """
277        self.panel_on_focus = event.panel
[0bd2cd8]278       
[278cc25]279    def build_gui(self):
[d955bf19]280        """
281        """
[41d466f]282        # Set up the layout
283        self._setup_layout()
284       
285        # Set up the menu
286        self._setup_menus()
[c9454bb]287        #self.Fit()
[af20f6b]288        #self._check_update(None)
[41d466f]289             
290    def _setup_layout(self):
291        """
[d955bf19]292        Set up the layout
[41d466f]293        """
294        # Status bar
[db10f97]295        from statusbar import StatusBar
296        self.sb = StatusBar(self, wx.ID_ANY)
[dd66fbd]297        self.SetStatusBar(self.sb)
[41d466f]298        # Add panel
299        self._mgr = wx.aui.AuiManager(self)
[c9454bb]300        self._mgr.SetDockSizeConstraint(0.5, 0.5) 
[41d466f]301       
302        # Load panels
303        self._load_panels()
304       
305        self._mgr.Update()
[db10f97]306       
307    def SetStatusText(self, *args, **kwds):
[d955bf19]308        """
309        """
[db10f97]310        number = self.sb.get_msg_position()
311        wx.Frame.SetStatusText(number=number, *args, **kwds)
312       
313    def PopStatusText(self, *args, **kwds):
[d955bf19]314        """
315        """
[db10f97]316        field = self.sb.get_msg_position()
317        wx.Frame.PopStatusText(field=field)
318       
319    def PushStatusText(self, *args, **kwds):
[d955bf19]320        """
321        """
[db10f97]322        field = self.sb.get_msg_position()
323        wx.Frame.PushStatusText(self, field=field,string=string)
[278cc25]324
325    def add_perspective(self, plugin):
326        """
[d955bf19]327        Add a perspective if it doesn't already
328        exist.
[278cc25]329        """
330        is_loaded = False
331        for item in self.plugins:
[fd68aa9]332             if plugin.__class__==item.__class__:
[278cc25]333                 print "Plugin %s already loaded" % plugin.__class__.__name__
334                 is_loaded = True
335                 
336        if not is_loaded:
337            self.plugins.append(plugin)
[41d466f]338     
339    def _find_plugins(self, dir="perspectives"):
340        """
[d955bf19]341        Find available perspective plug-ins
342       
343        :param dir: directory in which to look for plug-ins
344       
345        :return: list of plug-ins
346       
[41d466f]347        """
348        import imp
[278cc25]349       
[41d466f]350        plugins = []
351        # Go through files in panels directory
352        try:
353            list = os.listdir(dir)
[a88ac04]354            ## the default panel is the panel is the last plugin added
355            for item in list:
[41d466f]356                toks = os.path.splitext(os.path.basename(item))
357                name = None
358                if not toks[0] == '__init__':
359                   
360                    if toks[1]=='.py' or toks[1]=='':
361                        name = toks[0]
362               
363                    path = [os.path.abspath(dir)]
364                    file = None
365                    try:
366                        if toks[1]=='':
367                            mod_path = '.'.join([dir, name])
368                            module = __import__(mod_path, globals(), locals(), [name])
369                        else:
370                            (file, path, info) = imp.find_module(name, path)
371                            module = imp.load_module( name, file, item, info )
372                        if hasattr(module, "PLUGIN_ID"):
373                            try:
374                                plugins.append(module.Plugin())
[d68c655]375                                logging.info("Found plug-in: %s" % module.PLUGIN_ID)
[41d466f]376                            except:
377                                config.printEVT("Error accessing PluginPanel in %s\n  %s" % (name, sys.exc_value))
378                       
379                    except:
380                        print sys.exc_value
[d68c655]381                        logging.error("ViewerFrame._find_plugins: %s" % sys.exc_value)
[41d466f]382                    finally:
383                        if not file==None:
384                            file.close()
385        except:
386            # Should raise and catch at a higher level and display error on status bar
387            pass   
388        return plugins
389   
[f9e803e]390    def set_welcome_panel(self, panel_class):
391        """
[d955bf19]392        Sets the default panel as the given welcome panel
393       
394        :param panel_class: class of the welcome panel to be instantiated
395       
[f9e803e]396        """
[d955bf19]397        self.defaultPanel = panel_class(self, -1, style=wx.RAISED_BORDER)
[b28278e]398       
[41d466f]399    def _load_panels(self):
400        """
[d955bf19]401        Load all panels in the panels directory
[41d466f]402        """
403       
404        # Look for plug-in panels
[c1469ebe]405        panels = []   
[41d466f]406        for item in self.plugins:
407            if hasattr(item, "get_panels"):
408                ps = item.get_panels(self)
409                panels.extend(ps)
410
411        # Show a default panel with some help information
412        # It also sets the size of the application windows
[c9454bb]413        #TODO: Use this for slpash screen
[f9e803e]414        if self.defaultPanel is None:
415            self.defaultPanel    = DefaultPanel(self, -1, style=wx.RAISED_BORDER)
416           
[41d466f]417        self.panels["default"] = self.defaultPanel
[ca88b2e]418       
[41d466f]419        self._mgr.AddPane(self.defaultPanel, wx.aui.AuiPaneInfo().
420                              Name("default").
421                              CenterPane().
[d0802c3]422                              # This is where we set the size of the application window
423                              BestSize(wx.Size(self._window_width, self._window_height)).
[c9454bb]424                              #MinSize(wx.Size(self._window_width, self._window_height)).
[41d466f]425                              Show())
[6d920cd]426     
[41d466f]427
428        # Add the panels to the AUI manager
429        for panel_class in panels:
430            p = panel_class
431            id = wx.NewId()
432           
433            # Check whether we need to put this panel
434            # in the center pane
[6f59a98]435            if hasattr(p, "CENTER_PANE") and p.CENTER_PANE:
[41d466f]436                if p.CENTER_PANE:
437                    self.panels[str(id)] = p
438                    self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
439                                          Name(p.window_name).Caption(p.window_caption).
440                                          CenterPane().
[c9454bb]441                                          #BestSize(wx.Size(550,600)).
442                                          #MinSize(wx.Size(500,500)).
[41d466f]443                                          Hide())
444            else:
445                self.panels[str(id)] = p
446                self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
447                                  Name(p.window_name).Caption(p.window_caption).
448                                  Right().
449                                  Dock().
450                                  TopDockable().
451                                  BottomDockable().
452                                  LeftDockable().
453                                  RightDockable().
454                                  MinimizeButton().
[c9454bb]455                                  Hide())
456                                  #BestSize(wx.Size(550,600)))
457                                  #MinSize(wx.Size(500,500)))                 
[b28278e]458     
[2310d69]459    def get_context_menu(self, graph=None):
[41d466f]460        """
[d955bf19]461        Get the context menu items made available
462        by the different plug-ins.
463        This function is used by the plotting module
[41d466f]464        """
465        menu_list = []
466        for item in self.plugins:
467            if hasattr(item, "get_context_menu"):
[2310d69]468                menu_list.extend(item.get_context_menu(graph))
[41d466f]469           
470        return menu_list
471       
472    def popup_panel(self, p):
473        """
[d955bf19]474        Add a panel object to the AUI manager
475       
476        :param p: panel object to add to the AUI manager
[41d466f]477       
[d955bf19]478        :return: ID of the event associated with the new panel [int]
479       
480        """
[41d466f]481        ID = wx.NewId()
482        self.panels[str(ID)] = p
483       
484        count = 0
485        for item in self.panels:
[383189f9]486            if self.panels[item].window_name.startswith(p.window_name): 
[41d466f]487                count += 1
[16bf519]488       
[41d466f]489        windowname = p.window_name
490        caption = p.window_caption
[16bf519]491       
[41d466f]492        if count>0:
493            windowname += str(count+1)
494            caption += (' '+str(count))
[16bf519]495         
[41d466f]496        p.window_name = windowname
497        p.window_caption = caption
498           
499        self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
500                          Name(windowname).Caption(caption).
501                          Floatable().
502                          #Float().
503                          Right().
504                          Dock().
505                          TopDockable().
506                          BottomDockable().
507                          LeftDockable().
508                          RightDockable().
509                          MinimizeButton().
510                          #Hide().
511                          #Show().
[c9454bb]512                          Resizable(True).
513                          # Use a large best size to make sure the AUI manager
514                          # takes all the available space
[6ab0ad1]515                          BestSize(wx.Size(400,400)))
[d0802c3]516        pane = self._mgr.GetPane(windowname)
517        self._mgr.MaximizePane(pane)
518        self._mgr.RestoreMaximizedPane()
519       
[4e9583c]520       
[41d466f]521        # Register for showing/hiding the panel
[4e9583c]522       
[41d466f]523        wx.EVT_MENU(self, ID, self._on_view)
524       
525        self._mgr.Update()
526        return ID
527       
528    def _setup_menus(self):
529        """
[d955bf19]530        Set up the application menus
[41d466f]531        """
532        # Menu
533        menubar = wx.MenuBar()
534       
535        # File menu
[b0eee0f0]536        self.filemenu = wx.Menu()
[fc2b91a]537       
538        id = wx.NewId()
[b35d3d1]539        self.filemenu.Append(id, '&Open', 'Load data file into the application')
[4e9583c]540        wx.EVT_MENU(self, id, self._on_open)
541        #self.filemenu.AppendSeparator()
[b0eee0f0]542       
[ca88b2e]543        id = wx.NewId()
[b35d3d1]544        self.filemenu.Append(id, '&Save', 'Save project as a SanaView (svs) file')
545        wx.EVT_MENU(self, id, self._on_save)
546        #self.filemenu.AppendSeparator()
547       
548        id = wx.NewId()
[b0eee0f0]549        self.filemenu.Append(id,'&Quit', 'Exit') 
[fc2b91a]550        wx.EVT_MENU(self, id, self.Close)
[41d466f]551       
[adfcab3]552        # Add sub menus
[b0eee0f0]553        menubar.Append(self.filemenu,  '&File')
[adfcab3]554       
[6f59a98]555        # Window menu
[41d466f]556        # Attach a menu item for each panel in our
557        # panel list that also appears in a plug-in.
558       
[6f59a98]559        # Only add the panel menu if there is only one perspective and
560        # it has more than two panels.
561        # Note: the first plug-in is always the plotting plug-in. The first application
562        # plug-in is always the second one in the list.
563        if len(self.plugins)==2:
564            plug = self.plugins[1]
[41d466f]565            pers = plug.get_perspective()
[0d9dae8]566       
[6f59a98]567            if len(pers)>1:
568                viewmenu = wx.Menu()
569                for item in self.panels:
570                    if item == 'default':
571                        continue
572                    panel = self.panels[item]
573                    if panel.window_name in pers:
574                        viewmenu.Append(int(item), panel.window_caption, "Show %s window" % panel.window_caption)
575                        wx.EVT_MENU(self, int(item), self._on_view)
576                menubar.Append(viewmenu, '&Window')
[adfcab3]577
[41d466f]578        # Perspective
579        # Attach a menu item for each defined perspective.
[d68c655]580        # Only add the perspective menu if there are more than one perspectives
[adfcab3]581        n_perspectives = 0
[41d466f]582        for plug in self.plugins:
583            if len(plug.get_perspective()) > 0:
[adfcab3]584                n_perspectives += 1
585       
586        if n_perspectives>1:
587            p_menu = wx.Menu()
588            for plug in self.plugins:
589                if len(plug.get_perspective()) > 0:
590                    id = wx.NewId()
591                    p_menu.Append(id, plug.sub_menu, "Switch to %s perspective" % plug.sub_menu)
592                    wx.EVT_MENU(self, id, plug.on_perspective)
593            menubar.Append(p_menu,   '&Perspective')
[4e9583c]594 
[d68c655]595        # Tools menu
596        # Go through plug-ins and find tools to populate the tools menu
597        toolsmenu = None
598        for item in self.plugins:
599            if hasattr(item, "get_tools"):
600                for tool in item.get_tools():
601                    # Only create a menu if we have at least one tool
602                    if toolsmenu is None:
603                        toolsmenu = wx.Menu()
604                    id = wx.NewId()
[c1469ebe]605                   
[d68c655]606                    toolsmenu.Append(id, tool[0], tool[1])
607                    wx.EVT_MENU(self, id, tool[2])
608        if toolsmenu is not None:
609            menubar.Append(toolsmenu, '&Tools')
610 
[41d466f]611        # Help menu
612        helpmenu = wx.Menu()
[c1469ebe]613        # add the welcome panel menu item
[629e8b9]614        if self.defaultPanel is not None:
615            id = wx.NewId()
616            helpmenu.Append(id,'&Welcome', '')
617            helpmenu.AppendSeparator()
618            wx.EVT_MENU(self, id, self.show_welcome_panel)
[c1469ebe]619       
[fa452e4]620        # Look for help item in plug-ins
621        for item in self.plugins:
622            if hasattr(item, "help"):
623                id = wx.NewId()
624                helpmenu.Append(id,'&%s help' % item.sub_menu, '')
625                wx.EVT_MENU(self, id, item.help)
626       
[41d466f]627        if config._do_aboutbox:
628            id = wx.NewId()
629            helpmenu.Append(id,'&About', 'Software information')
630            wx.EVT_MENU(self, id, self._onAbout)
[af20f6b]631           
632        # Checking for updates needs major refactoring to work with py2exe
633        # We need to make sure it doesn't hang the application if the server
634        # is not up. We also need to make sure there's a proper executable to
635        # run if we spawn a new background process.
636        #id = wx.NewId()
637        #helpmenu.Append(id,'&Check for update', 'Check for the latest version of %s' % config.__appname__)
638        #wx.EVT_MENU(self, id, self._check_update)
[41d466f]639       
640        # Look for plug-in menus
641        # Add available plug-in sub-menus.
642        for item in self.plugins:
643            if hasattr(item, "populate_menu"):
644                for (self.next_id, menu, name) in item.populate_menu(self.next_id, self):
645                    menubar.Append(menu, name)
[0d9dae8]646                   
[41d466f]647        menubar.Append(helpmenu, '&Help')
648        self.SetMenuBar(menubar)
[d955bf19]649   
[41d466f]650    def _on_status_event(self, evt):
651        """
[d955bf19]652        Display status message
[41d466f]653        """
[db10f97]654        self.sb.set_status(event=evt)
[dd66fbd]655       
[41d466f]656    def _on_view(self, evt):
657        """
[d955bf19]658        A panel was selected to be shown. If it's not already
659        shown, display it.
660       
661        :param evt: menu event
662       
[41d466f]663        """
664        self.show_panel(evt.GetId())
[c1469ebe]665       
[b28278e]666    def on_close_welcome_panel(self):
[c1469ebe]667        """
[d955bf19]668        Close the welcome panel
[c1469ebe]669        """
[629e8b9]670        if self.defaultPanel is None:
671            return 
[c1469ebe]672        self._mgr.GetPane(self.panels["default"].window_name).Hide()
673        self._mgr.Update()
[b28278e]674        # set a default perspective
675        self.set_default_perspective()
[c1469ebe]676       
677    def show_welcome_panel(self, event):
678        """   
[d955bf19]679        Display the welcome panel
[c1469ebe]680        """
[629e8b9]681        if self.defaultPanel is None:
682            return 
[c1469ebe]683        for id in self.panels.keys():
684            if self._mgr.GetPane(self.panels[id].window_name).IsShown():
685                self._mgr.GetPane(self.panels[id].window_name).Hide()
686        # Show default panel
687        if not self._mgr.GetPane(self.panels["default"].window_name).IsShown():
688            self._mgr.GetPane(self.panels["default"].window_name).Show()
689       
690        self._mgr.Update()
691       
[41d466f]692    def show_panel(self, uid):
693        """
[d955bf19]694        Shows the panel with the given id
695       
696        :param uid: unique ID number of the panel to show
697       
[41d466f]698        """
699        ID = str(uid)
700        config.printEVT("show_panel: %s" % ID)
701        if ID in self.panels.keys():
702            if not self._mgr.GetPane(self.panels[ID].window_name).IsShown():
703                self._mgr.GetPane(self.panels[ID].window_name).Show()
[383189f9]704                # Hide default panel
705                self._mgr.GetPane(self.panels["default"].window_name).Hide()
[d955bf19]706       
[41d466f]707            self._mgr.Update()
[4e9583c]708   
709    def _on_open(self, event):
[b3644f3]710        """
711        """
[fc2b91a]712        path = self.choose_file()
[4e9583c]713        if path is None:
[700f9b4]714            return
[b3644f3]715       
[4e9583c]716        from data_loader import plot_data
717        if path and os.path.isfile(path):
[b35d3d1]718             basename  = os.path.basename(path)
719             if  basename.endswith('.svs'):
720                 plot_data(self, path,'.inv')
721                 plot_data(self, path,'.prv')
722                 plot_data(self, path,'.fitv')
723             else: plot_data(self,path)
[aebc4cc]724        if self.defaultPanel is not None and \
725            self._mgr.GetPane(self.panels["default"].window_name).IsShown():
726            self.on_close_welcome_panel()
[b35d3d1]727           
728    def _on_save(self, event):
729        """
730        Save state into a file
731        """
732        # Ask the user the location of the file to write to.
733       
734        ## Default file location for save
735        self._default_save_location = os.getcwd()
736        path = None
737        dlg = wx.FileDialog(self, "Choose a file", self._default_save_location, "", "*.svs", wx.SAVE)
738        if dlg.ShowModal() == wx.ID_OK:
739            path = dlg.GetPath()
740            self._default_save_location = os.path.dirname(path)
741        else:
742            return None
743       
744        dlg.Destroy()
745        if path is None:
746            return
747        # default cansas xml doc
748        doc = None
749        for item in self.panels:
750            try:
751                if self.panels[item].window_name == 'Invariant' or self.panels[item].window_name == 'pr_control' or self.panels[item].window_name == 'Fit panel':
752                    if self.panels[item].window_name == 'Invariant':
753                        data = self.panels[item]._data
754                        state = self.panels[item].state
755                        new_doc =self.panels[item]._manager.state_reader.write_toXML(data,state)
756                        if hasattr(doc, "firstChild"):
757                            doc.firstChild.appendChild (new_doc.firstChild.firstChild) 
758                        else:
759                            doc = new_doc
760                    elif self.panels[item].window_name == 'pr_control':
761                        data = self.panels[item].manager.current_plottable
762                        state = self.panels[item].get_state()
763                        new_doc =self.panels[item].manager.state_reader.write_toXML(data,state)
764                        if hasattr(doc, "firstChild"):
765                            doc.firstChild.appendChild (new_doc.firstChild.firstChild) 
766                        else:
767                            doc = new_doc
768                    elif self.panels[item].window_name == 'Fit panel':
769                        for index in range(self.panels[item].GetPageCount()):
770                            selected_page = self.panels[item].GetPage(index) 
771                            if hasattr(selected_page,"get_data"):
772                                data = selected_page.get_data()
773                                state = selected_page.state
774                                new_doc =selected_page.manager.state_reader.write_toXML(data,state)
775                                if doc != None and hasattr(doc, "firstChild"):
776                                    doc.firstChild.appendChild (new_doc.firstChild.firstChild)
777                                else:
778                                    doc = new_doc
779
780            except: 
781                pass
782
783        # Write the XML document
784        if doc != None:
785            fd = open(path, 'w')
786            fd.write(doc.toprettyxml())
787            fd.close()
788        else:
789            raise RuntimeError, "%s is not a SansView (.svs) file..." % path
790
[af47c8d]791       
[41d466f]792    def _onClose(self, event):
[b0eee0f0]793        """
[d955bf19]794        Store info to retrieve in xml before closing the application
[b0eee0f0]795        """
796        try:
797            doc = xml.dom.minidom.Document()
798            main_node = doc.createElement("file Path")
799           
800            doc.appendChild(main_node)
801       
802            for item in self.filePathList:
803                id, menuitem_name , path, title = item
804                pt1 = doc.createElement("File")
805                pt1.setAttribute("name", menuitem_name)
806                pt2 = doc.createElement("path")
807                pt2.appendChild(doc.createTextNode(str(path)))
808                pt1.appendChild(pt2)
809                pt3 = doc.createElement("title")
810                pt3.appendChild(doc.createTextNode(str(title)))
811                pt1.appendChild(pt3)
812               
813                main_node.appendChild(pt1)
[d955bf19]814           
[b0eee0f0]815            fd = open("fileOpened.xml",'w')
816            fd.write(doc.toprettyxml())
817            fd.close()
818        except:
819            pass
[4e9583c]820       
[41d466f]821        import sys
822        wx.Exit()
823        sys.exit()
[d955bf19]824                     
[41d466f]825    def Close(self, event=None):
826        """
[d955bf19]827        Quit the application
[41d466f]828        """
[ba535a6]829        flag = quit_guiframe(parent=self)
830        if flag:
831            import sys
832            wx.Frame.Close(self)
833            wx.Exit()
834            sys.exit()
[41d466f]835
836    def _check_update(self, event=None): 
837        """
[d955bf19]838        Check with the deployment server whether a new version
839        of the application is available.
840        A thread is started for the connecting with the server. The thread calls
841        a call-back method when the current version number has been obtained.
[52070a1]842        """
[d68c655]843        if hasattr(config, "__update_URL__"):
844            import version
845            checker = version.VersionThread(config.__update_URL__, self._process_version, baggage=event==None)
846            checker.start() 
[52070a1]847   
848    def _process_version(self, version, standalone=True):
849        """
[d955bf19]850        Call-back method for the process of checking for updates.
851        This methods is called by a VersionThread object once the current
852        version number has been obtained. If the check is being done in the
853        background, the user will not be notified unless there's an update.
854       
855        :param version: version string
856        :param standalone: True of the update is being checked in
857           the background, False otherwise.
858           
[52070a1]859        """
860        try:
861            if cmp(version, config.__version__)>0:
862                self.SetStatusText("Version %s is available! See the Help menu to download it." % version)
863                if not standalone:
864                    import webbrowser
865                    webbrowser.open(config.__download_page__)
866            else:
867                if not standalone:
868                    self.SetStatusText("You have the latest version of %s" % config.__appname__)
[41d466f]869        except:
[f089cbd]870            logging.error("guiframe: could not get latest application version number\n  %s" % sys.exc_value)
[52070a1]871            if not standalone:
872                self.SetStatusText("Could not connect to the application server. Please try again later.")
873                   
874       
[41d466f]875    def _onAbout(self, evt):
876        """
[d955bf19]877        Pop up the about dialog
878       
879        :param evt: menu event
880       
[41d466f]881        """
882        if config._do_aboutbox:
883            import aboutbox 
884            dialog = aboutbox.DialogAbout(None, -1, "")
[6ab0ad1]885            dialog.ShowModal()           
[4e9583c]886           
887    def _onreloaFile(self, event): 
888        """
889        load a data previously opened
890        """
891        from data_loader import plot_data
892        for item in self.filePathList:
893            id, menuitem_name , path, title = item
894            if id == event.GetId():
895                if path and os.path.isfile(path):
896                    plot_data(self, path)
897                    break
898           
[41d466f]899    def set_manager(self, manager):
900        """
[d955bf19]901        Sets the application manager for this frame
902       
903        :param manager: frame manager
[41d466f]904        """
905        self.app_manager = manager
906       
907    def post_init(self):
908        """
[d955bf19]909        This initialization method is called after the GUI
910        has been created and all plug-ins loaded. It calls
911        the post_init() method of each plug-in (if it exists)
912        so that final initialization can be done.
[41d466f]913        """
914        for item in self.plugins:
915            if hasattr(item, "post_init"):
916                item.post_init()
917       
[b28278e]918    def set_default_perspective(self):
919        """
[d955bf19]920        Choose among the plugin the first plug-in that has
921        "set_default_perspective" method and its return value is True will be
922        as a default perspective when the welcome page is closed
[b28278e]923        """
924        for item in self.plugins:
925            if hasattr(item, "set_default_perspective"):
926                if item.set_default_perspective():
927                    item.on_perspective(event=None)
928                    return 
929           
[41d466f]930    def set_perspective(self, panels):
931        """
[d955bf19]932        Sets the perspective of the GUI.
933        Opens all the panels in the list, and closes
934        all the others.
935       
936        :param panels: list of panels
[41d466f]937        """
938        for item in self.panels:
939            # Check whether this is a sticky panel
940            if hasattr(self.panels[item], "ALWAYS_ON"):
941                if self.panels[item].ALWAYS_ON:
942                    continue 
943           
944            if self.panels[item].window_name in panels:
945                if not self._mgr.GetPane(self.panels[item].window_name).IsShown():
946                    self._mgr.GetPane(self.panels[item].window_name).Show()
947            else:
948                if self._mgr.GetPane(self.panels[item].window_name).IsShown():
949                    self._mgr.GetPane(self.panels[item].window_name).Hide()
[af47c8d]950   
[41d466f]951        self._mgr.Update()
[4e9583c]952       
953    def choose_file(self, path=None):
[41d466f]954        """
[d955bf19]955        Functionality that belongs elsewhere
956        Should add a hook to specify the preferred file type/extension.
[41d466f]957        """
[4e9583c]958        #TODO: clean this up
959        from data_loader import choose_data_file
960       
[8068b52]961        # Choose a file path
[4e9583c]962        if path==None:
963            path = choose_data_file(self, self._default_save_location)
[8068b52]964           
[4e9583c]965        if not path==None:
[2310d69]966            try:
[4e9583c]967                self._default_save_location = os.path.dirname(path)
[2310d69]968            except:
[83ee3851]969                pass
[2310d69]970        return path
[4e9583c]971   
972    def load_ascii_1D(self, path):
973        """
974        """
975        from data_loader import load_ascii_1D
976        return load_ascii_1D(path)
977                 
[41d466f]978class DefaultPanel(wx.Panel):
979    """
[d955bf19]980    Defines the API for a panels to work with
981    the GUI manager
[41d466f]982    """
983    ## Internal nickname for the window, used by the AUI manager
984    window_name = "default"
985    ## Name to appear on the window title bar
986    window_caption = "Welcome panel"
987    ## Flag to tell the AUI manager to put this panel in the center pane
988    CENTER_PANE = True
989
990 
991# Toy application to test this Frame
992class ViewApp(wx.App):
[d955bf19]993    """
994    """
[41d466f]995    def OnInit(self):
[d955bf19]996        """
997        """
[41d466f]998        self.frame = ViewerFrame(None, -1, config.__appname__)   
999        self.frame.Show(True)
1000
1001        if hasattr(self.frame, 'special'):
1002            self.frame.special.SetCurrent()
1003        self.SetTopWindow(self.frame)
1004        return True
1005   
1006    def set_manager(self, manager):
1007        """
[d955bf19]1008        Sets a reference to the application manager
1009        of the GUI manager (Frame)
[41d466f]1010        """
1011        self.frame.set_manager(manager)
1012       
[278cc25]1013    def build_gui(self):
1014        """
[d955bf19]1015        Build the GUI
[278cc25]1016        """
1017        self.frame.build_gui()
1018        self.frame.post_init()
1019       
[f9e803e]1020    def set_welcome_panel(self, panel_class):
1021        """
[d955bf19]1022        Set the welcome panel
1023       
1024        :param panel_class: class of the welcome panel to be instantiated
1025       
[f9e803e]1026        """
1027        self.frame.set_welcome_panel(panel_class)
1028       
[278cc25]1029    def add_perspective(self, perspective):
1030        """
[d955bf19]1031        Manually add a perspective to the application GUI
[278cc25]1032        """
1033        self.frame.add_perspective(perspective)
1034       
[41d466f]1035
1036if __name__ == "__main__": 
1037    app = ViewApp(0)
1038    app.MainLoop()             
Note: See TracBrowser for help on using the repository browser.