source: sasview/guiframe/gui_manager.py @ 4986ce7

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 4986ce7 was cbf22e5, checked in by Gervaise Alina <gervyh@…>, 14 years ago

working on guiframe menu

  • Property mode set to 100644
File size: 49.6 KB
Line 
1
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################################################################################
11
12
13import wx
14import wx.aui
15import os
16import sys
17import xml
18
19try:
20    # Try to find a local config
21    import imp
22    path = os.getcwd()
23    if(os.path.isfile("%s/%s.py" % (path, 'local_config'))) or \
24        (os.path.isfile("%s/%s.pyc" % (path, 'local_config'))):
25        fObj, path, descr = imp.find_module('local_config', [path])
26        config = imp.load_module('local_config', fObj, path, descr) 
27    else:
28        # Try simply importing local_config
29        import local_config as config
30except:
31    # Didn't find local config, load the default
32    import config
33   
34import warnings
35warnings.simplefilter("ignore")
36
37import logging
38
39from sans.guiframe.events import EVT_STATUS
40from sans.guiframe.events import EVT_ADD_MANY_DATA
41from sans.guiframe.events import StatusEvent
42from sans.guiframe.events import NewPlotEvent
43from sans.guiframe.gui_style import *
44from sans.guiframe.events import NewLoadedDataEvent
45from sans.guiframe.data_panel import DataPanel
46
47STATE_FILE_EXT = ['.inv', '.fitv', '.prv']
48DATA_MANAGER = False
49AUTO_PLOT = False
50AUTO_SET_DATA = True
51PLOPANEL_WIDTH = 400
52PLOPANEL_HEIGTH = 400
53
54
55class ViewerFrame(wx.Frame):
56    """
57    Main application frame
58    """
59   
60    def __init__(self, parent, id, title, 
61                 window_height=300, window_width=300,
62                 gui_style=GUIFRAME.DEFAULT_STYLE):
63        """
64        Initialize the Frame object
65        """
66       
67        wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition,
68                          size=(window_width, window_height))
69        # Preferred window size
70        self._window_height = window_height
71        self._window_width  = window_width
72        self.__gui_style = gui_style
73       
74        # Logging info
75        logging.basicConfig(level=logging.DEBUG,
76                    format='%(asctime)s %(levelname)s %(message)s',
77                    filename='sans_app.log',
78                    filemode='w')       
79        path = os.path.dirname(__file__)
80        temp_path = os.path.join(path,'images')
81        ico_file = os.path.join(temp_path,'ball.ico')
82        if os.path.isfile(ico_file):
83            self.SetIcon(wx.Icon(ico_file, wx.BITMAP_TYPE_ICO))
84        else:
85            temp_path = os.path.join(os.getcwd(),'images')
86            ico_file = os.path.join(temp_path,'ball.ico')
87            if os.path.isfile(ico_file):
88                self.SetIcon(wx.Icon(ico_file, wx.BITMAP_TYPE_ICO))
89       
90        ## Application manager
91        self.app_manager = None
92        self._mgr = None
93        #add current perpsective
94        self._current_perspective = None
95        self._plotting_plugin = None
96        self._data_plugin = None
97        #Menu bar and item
98        self._menubar = None
99        self._file_menu = None
100        self._data_menu = None
101        self._window_menu = None
102        self._window_menu = None
103        self._help_menu = None
104        self._tool_menu = None
105        self._plugin_menu = None
106        ## Find plug-ins
107        # Modify this so that we can specify the directory to look into
108        self.plugins = []
109        #add local plugin
110        self.plugins += self._get_local_plugins()
111        self.plugins += self._find_plugins()
112        ## List of panels
113        self.panels = {}
114
115        # Default locations
116        self._default_save_location = os.getcwd()       
117       
118        # Welcome panel
119        self.defaultPanel = None
120        #panel on focus
121        self.panel_on_focus = None
122         #data manager
123        from data_manager import DataManager
124        self._data_manager = DataManager()
125        self._data_panel = DataPanel(parent=self)
126        if self.panel_on_focus is not None:
127            self._data_panel.set_panel_on_focus(self.panel_on_focus.window_caption)
128        # Check for update
129        #self._check_update(None)
130        # Register the close event so it calls our own method
131        wx.EVT_CLOSE(self, self._onClose)
132        # Register to status events
133        self.Bind(EVT_STATUS, self._on_status_event)
134        #Register add extra data on the same panel event on load
135        self.Bind(EVT_ADD_MANY_DATA, self.set_panel_on_focus)
136       
137    def set_panel_on_focus(self, event):
138        """
139        Store reference to the last panel on focus
140        """
141        self.panel_on_focus = event.panel
142        if self.panel_on_focus is not None and self._data_panel is not None:
143            self._data_panel.set_panel_on_focus(self.panel_on_focus.window_name)
144           
145    def build_gui(self):
146        """
147        """
148        # Set up the layout
149        self._setup_layout()
150       
151        # Set up the menu
152        self._setup_menus()
153        #self.Fit()
154        #self._check_update(None)
155             
156    def _setup_layout(self):
157        """
158        Set up the layout
159        """
160        # Status bar
161        from statusbar import StatusBar
162        self.sb = StatusBar(self, wx.ID_ANY)
163        self.SetStatusBar(self.sb)
164        # Add panel
165        default_flag = wx.aui.AUI_MGR_DEFAULT| wx.aui.AUI_MGR_ALLOW_ACTIVE_PANE
166        self._mgr = wx.aui.AuiManager(self, flags=default_flag)
167   
168        # Load panels
169        self._load_panels()
170        self.set_default_perspective()
171        self._mgr.Update()
172       
173    def SetStatusText(self, *args, **kwds):
174        """
175        """
176        number = self.sb.get_msg_position()
177        wx.Frame.SetStatusText(number=number, *args, **kwds)
178       
179    def PopStatusText(self, *args, **kwds):
180        """
181        """
182        field = self.sb.get_msg_position()
183        wx.Frame.PopStatusText(field=field)
184       
185    def PushStatusText(self, *args, **kwds):
186        """
187        """
188        field = self.sb.get_msg_position()
189        wx.Frame.PushStatusText(self, field=field, string=string)
190
191    def add_perspective(self, plugin):
192        """
193        Add a perspective if it doesn't already
194        exist.
195        """
196        is_loaded = False
197        for item in self.plugins:
198            if plugin.__class__ == item.__class__:
199                msg = "Plugin %s already loaded" % plugin.__class__.__name__
200                logging.info(msg)
201                is_loaded = True   
202        if not is_loaded:
203            self.plugins.append(plugin)
204     
205    def _get_local_plugins(self):
206        """
207        get plugins local to guiframe and others
208        """
209        plugins = []
210        #import guiframe local plugins
211        #check if the style contain guiframe.dataloader
212        style1 = self.__gui_style & GUIFRAME.DATALOADER_ON
213        style2 = self.__gui_style & GUIFRAME.PLOTTING_ON
214        if style1 == GUIFRAME.DATALOADER_ON:
215            try:
216                from sans.guiframe.local_perspectives.data_loader import data_loader
217                self._data_plugin = data_loader.Plugin()
218                plugins.append(self._data_plugin)
219            except:
220                msg = "ViewerFrame._get_local_plugins:"
221                msg += "cannot import dataloader plugin.\n %s" % sys.exc_value
222                logging.error(msg)
223        if style2 == GUIFRAME.PLOTTING_ON:
224            try:
225                from sans.guiframe.local_perspectives.plotting import plotting
226                self._plotting_plugin = plotting.Plugin()
227                plugins.append(self._plotting_plugin)
228            except:
229                msg = "ViewerFrame._get_local_plugins:"
230                msg += "cannot import plotting plugin.\n %s" % sys.exc_value
231                logging.error(msg)
232     
233        return plugins
234   
235    def _find_plugins(self, dir="perspectives"):
236        """
237        Find available perspective plug-ins
238       
239        :param dir: directory in which to look for plug-ins
240       
241        :return: list of plug-ins
242       
243        """
244        import imp
245        plugins = []
246        # Go through files in panels directory
247        try:
248            list = os.listdir(dir)
249            ## the default panel is the panel is the last plugin added
250            for item in list:
251                toks = os.path.splitext(os.path.basename(item))
252                name = None
253                if not toks[0] == '__init__':
254                   
255                    if toks[1] == '.py' or toks[1] == '':
256                        name = toks[0]
257               
258                    path = [os.path.abspath(dir)]
259                    file = None
260                    try:
261                        if toks[1] == '':
262                            mod_path = '.'.join([dir, name])
263                            module = __import__(mod_path, globals(),
264                                                locals(), [name])
265                        else:
266                            (file, path, info) = imp.find_module(name, path)
267                            module = imp.load_module( name, file, item, info)
268                        if hasattr(module, "PLUGIN_ID"):
269                            try: 
270                                plug = module.Plugin()
271                                if plug.set_default_perspective():
272                                    self._current_perspective = plug
273                                plugins.append(plug)
274                                msg = "Found plug-in: %s" % module.PLUGIN_ID
275                                logging.info(msg)
276                            except:
277                                msg = "Error accessing PluginPanel"
278                                msg += " in %s\n  %s" % (name, sys.exc_value)
279                                config.printEVT(msg)
280                    except:
281                        print sys.exc_value
282                        msg = "ViewerFrame._find_plugins: %s" % sys.exc_value
283                        logging.error(msg)
284                    finally:
285                        if not file == None:
286                            file.close()
287        except:
288            # Should raise and catch at a higher level and
289            # display error on status bar
290            pass   
291        return plugins
292   
293    def set_welcome_panel(self, panel_class):
294        """
295        Sets the default panel as the given welcome panel
296       
297        :param panel_class: class of the welcome panel to be instantiated
298       
299        """
300        self.defaultPanel = panel_class(self, -1, style=wx.RAISED_BORDER)
301       
302    def _get_panels_size(self, p):
303        """
304        find the proper size of the current panel
305        get the proper panel width and height
306        """
307        panel_height_min = self._window_height
308        panel_width_min = self._window_width
309        style = self.__gui_style & (GUIFRAME.MANAGER_ON)
310        if self._data_panel is not None  and (p == self._data_panel):
311            panel_width_min = self._window_width * 2/25 
312            return panel_width_min, panel_height_min
313        if hasattr(p, "CENTER_PANE") and p.CENTER_PANE:
314            style = self.__gui_style & (GUIFRAME.PLOTTING_ON|GUIFRAME.MANAGER_ON)
315            if style == (GUIFRAME.PLOTTING_ON|GUIFRAME.MANAGER_ON):
316                panel_width_min = self._window_width * 17/25 
317            return panel_width_min, panel_height_min
318        return panel_width_min, panel_height_min
319   
320    def _load_panels(self):
321        """
322        Load all panels in the panels directory
323        """
324       
325        # Look for plug-in panels
326        panels = []   
327        for item in self.plugins:
328            if hasattr(item, "get_panels"):
329                ps = item.get_panels(self)
330                panels.extend(ps)
331       
332        # Show a default panel with some help information
333        # It also sets the size of the application windows
334        #TODO: Use this for slpash screen
335        if self.defaultPanel is None:
336            self.defaultPanel = DefaultPanel(self, -1, style=wx.RAISED_BORDER)
337        # add a blank default panel always present
338        self.panels["default"] = self.defaultPanel
339        self._mgr.AddPane(self.defaultPanel, wx.aui.AuiPaneInfo().
340                              Name("default").
341                              Center().
342                              CloseButton(True).
343                              # This is where we set the size of
344                              # the application window
345                              BestSize(wx.Size(self._window_width, 
346                                               self._window_height)).
347                              Show())
348        #add data panel
349        self.panels["data_panel"] = self._data_panel
350        w, h = self._get_panels_size(self._data_panel)
351        self._mgr.AddPane(self._data_panel, wx.aui.AuiPaneInfo().
352                              Name(self._data_panel.window_name).
353                              Left().
354                              MinimizeButton().
355                              MinSize(wx.Size(w, h)).
356                              Show())
357        style = self.__gui_style & GUIFRAME.MANAGER_ON
358        if style != GUIFRAME.MANAGER_ON:
359            self._mgr.GetPane(self.panels["data_panel"].window_name).Hide()
360           
361        # Add the panels to the AUI manager
362        for panel_class in panels:
363            p = panel_class
364            id = wx.NewId()
365            w, h = self._get_panels_size(p)
366            # Check whether we need to put this panel
367            # in the center pane
368            if hasattr(p, "CENTER_PANE") and p.CENTER_PANE:
369                if p.CENTER_PANE:
370                    self.panels[str(id)] = p
371                    self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
372                                          Name(p.window_name).Caption(p.window_caption).
373                                           CenterPane().
374                                           MinSize(wx.Size(w, h)).
375                                           Hide())
376            else:
377                self.panels[str(id)] = p
378                self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
379                                  Name(p.window_name).Caption(p.window_caption).
380                                  Right().
381                                  Dock().
382                                  TopDockable().
383                                  BottomDockable().
384                                  LeftDockable().
385                                  RightDockable().
386                                  MinimizeButton().
387                                  Hide())       
388     
389    def get_context_menu(self, graph=None):
390        """
391        Get the context menu items made available
392        by the different plug-ins.
393        This function is used by the plotting module
394        """
395        menu_list = []
396        for item in self.plugins:
397            if hasattr(item, "get_context_menu"):
398                menu_list.extend(item.get_context_menu(graph))
399        return menu_list
400       
401    def popup_panel(self, p):
402        """
403        Add a panel object to the AUI manager
404       
405        :param p: panel object to add to the AUI manager
406       
407        :return: ID of the event associated with the new panel [int]
408       
409        """
410        ID = wx.NewId()
411        self.panels[str(ID)] = p
412        count = 0
413        for item in self.panels:
414            if self.panels[item].window_name.startswith(p.window_name): 
415                count += 1
416        windowname = p.window_name
417        caption = p.window_caption
418        if count > 0:
419            windowname += str(count+1)
420            caption += (' '+str(count))
421        p.window_name = windowname
422        p.window_caption = caption
423           
424        style1 = self.__gui_style & GUIFRAME.FIXED_PANEL
425        style2 = self.__gui_style & GUIFRAME.FLOATING_PANEL
426        if style1 == GUIFRAME.FIXED_PANEL:
427            self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
428                              Name(windowname).Caption(caption).
429                              MinimizeButton().
430                              Resizable(True).
431                              # Use a large best size to make sure the AUI
432                              # manager takes all the available space
433                              BestSize(wx.Size(PLOPANEL_WIDTH, PLOPANEL_HEIGTH)))
434            self._popup_fixed_panel(p)
435   
436        elif style2 == GUIFRAME.FLOATING_PANEL:
437            self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
438                              Name(windowname).Caption(caption).
439                              MinimizeButton().
440                              Resizable(True).
441                              # Use a large best size to make sure the AUI
442                              #  manager takes all the available space
443                              BestSize(wx.Size(PLOPANEL_WIDTH, PLOPANEL_HEIGTH)))
444            self._popup_floating_panel(p)
445           
446        pane = self._mgr.GetPane(windowname)
447        self._mgr.MaximizePane(pane)
448        self._mgr.RestoreMaximizedPane()
449        # Register for showing/hiding the panel
450        wx.EVT_MENU(self, ID, self._on_view)
451       
452        self._mgr.Update()
453        return ID
454       
455    def _populate_file_menu(self):
456        """
457        Insert menu item under file menu
458        """
459        for plugin in self.plugins:
460            if len(plugin.populate_file_menu()) > 0:
461                for item in plugin.populate_file_menu():
462                    id = wx.NewId()
463                    m_name, m_hint, m_handler = item
464                    self._filemenu.Append(id, m_name, m_hint)
465                    wx.EVT_MENU(self, id, m_handler)
466                self._filemenu.AppendSeparator()
467               
468    def _setup_menus(self):
469        """
470        Set up the application menus
471        """
472        # Menu
473        self._menubar = wx.MenuBar()
474        self._add_menu_file()
475        self._add_menu_data()
476        self._add_menu_application()
477        self._add_menu_tool()
478        self._add_menu_window()
479        self._add_help_menu()
480        self._add_current_plugin_menu()
481        self.SetMenuBar(self._menubar)
482       
483    def _add_menu_tool(self):
484        """
485        Tools menu
486        Go through plug-ins and find tools to populate the tools menu
487        """
488        style = self.__gui_style & GUIFRAME.TOOL_ON
489        if style == GUIFRAME.TOOL_ON:
490            self._tool_menu = None
491            for item in self.plugins:
492                if hasattr(item, "get_tools"):
493                    for tool in item.get_tools():
494                        # Only create a menu if we have at least one tool
495                        if self._tool_menu is None:
496                            self._tool_menu = wx.Menu()
497                        id = wx.NewId()
498                        self._tool_menu.Append(id, tool[0], tool[1])
499                        wx.EVT_MENU(self, id, tool[2])
500            if self._tool_menu is not None:
501                self._menubar.Append(self._tool_menu, '&Tools')
502               
503    def _add_current_plugin_menu(self):
504        """
505        add current plugin menu
506        Look for plug-in menus
507        Add available plug-in sub-menus.
508        """
509        if self._menubar is None:
510            return
511       
512        pos = self._menubar.GetMenuCount()
513        if self._current_perspective is not None:
514            if self._current_perspective != self._plotting_plugin:
515                menu_list = self._current_perspective.populate_menu(self)
516                if menu_list:
517                    for (menu, name) in menu_list:
518                        self._plugin_menu = self._menubar.Append(menu, 'Others')
519                #else:
520                #    self._plugin_menu = self._menubar.Append(wx.Menu(), 'all')             
521        #else:
522        #    self._plugin_menu = self._menubar.Insert(pos, wx.Menu(), 'all')
523                       
524    def _add_help_menu(self):
525        """
526        add help menu
527        """
528        # Help menu
529        self._help_menu = wx.Menu()
530        # add the welcome panel menu item
531        if self.defaultPanel is not None:
532            id = wx.NewId()
533            self._help_menu.Append(id, '&Welcome', '')
534            self._help_menu.AppendSeparator()
535            wx.EVT_MENU(self, id, self.show_welcome_panel)
536        # Look for help item in plug-ins
537        for item in self.plugins:
538            if hasattr(item, "help"):
539                id = wx.NewId()
540                self._help_menu.Append(id,'&%s help' % item.sub_menu, '')
541                wx.EVT_MENU(self, id, item.help)
542        if config._do_aboutbox:
543            id = wx.NewId()
544            self._help_menu.Append(id,'&About', 'Software information')
545            wx.EVT_MENU(self, id, self._onAbout)
546       
547        # Checking for updates needs major refactoring to work with py2exe
548        # We need to make sure it doesn't hang the application if the server
549        # is not up. We also need to make sure there's a proper executable to
550        # run if we spawn a new background process.
551        #id = wx.NewId()
552        #self._help_menu.Append(id,'&Check for update',
553        #'Check for the latest version of %s' % config.__appname__)
554        #wx.EVT_MENU(self, id, self._check_update)
555        self._menubar.Append(self._help_menu, '&Help')
556           
557    def _add_menu_window(self):
558        """
559        add a menu window to the menu bar
560        Window menu
561        Attach a menu item for each panel in our
562        panel list that also appears in a plug-in.
563       
564        Only add the panel menu if there is only one perspective and
565        it has more than two panels.
566        Note: the first plug-in is always the plotting plug-in.
567        The first application
568        #plug-in is always the second one in the list.
569        """
570        self._window_menu = wx.Menu()
571        if self._plotting_plugin is not None:
572            for (menu, name) in self._plotting_plugin.populate_menu(self):
573                self._window_menu.AppendSubMenu(menu, name)
574        self._window_menu.AppendSeparator()
575        self._menubar.Append(self._window_menu, '&Window')
576     
577        style = self.__gui_style & GUIFRAME.MANAGER_ON
578        if style == GUIFRAME.MANAGER_ON:
579            id = wx.NewId()
580            self._window_menu.Append(id,'&Data Manager', '')
581            wx.EVT_MENU(self, id, self.show_data_panel)
582           
583        id = wx.NewId()
584        preferences_menu = wx.Menu()
585        hint = "Plot panels will floating"
586        preferences_menu.Append(id, '&Floating Plot Panel', hint)
587        wx.EVT_MENU(self, id, self.set_plotpanel_floating)
588        id = wx.NewId()
589        hint = "Plot panels will displayed within the frame"
590        preferences_menu.Append(id, '&Fixed Plot Panel', hint)
591        wx.EVT_MENU(self, id, self.set_plotpanel_fixed)
592        id = wx.NewId()
593        self._window_menu.AppendSubMenu(preferences_menu,'&Preferences')
594        #wx.EVT_MENU(self, id, self.show_preferences_panel)   
595        """
596        if len(self.plugins) == 2:
597            plug = self.plugins[1]
598            pers = plug.get_perspective()
599       
600            if len(pers) > 1:
601                self._window_menu = wx.Menu()
602                for item in self.panels:
603                    if item == 'default':
604                        continue
605                    panel = self.panels[item]
606                    if panel.window_name in pers:
607                        self._window_menu.Append(int(item),
608                                                  panel.window_caption,
609                                        "Show %s window" % panel.window_caption)
610                        wx.EVT_MENU(self, int(item), self._on_view)
611                self._menubar.Append(self._window_menu, '&Window')
612                """
613               
614    def _add_menu_application(self):
615        """
616       
617        # Attach a menu item for each defined perspective or application.
618        # Only add the perspective menu if there are more than one perspectives
619        add menu application
620        """
621        style = self.__gui_style & GUIFRAME.MULTIPLE_APPLICATIONS
622        if style == GUIFRAME.MULTIPLE_APPLICATIONS:
623            p_menu = wx.Menu()
624            for plug in self.plugins:
625                if len(plug.get_perspective()) > 0:
626                    id = wx.NewId()
627                    p_menu.Append(id, plug.sub_menu,
628                                  "Switch to application: %s" % plug.sub_menu)
629                    wx.EVT_MENU(self, id, plug.on_perspective)
630            self._menubar.Append(p_menu, '&Applications')
631           
632    def _add_menu_file(self):
633        """
634        add menu file
635        """
636         # File menu
637        self._filemenu = wx.Menu()
638        # some menu of plugin to be seen under file menu
639        self._populate_file_menu()
640        id = wx.NewId()
641        self._filemenu.Append(id, '&Save state into File',
642                             'Save project as a SansView (svs) file')
643        wx.EVT_MENU(self, id, self._on_save)
644        id = wx.NewId()
645        self._filemenu.Append(id, '&Quit', 'Exit') 
646        wx.EVT_MENU(self, id, self.Close)
647        # Add sub menus
648        self._menubar.Append(self._filemenu, '&File')
649       
650    def _add_menu_data(self):
651        """
652        Add menu item item data to menu bar
653        """
654        # Add menu data
655        self._data_menu = wx.Menu()
656        #menu for data files
657        data_file_id = wx.NewId()
658        data_file_hint = "load one or more data in the application"
659        self._data_menu.Append(data_file_id, 
660                         '&Load Data File(s)', data_file_hint)
661        wx.EVT_MENU(self, data_file_id, self._load_data)
662        style = self.__gui_style & GUIFRAME.MULTIPLE_APPLICATIONS
663        style1 = self.__gui_style & GUIFRAME.SINGLE_APPLICATION
664        if style == GUIFRAME.MULTIPLE_APPLICATIONS:
665            #menu for data from folder
666            data_folder_id = wx.NewId()
667            data_folder_hint = "load multiple data in the application"
668            self._data_menu.Append(data_folder_id, 
669                             '&Load Data Folder', data_folder_hint)
670            wx.EVT_MENU(self, data_folder_id, self._load_folder)
671            self._menubar.Append(self._data_menu, '&Data')
672        elif style1 == GUIFRAME.SINGLE_APPLICATION:
673            self._menubar.Append(self._data_menu, '&Data')
674       
675    def _load_data(self, event):
676        """
677        connect menu item load data with the first plugin that can load data
678        """
679        for plug in self.plugins:
680            if plug.can_load_data():
681                plug.load_data(event)
682        style = self.__gui_style & GUIFRAME.MANAGER_ON
683        if style == GUIFRAME.MANAGER_ON:
684            self.show_data_panel(event=None)
685       
686    def _load_folder(self, event):
687        """
688        connect menu item load data with the first plugin that can load data and
689        folder
690        """
691        for plug in self.plugins:
692            if plug.can_load_data():
693                plug.load_folder(event)
694        style = self.__gui_style & GUIFRAME.MANAGER_ON
695        if style == GUIFRAME.MANAGER_ON:
696            self.show_data_panel(event=None)
697               
698    def _on_status_event(self, evt):
699        """
700        Display status message
701        """
702        self.sb.set_status(event=evt)
703       
704    def _on_view(self, evt):
705        """
706        A panel was selected to be shown. If it's not already
707        shown, display it.
708       
709        :param evt: menu event
710       
711        """
712        self.show_panel(evt.GetId())
713       
714    def on_close_welcome_panel(self):
715        """
716        Close the welcome panel
717        """
718        if self.defaultPanel is None:
719            return 
720        self._mgr.GetPane(self.panels["default"].window_name).Hide()
721        self._mgr.Update()
722        # set a default perspective
723        self.set_default_perspective()
724       
725    def show_welcome_panel(self, event):
726        """   
727        Display the welcome panel
728        """
729        if self.defaultPanel is None:
730            return 
731        for id in self.panels.keys():
732            if id  ==  'default':
733                # Show default panel
734                if not self._mgr.GetPane(self.panels["default"].window_name).IsShown():
735                    self._mgr.GetPane(self.panels["default"].window_name).Show(True)
736            elif id == "data_panel":
737                flag = self._mgr.GetPane(self.panels["data_panel"].window_name).IsShown()
738                self._mgr.GetPane(self.panels["data_panel"].window_name).Show(flag)
739            else:
740                self._mgr.GetPane(self.panels[id].window_name).IsShown()
741                self._mgr.GetPane(self.panels[id].window_name).Hide()
742        self._mgr.Update()
743       
744    def show_panel(self, uid):
745        """
746        Shows the panel with the given id
747       
748        :param uid: unique ID number of the panel to show
749       
750        """
751        ID = str(uid)
752        config.printEVT("show_panel: %s" % ID)
753        if ID in self.panels.keys():
754            if not self._mgr.GetPane(self.panels[ID].window_name).IsShown():
755                self._mgr.GetPane(self.panels[ID].window_name).Show()
756                # Hide default panel
757                self._mgr.GetPane(self.panels["default"].window_name).Hide()
758            self._mgr.Update()
759   
760    def _on_open(self, event):
761        """
762        """
763        path = self.choose_file()
764        if path is None:
765            return
766       
767        from data_loader import plot_data
768        from sans.perspectives import invariant
769        if path and os.path.isfile(path):
770            basename  = os.path.basename(path)
771            if  basename.endswith('.svs'):
772                #remove panels for new states
773                for item in self.panels:
774                    try:
775                        self.panels[item].clear_panel()
776                    except:
777                        pass
778                #reset states and plot data
779                for item in STATE_FILE_EXT:
780                    exec "plot_data(self, path,'%s')" % str(item)
781            else:
782                plot_data(self, path)
783        if self.defaultPanel is not None and \
784            self._mgr.GetPane(self.panels["default"].window_name).IsShown():
785            self.on_close_welcome_panel()
786           
787    def _on_save(self, event):
788        """
789        Save state into a file
790        """
791        # Ask the user the location of the file to write to.
792       
793        ## Default file location for save
794        self._default_save_location = os.getcwd()
795        path = None
796        dlg = wx.FileDialog(self, "Choose a file",
797                            self._default_save_location, "", "*.svs", wx.SAVE)
798        if dlg.ShowModal() == wx.ID_OK:
799            path = dlg.GetPath()
800            self._default_save_location = os.path.dirname(path)
801        else:
802            return None
803        dlg.Destroy()
804        if path is None:
805            return
806        # default cansas xml doc
807        doc = None
808        for item in self.panels:
809            try:
810                if self.panels[item].window_name == 'Invariant':
811                    data = self.panels[item]._data
812                    if data != None:
813                        state = self.panels[item].state
814                        manager = self.panels[item]._manager
815                        new_doc = manager.state_reader.write_toXML(data, state)
816                        if hasattr(doc, "firstChild"):
817                            child = new_doc.firstChild.firstChild
818                            doc.firstChild.appendChild(child) 
819                        else:
820                            doc = new_doc
821                elif self.panels[item].window_name == 'pr_control':
822                    data = self.panels[item].manager.current_plottable
823                    if data != None:
824                        state = self.panels[item].get_state()
825                        manager = self.panels[item].manager
826                        new_doc = manager.state_reader.write_toXML(data, state)
827                        if hasattr(doc, "firstChild"):
828                            child = new_doc.firstChild.firstChild
829                            doc.firstChild.appendChild(child) 
830                        else:
831                            doc = new_doc
832                elif self.panels[item].window_name == 'Fit panel':
833                    for index in range(self.panels[item].GetPageCount()):
834                        selected_page = self.panels[item].GetPage(index) 
835                        if hasattr(selected_page,"get_data"):
836                            data = selected_page.get_data()
837                            state = selected_page.state
838                            reader = selected_page.manager.state_reader
839                            new_doc = reader.write_toXML(data, state)
840                            if doc != None and hasattr(doc, "firstChild"):
841                                child = new_doc.firstChild.firstChild
842                                doc.firstChild.appendChild(child)
843                            else:
844                                doc = new_doc
845            except: 
846                pass
847        # Write the XML document
848        if doc != None:
849            fd = open(path, 'w')
850            fd.write(doc.toprettyxml())
851            fd.close()
852        else:
853            #print "Nothing to save..."
854            raise RuntimeError, "%s is not a SansView (.svs) file..." % path
855
856    def _onClose(self, event):
857        """
858        Store info to retrieve in xml before closing the application
859        """
860        wx.Exit()
861        sys.exit()
862                 
863    def quit_guiframe(self):
864        """
865        Pop up message to make sure the user wants to quit the application
866        """
867        message = "Do you really want to quit \n"
868        message += "this application?"
869        dial = wx.MessageDialog(self, message, 'Question',
870                           wx.YES_NO|wx.YES_DEFAULT|wx.ICON_QUESTION)
871        if dial.ShowModal() == wx.ID_YES:
872            return True
873        else:
874            return False   
875       
876    def Close(self, event=None):
877        """
878        Quit the application
879        """
880        flag = self.quit_guiframe()
881        if flag:
882            wx.Frame.Close(self)
883            wx.Exit()
884            sys.exit()
885
886    def _check_update(self, event=None): 
887        """
888        Check with the deployment server whether a new version
889        of the application is available.
890        A thread is started for the connecting with the server. The thread calls
891        a call-back method when the current version number has been obtained.
892        """
893        if hasattr(config, "__update_URL__"):
894            import version
895            checker = version.VersionThread(config.__update_URL__,
896                                            self._process_version,
897                                            baggage=event==None)
898            checker.start() 
899   
900    def _process_version(self, version, standalone=True):
901        """
902        Call-back method for the process of checking for updates.
903        This methods is called by a VersionThread object once the current
904        version number has been obtained. If the check is being done in the
905        background, the user will not be notified unless there's an update.
906       
907        :param version: version string
908        :param standalone: True of the update is being checked in
909           the background, False otherwise.
910           
911        """
912        try:
913            if cmp(version, config.__version__) > 0:
914                msg = "Version %s is available! See the Help "
915                msg += "menu to download it." % version
916                self.SetStatusText(msg)
917                if not standalone:
918                    import webbrowser
919                    webbrowser.open(config.__download_page__)
920            else:
921                if not standalone:
922                    msg = "You have the latest version"
923                    msg += " of %s" % config.__appname__
924                    self.SetStatusText(msg)
925        except:
926            msg = "guiframe: could not get latest application"
927            msg += " version number\n  %s" % sys.exc_value
928            logging.error(msg)
929            if not standalone:
930                msg = "Could not connect to the application server."
931                msg += " Please try again later."
932                self.SetStatusText(msg)
933                   
934    def _onAbout(self, evt):
935        """
936        Pop up the about dialog
937       
938        :param evt: menu event
939       
940        """
941        if config._do_aboutbox:
942            import aboutbox 
943            dialog = aboutbox.DialogAbout(None, -1, "")
944            dialog.ShowModal()           
945           
946    def set_manager(self, manager):
947        """
948        Sets the application manager for this frame
949       
950        :param manager: frame manager
951        """
952        self.app_manager = manager
953       
954    def post_init(self):
955        """
956        This initialization method is called after the GUI
957        has been created and all plug-ins loaded. It calls
958        the post_init() method of each plug-in (if it exists)
959        so that final initialization can be done.
960        """
961        for item in self.plugins:
962            if hasattr(item, "post_init"):
963                item.post_init()
964       
965    def set_default_perspective(self):
966        """
967        Choose among the plugin the first plug-in that has
968        "set_default_perspective" method and its return value is True will be
969        as a default perspective when the welcome page is closed
970        """
971        for item in self.plugins:
972            if hasattr(item, "set_default_perspective"):
973                if item.set_default_perspective():
974                    item.on_perspective(event=None)
975                    return 
976       
977    def set_perspective(self, panels):
978        """
979        Sets the perspective of the GUI.
980        Opens all the panels in the list, and closes
981        all the others.
982       
983        :param panels: list of panels
984        """
985        for item in self.panels:
986            # Check whether this is a sticky panel
987            if hasattr(self.panels[item], "ALWAYS_ON"):
988                if self.panels[item].ALWAYS_ON:
989                    continue 
990            if self.panels[item].window_name in panels:
991                if not self._mgr.GetPane(self.panels[item].window_name).IsShown():
992                    self._mgr.GetPane(self.panels[item].window_name).Show()
993            else:
994                # always show the data panel if enable
995                style = self.__gui_style & GUIFRAME.MANAGER_ON
996                if (style == GUIFRAME.MANAGER_ON) and self.panels[item] == self._data_panel:
997                    if 'data_panel' in self.panels.keys():
998                        flag = self._mgr.GetPane(self.panels['data_panel'].window_name).IsShown()
999                        self._mgr.GetPane(self.panels['data_panel'].window_name).Show(flag)
1000                else:
1001                    if self._mgr.GetPane(self.panels[item].window_name).IsShown():
1002                        self._mgr.GetPane(self.panels[item].window_name).Hide()
1003        self._mgr.Update()
1004       
1005    def choose_file(self, path=None):
1006        """
1007        Functionality that belongs elsewhere
1008        Should add a hook to specify the preferred file type/extension.
1009        """
1010        #TODO: clean this up
1011        from .data_loader import choose_data_file
1012        # Choose a file path
1013        if path == None:
1014            path = choose_data_file(self, self._default_save_location)
1015        if not path == None:
1016            try:
1017                self._default_save_location = os.path.dirname(path)
1018            except:
1019                pass
1020        return path
1021   
1022    def show_data_panel(self, event=None):
1023        """
1024        show the data panel
1025        """
1026        pane = self._mgr.GetPane(self.panels["data_panel"].window_name)
1027        #if not pane.IsShown():
1028        pane.Show(True)
1029        self._mgr.Update()
1030 
1031    def add_data(self, data_list):
1032        """
1033        receive a list of data . store them its data manager if possible
1034        determine if data was be plot of send to data perspectives
1035        """
1036        #send a list of available data to plotting plugin
1037        avalaible_data = []
1038        if self._data_manager is not None:
1039            self._data_manager.add_data(data_list)
1040            avalaible_data = self._data_manager.get_all_data()
1041           
1042        style = self.__gui_style & GUIFRAME.MANAGER_ON
1043        if style == GUIFRAME.MANAGER_ON:
1044            if self._data_panel is not None:
1045                data_state = self._data_manager.get_selected_data()
1046                self._data_panel.load_data_list(data_state)
1047                self._mgr.GetPane(self._data_panel.window_name).Show(True)
1048                #wait for button press from the data panel to send data
1049        else:
1050            #automatically send that to the current perspective
1051            style = self.__gui_style & GUIFRAME.SINGLE_APPLICATION
1052            if style == GUIFRAME.SINGLE_APPLICATION:
1053                self.set_data(data_list)
1054               
1055    def get_data_from_panel(self, data_id, plot=False,append=False):
1056        """
1057        receive a list of data key retreive the data from data manager and set
1058        then to the current perspective
1059        """
1060        data_dict = self._data_manager.get_by_id(data_id)
1061        data_list = []
1062        for data_state in data_dict.values():
1063            data_list.append(data_state.data)
1064        if plot:
1065            self.plot_data(data_list, append=append)
1066        else:
1067            #sent data to active application
1068            self.set_data(data_list=data_list)
1069       
1070       
1071    def set_data(self, data_list):
1072        """
1073        set data to current perspective
1074        """
1075        if self._current_perspective is not None:
1076            try:
1077                self._current_perspective.set_data(data_list)
1078            except:
1079                msg = str(sys.exc_value)
1080                wx.PostEvent(self, StatusEvent(status=msg, info="error"))
1081        else:
1082            msg = "Guiframe does not have a current perspective"
1083            logging.info(msg)
1084           
1085    def plot_data(self, data_list, append=False):
1086        """
1087        send a list of data to plot
1088        """
1089        if not data_list:
1090            message = "Please check data to plot or append"
1091            wx.PostEvent(self, StatusEvent(status=message, info='warning'))
1092            return 
1093        for new_plot in data_list:
1094            if append:
1095                if self.panel_on_focus is None or \
1096                    not self.enable_add_data(new_plot):
1097                    message = "cannot append plot. No plot panel on focus!"
1098                    message += "please click on any available plot to set focus"
1099                    wx.PostEvent(self, StatusEvent(status=message, 
1100                                                   info='warning'))
1101                    return 
1102                else:
1103                    if self.enable_add_data(new_plot):
1104                        new_plot.group_id = self.panel_on_focus.group_id
1105            wx.PostEvent(self, NewPlotEvent(plot=new_plot,
1106                                                  title=str(new_plot.title)))
1107           
1108    def add_theory(self, data_id, theory):
1109        """
1110        """
1111        self._data_manager.append_theory(data_id, theory)
1112        style = self.__gui_style & GUIFRAME.MANAGER_ON
1113        if style == GUIFRAME.MANAGER_ON:
1114            if self._data_panel is not None:
1115                data_state = self._data_manager.get_by_id([data_id])
1116                self._data_panel.load_data_list(data_state)
1117               
1118    def delete_data(self, data_id, theory_id=None, delete_all=True):
1119        """
1120        Delete data state if data_id is provide
1121        delete theory created with data of id data_id if theory_id is provide
1122        if delete all true: delete the all state
1123        else delete theory
1124        """
1125        self._data_manager.delete_data(data_id=data_id, 
1126                                       theory_id=theory_id, 
1127                                       delete_all=delete_all)
1128        for plug in self.plugins:
1129            plug.delete_data(data_id)
1130           
1131       
1132    def set_current_perspective(self, perspective):
1133        """
1134        set the current active perspective
1135        """
1136        self._current_perspective = perspective
1137        name = "No current Application selected"
1138        if self._current_perspective is not None:
1139            self._add_current_plugin_menu()
1140            if self._data_panel is not None:
1141                name = self._current_perspective.sub_menu
1142                self._data_panel.set_active_perspective(name)
1143           
1144    def set_plotpanel_floating(self, event=None):
1145        """
1146        make the plot panel floatable
1147        """
1148        self.__gui_style &= (~GUIFRAME.FIXED_PANEL)
1149        self.__gui_style |= GUIFRAME.FLOATING_PANEL
1150        for p in self.panels.values():
1151            plot_panel = self._plotting_plugin.plot_panels
1152            for p in self.panels.values():
1153                if p in plot_panel:
1154                    self._popup_floating_panel(p)
1155       
1156    def set_plotpanel_fixed(self, event=None):
1157        """
1158        make the plot panel fixed
1159        """
1160        self.__gui_style &= (~GUIFRAME.FLOATING_PANEL)
1161        self.__gui_style |= GUIFRAME.FIXED_PANEL
1162        plot_panel = []
1163        if self._plotting_plugin is not None:
1164            plot_panel = self._plotting_plugin.plot_panels
1165            for p in self.panels.values():
1166                if p in plot_panel:
1167                    self._popup_fixed_panel(p)
1168                   
1169    def _popup_fixed_panel(self, p):
1170        """
1171        """
1172        style = self.__gui_style & GUIFRAME.FIXED_PANEL
1173        if style == GUIFRAME.FIXED_PANEL:
1174            self._mgr.GetPane(p.window_name).Floatable()
1175            self._mgr.GetPane(p.window_name).Right()
1176            self._mgr.GetPane(p.window_name).TopDockable(False)
1177            self._mgr.GetPane(p.window_name).BottomDockable(False)
1178            self._mgr.GetPane(p.window_name).LeftDockable(False)
1179            self._mgr.GetPane(p.window_name).RightDockable(True)
1180            flag = self._mgr.GetPane(p.window_name).IsShown()
1181            self._mgr.GetPane(p.window_name).Show(flag)
1182            self._mgr.Update()
1183           
1184    def _popup_floating_panel(self, p):
1185        """
1186        """
1187        style = self.__gui_style &  GUIFRAME.FLOATING_PANEL
1188        if style == GUIFRAME.FLOATING_PANEL: 
1189            self._mgr.GetPane(p.window_name).Floatable(True)
1190            self._mgr.GetPane(p.window_name).Float()
1191            self._mgr.GetPane(p.window_name).Dockable(False)
1192            flag = self._mgr.GetPane(p.window_name).IsShown()
1193            self._mgr.GetPane(p.window_name).Show(flag)
1194            self._mgr.Update()
1195           
1196    def enable_add_data(self, new_plot):
1197        """
1198        Enable append data on a plot panel
1199        """
1200        is_theory = len(self.panel_on_focus.plots) <= 1 and \
1201            self.panel_on_focus.plots.values()[0].__class__.__name__ == "Theory1D"
1202           
1203        is_data2d = hasattr(new_plot, 'data')
1204        is_data1d = self.panel_on_focus.__class__.__name__ == "ModelPanel1D"\
1205            and self.panel_on_focus.group_id is not None
1206        has_meta_data = hasattr(new_plot, 'meta_data')
1207       
1208        #disable_add_data if the data is being recovered from  a saved state file.
1209        is_state_data = False
1210        if has_meta_data:
1211            if 'invstate' in new_plot.meta_data: is_state_data = True
1212            if  'prstate' in new_plot.meta_data: is_state_data = True
1213            if  'fitstate' in new_plot.meta_data: is_state_data = True
1214   
1215        return is_data1d and not is_data2d and not is_theory and not is_state_data
1216       
1217class DefaultPanel(wx.Panel):
1218    """
1219    Defines the API for a panels to work with
1220    the GUI manager
1221    """
1222    ## Internal nickname for the window, used by the AUI manager
1223    window_name = "default"
1224    ## Name to appear on the window title bar
1225    window_caption = "Welcome panel"
1226    ## Flag to tell the AUI manager to put this panel in the center pane
1227    CENTER_PANE = True
1228
1229 
1230# Toy application to test this Frame
1231class ViewApp(wx.App):
1232    """
1233    """
1234    def OnInit(self):
1235        """
1236        """
1237        self.frame = ViewerFrame(None, -1, config.__appname__)   
1238        self.frame.Show(True)
1239
1240        if hasattr(self.frame, 'special'):
1241            self.frame.special.SetCurrent()
1242        self.SetTopWindow(self.frame)
1243        return True
1244   
1245    def set_manager(self, manager):
1246        """
1247        Sets a reference to the application manager
1248        of the GUI manager (Frame)
1249        """
1250        self.frame.set_manager(manager)
1251       
1252    def build_gui(self):
1253        """
1254        Build the GUI
1255        """
1256        self.frame.build_gui()
1257        self.frame.post_init()
1258       
1259    def set_welcome_panel(self, panel_class):
1260        """
1261        Set the welcome panel
1262       
1263        :param panel_class: class of the welcome panel to be instantiated
1264       
1265        """
1266        self.frame.set_welcome_panel(panel_class)
1267       
1268    def add_perspective(self, perspective):
1269        """
1270        Manually add a perspective to the application GUI
1271        """
1272        self.frame.add_perspective(perspective)
1273       
1274
1275if __name__ == "__main__": 
1276    app = ViewApp(0)
1277    app.MainLoop()
1278
1279             
Note: See TracBrowser for help on using the repository browser.