source: sasview/guiframe/gui_manager.py @ 6d48919

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

a bit generalized the plotdata

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