source: sasview/guiframe/gui_manager.py @ 614ce1b1

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 614ce1b1 was 32c0841, checked in by Gervaise Alina <gervyh@…>, 14 years ago

working on pylint

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