source: sasview/src/sans/guiframe/gui_manager.py @ 51af54b

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 51af54b was 51af54b, checked in by Jeff Krzywon <jeffery.krzywon@…>, 10 years ago

Fixing project save/load and changed up an poorly worded error message.

  • Property mode set to 100644
File size: 129.0 KB
Line 
1"""
2    Gui manager: manages the widgets making up an application
3"""
4################################################################################
5#This software was developed by the University of Tennessee as part of the
6#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
7#project funded by the US National Science Foundation.
8#
9#See the license text in license.txtz
10#
11#copyright 2008, University of Tennessee
12################################################################################
13
14
15import wx
16import wx.aui
17import os
18import sys
19import time
20import imp
21import warnings
22import re
23warnings.simplefilter("ignore")
24import logging
25import httplib
26
27from sans.guiframe.events import EVT_CATEGORY
28from sans.guiframe.events import EVT_STATUS
29from sans.guiframe.events import EVT_APPEND_BOOKMARK
30from sans.guiframe.events import EVT_PANEL_ON_FOCUS
31from sans.guiframe.events import EVT_NEW_LOAD_DATA
32from sans.guiframe.events import EVT_NEW_COLOR
33from sans.guiframe.events import StatusEvent
34from sans.guiframe.events import NewPlotEvent
35from sans.guiframe.gui_style import GUIFRAME
36from sans.guiframe.gui_style import GUIFRAME_ID
37from sans.guiframe.data_panel import DataPanel
38from sans.guiframe.panel_base import PanelBase
39from sans.guiframe.gui_toolbar import GUIToolBar
40from sans.guiframe.data_processor import GridFrame
41from sans.guiframe.events import EVT_NEW_BATCH
42from sans.guiframe.CategoryManager import CategoryManager
43from sans.dataloader.loader import Loader
44from matplotlib import _pylab_helpers
45
46def get_app_dir():
47    """
48        The application directory is the one where the default custom_config.py
49        file resides.
50    """
51    # First, try the directory of the executable we are running
52    app_path = sys.path[0]
53    if os.path.isfile(app_path):
54        app_path = os.path.dirname(app_path)
55    if os.path.isfile(os.path.join(app_path, "custom_config.py")):
56        app_path = os.path.abspath(app_path)
57        logging.info("Using application path: %s", app_path)
58        return app_path
59   
60    # Next, try the current working directory
61    if os.path.isfile(os.path.join(os.getcwd(), "custom_config.py")):
62        logging.info("Using application path: %s", os.getcwd())
63        return os.path.abspath(os.getcwd())
64   
65    # Finally, try the directory of the sasview module
66    #TODO: gui_manager will have to know about sasview until we
67    # clean all these module variables and put them into a config class
68    # that can be passed by sasview.py.
69    logging.info(sys.executable)
70    logging.info(str(sys.argv))
71    from sans import sansview as sasview
72    app_path = os.path.dirname(sasview.__file__)
73    logging.info("Using application path: %s", app_path)
74    return app_path
75
76def get_user_directory():
77    """
78        Returns the user's home directory
79    """
80    userdir = os.path.join(os.path.expanduser("~"),".sasview")
81    if not os.path.isdir(userdir):
82        os.makedirs(userdir)
83    return userdir
84   
85def _find_local_config(file, path):
86    """
87        Find configuration file for the current application
88    """   
89    config_module = None
90    fObj = None
91    try:
92        fObj, path_config, descr = imp.find_module(file, [path])
93        config_module = imp.load_module(file, fObj, path_config, descr) 
94    except:
95        logging.error("Error loading %s/%s: %s" % (path, file, sys.exc_value))
96    finally:
97        if fObj is not None:
98            fObj.close()
99    logging.info("GuiManager loaded %s/%s" % (path, file))
100    return config_module
101
102# Get APP folder
103PATH_APP = get_app_dir() 
104DATAPATH = PATH_APP
105
106# GUI always starts from the App folder
107#os.chdir(PATH_APP)
108# Read in the local config, which can either be with the main
109# application or in the installation directory
110config = _find_local_config('local_config', PATH_APP)
111if config is None:
112    config = _find_local_config('local_config', os.getcwd())
113    if config is None:
114        # Didn't find local config, load the default
115        import sans.guiframe.config as config
116        logging.info("using default local_config")       
117    else:
118        logging.info("found local_config in %s" % os.getcwd()) 
119else:
120    logging.info("found local_config in %s" % PATH_APP)     
121           
122from sans.guiframe.customdir  import SetupCustom
123c_conf_dir = SetupCustom().setup_dir(PATH_APP)
124custom_config = _find_local_config('custom_config', c_conf_dir)
125if custom_config is None:
126    custom_config = _find_local_config('custom_config', os.getcwd())
127    if custom_config is None:
128        msgConfig = "Custom_config file was not imported"
129        logging.info(msgConfig)
130    else:
131        logging.info("using custom_config in %s" % os.getcwd())
132else:
133    logging.info("using custom_config from %s" % c_conf_dir)
134
135#read some constants from config
136APPLICATION_STATE_EXTENSION = config.APPLICATION_STATE_EXTENSION
137APPLICATION_NAME = config.__appname__
138SPLASH_SCREEN_PATH = config.SPLASH_SCREEN_PATH
139WELCOME_PANEL_ON = config.WELCOME_PANEL_ON
140SPLASH_SCREEN_WIDTH = config.SPLASH_SCREEN_WIDTH
141SPLASH_SCREEN_HEIGHT = config.SPLASH_SCREEN_HEIGHT
142SS_MAX_DISPLAY_TIME = config.SS_MAX_DISPLAY_TIME
143if not WELCOME_PANEL_ON:
144    WELCOME_PANEL_SHOW = False
145else:
146    WELCOME_PANEL_SHOW = True
147try:
148    DATALOADER_SHOW = custom_config.DATALOADER_SHOW
149    TOOLBAR_SHOW = custom_config.TOOLBAR_SHOW
150    FIXED_PANEL = custom_config.FIXED_PANEL
151    if WELCOME_PANEL_ON:
152        WELCOME_PANEL_SHOW = custom_config.WELCOME_PANEL_SHOW
153    PLOPANEL_WIDTH = custom_config.PLOPANEL_WIDTH
154    DATAPANEL_WIDTH = custom_config.DATAPANEL_WIDTH
155    GUIFRAME_WIDTH = custom_config.GUIFRAME_WIDTH
156    GUIFRAME_HEIGHT = custom_config.GUIFRAME_HEIGHT
157    CONTROL_WIDTH = custom_config.CONTROL_WIDTH
158    CONTROL_HEIGHT = custom_config.CONTROL_HEIGHT
159    DEFAULT_PERSPECTIVE = custom_config.DEFAULT_PERSPECTIVE
160    CLEANUP_PLOT = custom_config.CLEANUP_PLOT
161    # custom open_path
162    open_folder = custom_config.DEFAULT_OPEN_FOLDER
163    if open_folder != None and os.path.isdir(open_folder):
164        DEFAULT_OPEN_FOLDER = os.path.abspath(open_folder)
165    else:
166        DEFAULT_OPEN_FOLDER = PATH_APP
167except:
168    DATALOADER_SHOW = True
169    TOOLBAR_SHOW = True
170    FIXED_PANEL = True
171    WELCOME_PANEL_SHOW = False
172    PLOPANEL_WIDTH = config.PLOPANEL_WIDTH
173    DATAPANEL_WIDTH = config.DATAPANEL_WIDTH
174    GUIFRAME_WIDTH = config.GUIFRAME_WIDTH
175    GUIFRAME_HEIGHT = config.GUIFRAME_HEIGHT
176    CONTROL_WIDTH = -1 
177    CONTROL_HEIGHT = -1
178    DEFAULT_PERSPECTIVE = None
179    CLEANUP_PLOT = False
180    DEFAULT_OPEN_FOLDER = PATH_APP
181
182DEFAULT_STYLE = config.DEFAULT_STYLE
183
184PLUGIN_STATE_EXTENSIONS =  config.PLUGIN_STATE_EXTENSIONS
185OPEN_SAVE_MENU = config.OPEN_SAVE_PROJECT_MENU
186VIEW_MENU = config.VIEW_MENU
187EDIT_MENU = config.EDIT_MENU
188extension_list = []
189if APPLICATION_STATE_EXTENSION is not None:
190    extension_list.append(APPLICATION_STATE_EXTENSION)
191EXTENSIONS = PLUGIN_STATE_EXTENSIONS + extension_list
192try:
193    PLUGINS_WLIST = '|'.join(config.PLUGINS_WLIST)
194except:
195    PLUGINS_WLIST = ''
196APPLICATION_WLIST = config.APPLICATION_WLIST
197IS_WIN = True
198IS_LINUX = False
199CLOSE_SHOW = True
200TIME_FACTOR = 2
201MDI_STYLE = wx.DEFAULT_FRAME_STYLE
202NOT_SO_GRAPH_LIST = ["BoxSum"]
203PARENT_FRAME = wx.MDIParentFrame
204CHILD_FRAME = wx.MDIChildFrame
205if sys.platform.count("win32") < 1:
206    IS_WIN = False
207    TIME_FACTOR = 2
208    if int(str(wx.__version__).split('.')[0]) == 2:
209        if int(str(wx.__version__).split('.')[1]) < 9:
210            CLOSE_SHOW = False
211    if sys.platform.count("darwin") < 1:
212        IS_LINUX = True
213        PARENT_FRAME = wx.Frame
214        CHILD_FRAME = wx.Frame
215   
216class ViewerFrame(PARENT_FRAME):
217    """
218    Main application frame
219    """
220   
221    def __init__(self, parent, title, 
222                 size=(GUIFRAME_WIDTH, GUIFRAME_HEIGHT),
223                 gui_style=DEFAULT_STYLE, 
224                 style=wx.DEFAULT_FRAME_STYLE,
225                 pos=wx.DefaultPosition):
226        """
227        Initialize the Frame object
228        """
229
230        PARENT_FRAME.__init__(self, parent=parent, title=title, pos=pos, size=size)
231        # title
232        self.title = title
233        self.__gui_style = gui_style       
234        path = os.path.dirname(__file__)
235        temp_path = os.path.join(path,'images')
236        ico_file = os.path.join(temp_path,'ball.ico')
237        if os.path.isfile(ico_file):
238            self.SetIcon(wx.Icon(ico_file, wx.BITMAP_TYPE_ICO))
239        else:
240            temp_path = os.path.join(os.getcwd(),'images')
241            ico_file = os.path.join(temp_path,'ball.ico')
242            if os.path.isfile(ico_file):
243                self.SetIcon(wx.Icon(ico_file, wx.BITMAP_TYPE_ICO))
244            else:
245                ico_file = os.path.join(os.path.dirname(os.path.sys.path[0]),
246                             'images', 'ball.ico')
247                if os.path.isfile(ico_file):
248                    self.SetIcon(wx.Icon(ico_file, wx.BITMAP_TYPE_ICO))
249        self.path = PATH_APP
250        self.application_name = APPLICATION_NAME
251        ## Application manager
252        self._input_file = None
253        self.app_manager = None
254        self._mgr = None
255        #add current perpsective
256        self._current_perspective = None
257        self._plotting_plugin = None
258        self._data_plugin = None
259        #Menu bar and item
260        self._menubar = None
261        self._file_menu = None
262        self._data_menu = None
263        self._view_menu = None
264        self._data_panel_menu = None
265        self._help_menu = None
266        self._tool_menu = None
267        self._applications_menu_pos = -1
268        self._applications_menu_name = None
269        self._applications_menu = None
270        self._edit_menu = None
271        self._toolbar_menu = None
272        self._save_appl_menu = None
273        #tool bar
274        self._toolbar = None
275        # Status bar
276        self.sb = None
277        # number of plugins
278        self._num_perspectives = 0
279        # plot duck cleanup option
280        self.cleanup_plots = CLEANUP_PLOT
281        ## Find plug-ins
282        # Modify this so that we can specify the directory to look into
283        self.plugins = []
284        #add local plugin
285        self.plugins += self._get_local_plugins()
286        self.plugins += self._find_plugins()
287        ## List of panels
288        self.panels = {}
289        # List of plot panels
290        self.plot_panels = {}
291        # default Graph number fot the plotpanel caption
292        self.graph_num = 0
293
294        # Default locations
295        self._default_save_location = DEFAULT_OPEN_FOLDER       
296        # Welcome panel
297        self.defaultPanel = None
298        self.welcome_panel_class = None
299        #panel on focus
300        self.panel_on_focus = None
301        #control_panel on focus
302        self.cpanel_on_focus = None
303
304        self.loader = Loader()   
305        #data manager
306        self.batch_on = False
307        from sans.guiframe.data_manager import DataManager
308        self._data_manager = DataManager()
309        self._data_panel = None#DataPanel(parent=self)
310        if self.panel_on_focus is not None:
311            self._data_panel.set_panel_on_focus(
312                                self.panel_on_focus.window_caption)
313        # list of plot panels in schedule to full redraw
314        self.schedule = False
315        #self.callback = True
316        self._idle_count = 0
317        self.schedule_full_draw_list = []
318        self.idletimer = wx.CallLater(TIME_FACTOR, self._onDrawIdle)
319       
320        self.batch_frame = GridFrame(parent=self)
321        self.batch_frame.Hide()
322        self.on_batch_selection(event=None)
323        self.add_icon()
324       
325        # Register the close event so it calls our own method
326        wx.EVT_CLOSE(self, self.WindowClose)
327        # Register to status events
328        self.Bind(EVT_STATUS, self._on_status_event)
329        #Register add extra data on the same panel event on load
330        self.Bind(EVT_PANEL_ON_FOCUS, self.set_panel_on_focus)
331        self.Bind(EVT_APPEND_BOOKMARK, self.append_bookmark)
332        self.Bind(EVT_NEW_LOAD_DATA, self.on_load_data)
333        self.Bind(EVT_NEW_BATCH, self.on_batch_selection)
334        self.Bind(EVT_NEW_COLOR, self.on_color_selection)
335        self.Bind(EVT_CATEGORY, self.on_change_categories)
336        self.setup_custom_conf()
337        # Preferred window size
338        self._window_width, self._window_height = size
339       
340    def add_icon(self):
341        """
342        get list of child and attempt to add the default icon
343        """
344       
345        list_children = self.GetChildren() 
346        for frame in list_children:
347            self.put_icon(frame)
348       
349    def put_icon(self, frame): 
350        """
351        Put icon on the tap of a panel
352        """
353        if hasattr(frame, "IsIconized"):
354            if not frame.IsIconized():
355                try:
356                    icon = self.GetIcon()
357                    frame.SetIcon(icon)
358                except:
359                    pass 
360               
361    def get_client_size(self):
362        """
363        return client size tuple
364        """
365        width, height = self.GetClientSizeTuple()
366        height -= 45
367        # Adjust toolbar height
368        toolbar = self.GetToolBar()
369        if toolbar != None:
370            _, tb_h = toolbar.GetSizeTuple()
371            height -= tb_h
372        return width, height
373   
374    def on_change_categories(self, evt):
375        # ILL
376        fitpanel = None
377        for item in self.plugins:
378            if hasattr(item, "get_panels"):
379                if hasattr(item, "fit_panel"):
380                    fitpanel = item.fit_panel
381
382        if fitpanel != None:
383            for i in range(0,fitpanel.GetPageCount()):
384                fitpanel.GetPage(i)._populate_listbox()
385
386
387
388    def on_set_batch_result(self, data_outputs, data_inputs=None,
389                             plugin_name=""):
390        """
391        Display data into a grid in batch mode and show the grid
392        """
393        t = time.localtime(time.time())
394        time_str = time.strftime("%b %d %H;%M of %Y", t)
395        details = "File Generated by %s : %s" % (APPLICATION_NAME,
396                                                     str(plugin_name))
397        details += "on %s.\n" % time_str
398        ext = ".csv"
399        file_name = "Batch_" + str(plugin_name)+ "_" + time_str + ext
400        file_name = self._default_save_location + str(file_name)
401       
402        self.open_with_localapp(file_name=file_name,
403                                details=details,
404                                data_inputs=data_inputs,
405                                    data_outputs=data_outputs)
406     
407   
408    def open_with_localapp(self, data_inputs=None, details="", file_name=None,
409                           data_outputs=None):
410        """
411        Display value of data into the application grid
412        :param data: dictionary of string and list of items
413        """
414        self.batch_frame.set_data(data_inputs=data_inputs, 
415                                  data_outputs=data_outputs,
416                                  details=details,
417                                  file_name=file_name)
418        self.show_batch_frame(None)
419       
420    def on_read_batch_tofile(self, base):
421        """
422        Open a file dialog , extract the file to read and display values
423        into a grid
424        """
425        path = None
426        if self._default_save_location == None:
427            self._default_save_location = os.getcwd()
428        wildcard = "(*.csv; *.txt)|*.csv; *.txt"
429        dlg = wx.FileDialog(base, 
430                            "Choose a file", 
431                            self._default_save_location, "",
432                             wildcard)
433        if dlg.ShowModal() == wx.ID_OK:
434            path = dlg.GetPath()
435            if path is not None:
436                self._default_save_location = os.path.dirname(path)
437        dlg.Destroy()
438        try:
439            self.read_batch_tofile(file_name=path)
440        except:
441            msg = "Error occurred when reading the file; %s\n"% path
442            msg += "%s\n"% sys.exc_value
443            wx.PostEvent(self, StatusEvent(status=msg,
444                                             info="error"))
445           
446    def read_batch_tofile(self, file_name):
447        """
448        Extract value from file name and Display them into a grid
449        """
450        if file_name is None or file_name.strip() == "":
451            return
452        data = {}
453        fd = open(file_name, 'r')
454        _, ext = os.path.splitext(file_name)
455        separator = None
456        if ext.lower() == ".csv":
457            separator = ","
458        buffer = fd.read()
459        lines = buffer.split('\n')
460        fd.close()
461        column_names_line  = ""
462        index = None
463        details = ""
464        for index in range(len(lines)):
465            line = lines[index]
466            line.strip()
467            count = 0
468            if separator == None:
469                line.replace('\t', ' ')
470                #found the first line containing the label
471                col_name_toks = line.split()
472                for item in col_name_toks:
473                    if item.strip() != "":
474                        count += 1
475                    else:
476                        line = " "
477            elif line.find(separator) != -1:
478                if line.count(separator) >= 2:
479                    #found the first line containing the label
480                    col_name_toks = line.split(separator)
481                    for item in col_name_toks:
482                        if item.strip() != "":
483                            count += 1
484            else:
485                details += line
486            if count >= 2:
487                column_names_line = line
488                break 
489           
490        if column_names_line.strip() == "" or index is None:
491            return 
492
493        col_name_toks = column_names_line.split(separator)
494        c_index = 0
495        for col_index in range(len(col_name_toks)):
496            c_name = col_name_toks[col_index]
497            if c_name.strip() != "":
498                # distinguish between column name and value
499                try:
500                    float(c_name)
501                    col_name = "Column %s"% str(col_index + 1)
502                    index_min = index
503                except:
504                    col_name = c_name
505                    index_min = index + 1
506                data[col_name] = [ lines[row].split(separator)[c_index]
507                                for row in range(index_min, len(lines)-1)]
508                c_index += 1
509               
510        self.open_with_localapp(data_outputs=data, data_inputs=None,
511                                file_name=file_name, details=details)
512       
513    def write_batch_tofile(self, data, file_name, details=""):
514        """
515        Helper to write result from batch into cvs file
516        """
517        self._default_save_location = os.path.dirname(file_name)
518        name = os.path.basename(file_name)
519        if data is None or file_name is None or file_name.strip() == "":
520            return
521        _, ext = os.path.splitext(name)
522        try:
523            fd = open(file_name, 'w')
524        except:
525            # On Permission denied: IOError
526            temp_dir = get_user_directory()
527            temp_file_name = os.path.join(temp_dir, name)
528            fd = open(temp_file_name, 'w')
529        separator = "\t"
530        if ext.lower() == ".csv":
531            separator = ","
532        fd.write(str(details))
533        for col_name  in data.keys():
534            fd.write(str(col_name))
535            fd.write(separator)
536        fd.write('\n')
537        max_list = [len(value) for value in data.values()]
538        if len(max_list) == 0:
539            return
540        max_index = max(max_list)
541        index = 0
542        while(index < max_index):
543            for value_list in data.values():
544                if index < len(value_list):
545                    fd.write(str(value_list[index]))
546                    fd.write(separator)
547                else:
548                    fd.write('')
549                    fd.write(separator)
550            fd.write('\n')
551            index += 1
552        fd.close()
553           
554    def open_with_externalapp(self, data, file_name, details=""):
555        """
556        Display data in the another application , by default Excel
557        """
558        if not os.path.exists(file_name):
559            self.write_batch_tofile(data=data, file_name=file_name,
560                                               details=details)
561        try:
562            from win32com.client import Dispatch
563            excel_app = Dispatch('Excel.Application')     
564            wb = excel_app.Workbooks.Open(file_name) 
565            excel_app.Visible = 1
566        except:
567            msg = "Error occured when calling Excel.\n"
568            msg += "Check that Excel installed in this machine or \n"
569            msg += "check that %s really exists.\n" % str(file_name)
570            wx.PostEvent(self, StatusEvent(status=msg,
571                                             info="error"))
572           
573         
574    def on_batch_selection(self, event=None):
575        """
576        :param event: contains parameter enable . when enable is set to True
577        the application is in Batch mode
578        else the application is default mode(single mode)
579        """
580        if event is not None:
581            self.batch_on = event.enable
582        for plug in self.plugins:
583            plug.set_batch_selection(self.batch_on)
584           
585    def on_color_selection(self, event):
586        """
587        :param event: contains parameters for id and color
588        """ 
589        color, id = event.color, event.id
590        for plug in self.plugins:
591            plug.add_color(color, id)
592       
593       
594    def setup_custom_conf(self):
595        """
596        Set up custom configuration if exists
597        """
598        if custom_config == None:
599            return
600       
601        if not FIXED_PANEL:
602            self.__gui_style &= (~GUIFRAME.FIXED_PANEL)
603            self.__gui_style |= GUIFRAME.FLOATING_PANEL
604
605        if not DATALOADER_SHOW:
606            self.__gui_style &= (~GUIFRAME.MANAGER_ON)
607
608        if not TOOLBAR_SHOW:
609            self.__gui_style &= (~GUIFRAME.TOOLBAR_ON)
610
611        if WELCOME_PANEL_SHOW:
612            self.__gui_style |= GUIFRAME.WELCOME_PANEL_ON   
613             
614    def set_custom_default_perspective(self):
615        """
616        Set default starting perspective
617        """
618        if custom_config == None:
619            return
620        for plugin in self.plugins:
621            try:
622                if plugin.sub_menu == DEFAULT_PERSPECTIVE:
623                   
624                    plugin.on_perspective(event=None)
625                    frame = plugin.get_frame()
626                    frame.Show(True)
627                    #break
628                else:
629                    frame = plugin.get_frame()
630                    frame.Show(False) 
631            except:
632                pass 
633        return         
634               
635    def on_load_data(self, event):
636        """
637        received an event to trigger load from data plugin
638        """
639        if self._data_plugin is not None:
640            self._data_plugin.load_data(event)
641           
642    def get_current_perspective(self):
643        """
644        return the current perspective
645        """
646        return self._current_perspective
647
648    def get_save_location(self):
649        """
650        return the _default_save_location
651        """
652        return self._default_save_location
653   
654    def set_input_file(self, input_file):
655        """
656        :param input_file: file to read
657        """
658        self._input_file = input_file
659       
660    def get_data_manager(self):
661        """
662        return the data manager.
663        """
664        return self._data_manager
665   
666    def get_toolbar(self):
667        """
668        return the toolbar.
669        """
670        return self._toolbar
671   
672    def set_panel_on_focus(self, event):
673        """
674        Store reference to the last panel on focus
675        update the toolbar if available
676        update edit menu if available
677        """
678        if event != None:
679            self.panel_on_focus = event.panel
680        if self.panel_on_focus is not None:
681            #Disable save application if the current panel is in batch mode
682            flag = self.panel_on_focus.get_save_flag()
683            if self._save_appl_menu != None:
684                self._save_appl_menu.Enable(flag)
685
686            if self.panel_on_focus not in self.plot_panels.values():
687                for ID in self.panels.keys():
688                    if self.panel_on_focus != self.panels[ID]:
689                        self.panels[ID].on_kill_focus(None)
690
691            if self._data_panel is not None and \
692                            self.panel_on_focus is not None:
693                self.set_panel_on_focus_helper()
694                #update toolbar
695                self._update_toolbar_helper()
696                #update edit menu
697                self.enable_edit_menu()
698
699    def disable_app_menu(self, p_panel=None): 
700        """
701        Disables all menus in the menubar
702        """
703        return
704   
705    def send_focus_to_datapanel(self, name): 
706        """
707        Send focusing on ID to data explorer
708        """
709        if self._data_panel != None:
710            self._data_panel.set_panel_on_focus(name)
711           
712    def set_panel_on_focus_helper(self):
713        """
714        Helper for panel on focus with data_panel
715        """
716        caption = self.panel_on_focus.window_caption
717        self.send_focus_to_datapanel(caption)
718        #update combo
719        if self.panel_on_focus in self.plot_panels.values():
720            combo = self._data_panel.cb_plotpanel
721            combo_title = str(self.panel_on_focus.window_caption)
722            combo.SetStringSelection(combo_title)
723            combo.SetToolTip( wx.ToolTip(combo_title )) 
724        elif self.panel_on_focus != self._data_panel:
725            cpanel = self.panel_on_focus
726            if self.cpanel_on_focus != cpanel:
727                cpanel.on_tap_focus()
728                self.cpanel_on_focus = self.panel_on_focus
729     
730    def reset_bookmark_menu(self, panel):
731        """
732        Reset Bookmark menu list
733       
734        : param panel: a control panel or tap where the bookmark is
735        """
736        cpanel = panel
737        if self._toolbar != None and cpanel._bookmark_flag:
738            for item in  self._toolbar.get_bookmark_items():
739                self._toolbar.remove_bookmark_item(item)
740            self._toolbar.add_bookmark_default()
741            pos = 0
742            for bitem in cpanel.popUpMenu.GetMenuItems():
743                pos += 1
744                if pos < 3:
745                    continue
746                id =  bitem.GetId()
747                label = bitem.GetLabel()
748                self._toolbar.append_bookmark_item(id, label)
749                wx.EVT_MENU(self, id, cpanel._back_to_bookmark)
750            self._toolbar.Realize()
751             
752
753    def build_gui(self):
754        """
755        Build the GUI by setting up the toolbar, menu and layout.
756        """
757        # set tool bar
758        self._setup_tool_bar()
759
760        # Create the menu bar. To be filled later.
761        # WX 3.0 needs us to create the menu bar first.
762        self._menubar = wx.MenuBar()
763        if wx.VERSION_STRING >= '3.0.0.0':
764            self.SetMenuBar(self._menubar)
765        self._add_menu_file()
766        self._add_menu_edit()
767        self._add_menu_view()
768        self._add_menu_tool()
769        # Set up the layout
770        self._setup_layout()
771        self._add_menu_application()
772       
773        # Set up the menu
774        self._add_current_plugin_menu()
775        self._add_help_menu()
776        # Append item from plugin under menu file if necessary
777        self._populate_file_menu()
778
779
780        if not wx.VERSION_STRING >= '3.0.0.0':
781            self.SetMenuBar(self._menubar)
782       
783        try:
784            self.load_from_cmd(self._input_file)
785        except:
786            msg = "%s Cannot load file %s\n" %(str(APPLICATION_NAME), 
787                                             str(self._input_file))
788            msg += str(sys.exc_value) + '\n'
789            logging.error(msg)
790        if self._data_panel is not None and len(self.plugins) > 0:
791            self._data_panel.fill_cbox_analysis(self.plugins)
792        self.post_init()
793        # Set Custom default
794        self.set_custom_default_perspective()
795        # Set up extra custom tool menu
796        self._setup_extra_custom()
797        self._check_update(None)
798
799    def _setup_extra_custom(self): 
800        """
801        Set up toolbar and welcome view if needed
802        """
803        style = self.__gui_style & GUIFRAME.TOOLBAR_ON
804        if (style == GUIFRAME.TOOLBAR_ON) & (not self._toolbar.IsShown()):
805            self._on_toggle_toolbar() 
806       
807        # Set Custom deafult start page
808        welcome_style = self.__gui_style & GUIFRAME.WELCOME_PANEL_ON
809        if welcome_style == GUIFRAME.WELCOME_PANEL_ON:
810            self.show_welcome_panel(None)
811     
812    def _setup_layout(self):
813        """
814        Set up the layout
815        """
816        # Status bar
817        from sans.guiframe.gui_statusbar import StatusBar
818        self.sb = StatusBar(self, wx.ID_ANY)
819        self.SetStatusBar(self.sb)
820        # Load panels
821        self._load_panels()
822        self.set_default_perspective()
823       
824    def SetStatusText(self, *args, **kwds):
825        """
826        """
827        number = self.sb.get_msg_position()
828        wx.Frame.SetStatusText(self, number=number, *args, **kwds)
829       
830    def PopStatusText(self, *args, **kwds):
831        """
832        """
833        field = self.sb.get_msg_position()
834        wx.Frame.PopStatusText(self, field=field)
835       
836    def PushStatusText(self, *args, **kwds):
837        """
838            FIXME: No message is passed. What is this supposed to do?
839        """
840        field = self.sb.get_msg_position()
841        wx.Frame.PushStatusText(self, field=field, 
842                        string="FIXME: PushStatusText called without text")
843
844    def add_perspective(self, plugin):
845        """
846        Add a perspective if it doesn't already
847        exist.
848        """
849        self._num_perspectives += 1
850        is_loaded = False
851        for item in self.plugins:
852            item.set_batch_selection(self.batch_on)
853            if plugin.__class__ == item.__class__:
854                msg = "Plugin %s already loaded" % plugin.sub_menu
855                logging.info(msg)
856                is_loaded = True 
857        if not is_loaded:
858            self.plugins.append(plugin) 
859            msg = "Plugin %s appended" % plugin.sub_menu
860            logging.info(msg)
861     
862    def _get_local_plugins(self):
863        """
864        get plugins local to guiframe and others
865        """
866        plugins = []
867        #import guiframe local plugins
868        #check if the style contain guiframe.dataloader
869        style1 = self.__gui_style & GUIFRAME.DATALOADER_ON
870        style2 = self.__gui_style & GUIFRAME.PLOTTING_ON
871        if style1 == GUIFRAME.DATALOADER_ON:
872            try:
873                from sans.guiframe.local_perspectives.data_loader import data_loader
874                self._data_plugin = data_loader.Plugin()
875                plugins.append(self._data_plugin)
876            except:
877                msg = "ViewerFrame._get_local_plugins:"
878                msg += "cannot import dataloader plugin.\n %s" % sys.exc_value
879                logging.error(msg)
880        if style2 == GUIFRAME.PLOTTING_ON:
881            try:
882                from sans.guiframe.local_perspectives.plotting import plotting
883                self._plotting_plugin = plotting.Plugin()
884                plugins.append(self._plotting_plugin)
885            except:
886                msg = "ViewerFrame._get_local_plugins:"
887                msg += "cannot import plotting plugin.\n %s" % sys.exc_value
888                logging.error(msg)
889     
890        return plugins
891   
892    def _find_plugins(self, dir="perspectives"):
893        """
894        Find available perspective plug-ins
895       
896        :param dir: directory in which to look for plug-ins
897       
898        :return: list of plug-ins
899       
900        """
901        plugins = []
902        # Go through files in panels directory
903        try:
904            list = os.listdir(dir)
905            ## the default panel is the panel is the last plugin added
906            for item in list:
907                toks = os.path.splitext(os.path.basename(item))
908                name = ''
909                if not toks[0] == '__init__':
910                    if toks[1] == '.py' or toks[1] == '':
911                        name = toks[0]
912                    #check the validity of the module name parsed
913                    #before trying to import it
914                    if name is None or name.strip() == '':
915                        continue
916                    path = [os.path.abspath(dir)]
917                    file = None
918                    try:
919                        if toks[1] == '':
920                            mod_path = '.'.join([dir, name])
921                            module = __import__(mod_path, globals(),
922                                                locals(), [name])
923                        else:
924                            (file, path, info) = imp.find_module(name, path)
925                            module = imp.load_module( name, file, item, info)
926                        if hasattr(module, "PLUGIN_ID"):
927                            try: 
928                                plug = module.Plugin()
929                                if plug.set_default_perspective():
930                                    self._current_perspective = plug
931                                plugins.append(plug)
932                               
933                                msg = "Found plug-in: %s" % module.PLUGIN_ID
934                                logging.info(msg)
935                            except:
936                                msg = "Error accessing PluginPanel"
937                                msg += " in %s\n  %s" % (name, sys.exc_value)
938                                config.printEVT(msg)
939                    except:
940                        msg = "ViewerFrame._find_plugins: %s" % sys.exc_value
941                        logging.error(msg)
942                    finally:
943                        if not file == None:
944                            file.close()
945        except:
946            # Should raise and catch at a higher level and
947            # display error on status bar
948            pass 
949
950        return plugins
951
952    def _get_panels_size(self, p):
953        """
954        find the proper size of the current panel
955        get the proper panel width and height
956        """
957        self._window_width, self._window_height = self.get_client_size()
958        ## Default size
959        if DATAPANEL_WIDTH < 0:
960            panel_width = int(self._window_width * 0.25)
961        else:
962            panel_width = DATAPANEL_WIDTH
963        panel_height = int(self._window_height)
964        style = self.__gui_style & (GUIFRAME.MANAGER_ON)
965        if self._data_panel is not None  and (p == self._data_panel):
966            return panel_width, panel_height
967        if hasattr(p, "CENTER_PANE") and p.CENTER_PANE:
968            panel_width = self._window_width * 0.45
969            if CONTROL_WIDTH > 0:
970                panel_width = CONTROL_WIDTH
971            if CONTROL_HEIGHT > 0:
972                panel_height = CONTROL_HEIGHT
973            return panel_width, panel_height
974        elif p == self.defaultPanel:
975            return self._window_width, panel_height
976        return panel_width, panel_height
977   
978    def _load_panels(self):
979        """
980        Load all panels in the panels directory
981        """
982        # Look for plug-in panels
983        panels = []
984        if wx.VERSION_STRING >= '3.0.0.0':
985            mac_pos_y = 85
986        else:
987            mac_pos_y = 40
988        for item in self.plugins:
989            if hasattr(item, "get_panels"):
990                ps = item.get_panels(self)
991                panels.extend(ps)
992       
993        # Set up welcome panel
994        #TODO: this needs serious simplification
995        if self.welcome_panel_class is not None:
996            welcome_panel = MDIFrame(self, None, 'None', (100, 200))
997            self.defaultPanel = self.welcome_panel_class(welcome_panel, -1, style=wx.RAISED_BORDER)
998            welcome_panel.set_panel(self.defaultPanel)
999            self.defaultPanel.set_frame(welcome_panel)
1000            welcome_panel.Show(False)
1001       
1002        self.panels["default"] = self.defaultPanel
1003        size_t_bar = 70
1004        if IS_LINUX:
1005            size_t_bar = 115
1006        if self.defaultPanel is not None:
1007            w, h = self._get_panels_size(self.defaultPanel)
1008            frame = self.defaultPanel.get_frame()
1009            frame.SetSize((self._window_width, self._window_height))
1010            if not IS_WIN:
1011                frame.SetPosition((0, mac_pos_y + size_t_bar))
1012            frame.Show(True)
1013        #add data panel
1014        win = MDIFrame(self, None, 'None', (100, 200))
1015        data_panel = DataPanel(parent=win,  id=-1)
1016        win.set_panel(data_panel)
1017        self.panels["data_panel"] = data_panel
1018        self._data_panel = data_panel
1019        d_panel_width, h = self._get_panels_size(self._data_panel)
1020        win.SetSize((d_panel_width, h))
1021        is_visible = self.__gui_style & GUIFRAME.MANAGER_ON == GUIFRAME.MANAGER_ON
1022        if IS_WIN:
1023            win.SetPosition((0, 0))
1024        else:
1025            win.SetPosition((0, mac_pos_y + size_t_bar))
1026        win.Show(is_visible)
1027        # Add the panels to the AUI manager
1028        for panel_class in panels:
1029            frame = panel_class.get_frame()
1030            id = wx.NewId()
1031            # Check whether we need to put this panel in the center pane
1032            if hasattr(panel_class, "CENTER_PANE") and panel_class.CENTER_PANE:
1033                w, h = self._get_panels_size(panel_class)
1034                if panel_class.CENTER_PANE:
1035                    self.panels[str(id)] = panel_class
1036                    _, pos_y = frame.GetPositionTuple()
1037                    frame.SetPosition((d_panel_width + 1, pos_y))
1038                    frame.SetSize((w, h))
1039                    frame.Show(False)
1040            elif panel_class == self._data_panel:
1041                panel_class.frame.Show(is_visible)
1042                continue
1043            else:
1044                self.panels[str(id)] = panel_class
1045                frame.SetSize((w, h))
1046                frame.Show(False)
1047            if IS_WIN:
1048                frame.SetPosition((d_panel_width + 1, 0))
1049            else:
1050                frame.SetPosition((d_panel_width + 1, mac_pos_y + size_t_bar))
1051
1052        if not IS_WIN:
1053            win_height = mac_pos_y
1054            if IS_LINUX:
1055                if wx.VERSION_STRING >= '3.0.0.0':
1056                    win_height = mac_pos_y + 10
1057                else:
1058                    win_height = mac_pos_y + 55
1059                self.SetMaxSize((-1, win_height))
1060            else:
1061                self.SetSize((self._window_width, win_height))
1062       
1063    def update_data(self, prev_data, new_data):
1064        """
1065        Update the data.
1066        """
1067        prev_id, data_state = self._data_manager.update_data(
1068                              prev_data=prev_data, new_data=new_data)
1069       
1070        self._data_panel.remove_by_id(prev_id)
1071        self._data_panel.load_data_list(data_state)
1072       
1073    def update_theory(self, data_id, theory, state=None):
1074        """
1075        Update the theory
1076        """ 
1077        data_state = self._data_manager.update_theory(data_id=data_id, 
1078                                         theory=theory,
1079                                         state=state) 
1080        wx.CallAfter(self._data_panel.load_data_list, data_state)
1081       
1082    def onfreeze(self, theory_id):
1083        """
1084        """
1085        data_state_list = self._data_manager.freeze(theory_id)
1086        self._data_panel.load_data_list(list=data_state_list)
1087        for data_state in data_state_list.values():
1088            new_plot = data_state.get_data()
1089           
1090            wx.PostEvent(self, NewPlotEvent(plot=new_plot,
1091                                             title=new_plot.title))
1092       
1093    def freeze(self, data_id, theory_id):
1094        """
1095        """
1096        data_state_list = self._data_manager.freeze_theory(data_id=data_id, 
1097                                                theory_id=theory_id)
1098        self._data_panel.load_data_list(list=data_state_list)
1099        for data_state in data_state_list.values():
1100            new_plot = data_state.get_data()
1101            wx.PostEvent(self, NewPlotEvent(plot=new_plot,
1102                                             title=new_plot.title))
1103       
1104    def delete_data(self, data):
1105        """
1106        Delete the data.
1107        """
1108        self._current_perspective.delete_data(data)
1109       
1110   
1111    def get_context_menu(self, plotpanel=None):
1112        """
1113        Get the context menu items made available
1114        by the different plug-ins.
1115        This function is used by the plotting module
1116        """
1117        if plotpanel is None:
1118            return
1119        menu_list = []
1120        for item in self.plugins:
1121            menu_list.extend(item.get_context_menu(plotpanel=plotpanel))
1122        return menu_list
1123       
1124    def get_current_context_menu(self, plotpanel=None):
1125        """
1126        Get the context menu items made available
1127        by the current plug-in.
1128        This function is used by the plotting module
1129        """
1130        if plotpanel is None:
1131            return
1132        menu_list = []
1133        item = self._current_perspective
1134        if item != None:
1135            menu_list.extend(item.get_context_menu(plotpanel=plotpanel))
1136        return menu_list
1137           
1138    def on_panel_close(self, event):
1139        """
1140        Gets called when the close event for a panel runs.
1141        This will check which panel has been closed and
1142        delete it.
1143        """
1144        frame = event.GetEventObject()
1145        for ID in self.plot_panels.keys():
1146            if self.plot_panels[ID].window_name == frame.name:
1147                self.disable_app_menu(self.plot_panels[ID])
1148                self.delete_panel(ID)
1149                break
1150        self.cpanel_on_focus.SetFocus()
1151   
1152   
1153    def popup_panel(self, p):
1154        """
1155        Add a panel object to the AUI manager
1156       
1157        :param p: panel object to add to the AUI manager
1158       
1159        :return: ID of the event associated with the new panel [int]
1160       
1161        """
1162        ID = wx.NewId()
1163        self.panels[str(ID)] = p
1164        ## Check and set the size
1165        if PLOPANEL_WIDTH < 0:
1166            p_panel_width = int(self._window_width * 0.45)
1167        else:
1168            p_panel_width = PLOPANEL_WIDTH
1169        p_panel_height = int(p_panel_width * 0.76)
1170        p.frame.SetSize((p_panel_width, p_panel_height))
1171        self.graph_num += 1
1172        if p.window_caption.split()[0] in NOT_SO_GRAPH_LIST:
1173            windowcaption = p.window_caption
1174        else:
1175            windowcaption = 'Graph'
1176        windowname = p.window_name
1177
1178        # Append nummber
1179        captions = self._get_plotpanel_captions()
1180        while (1):
1181            caption = windowcaption + '%s'% str(self.graph_num)
1182            if caption not in captions:
1183                break
1184            self.graph_num += 1
1185            # protection from forever-loop: max num = 1000
1186            if self.graph_num > 1000:
1187                break
1188        if p.window_caption.split()[0] not in NOT_SO_GRAPH_LIST:
1189            p.window_caption = caption
1190        p.window_name = windowname + str(self.graph_num)
1191       
1192        p.frame.SetTitle(p.window_caption)
1193        p.frame.name = p.window_name
1194        if not IS_WIN:
1195            p.frame.Center()
1196            x_pos, _ = p.frame.GetPositionTuple()
1197            p.frame.SetPosition((x_pos, 112))
1198        p.frame.Show(True)
1199
1200        # Register for showing/hiding the panel
1201        wx.EVT_MENU(self, ID, self.on_view)
1202        if p not in self.plot_panels.values() and p.group_id != None:
1203            self.plot_panels[ID] = p
1204            if len(self.plot_panels) == 1:
1205                self.panel_on_focus = p
1206                self.set_panel_on_focus(None)
1207            if self._data_panel is not None and \
1208                self._plotting_plugin is not None:
1209                ind = self._data_panel.cb_plotpanel.FindString('None')
1210                if ind != wx.NOT_FOUND:
1211                    self._data_panel.cb_plotpanel.Delete(ind)
1212                if caption not in self._data_panel.cb_plotpanel.GetItems():
1213                    self._data_panel.cb_plotpanel.Append(str(caption), p)
1214        return ID
1215   
1216    def _get_plotpanel_captions(self):
1217        """
1218        Get all the plotpanel cations
1219       
1220        : return: list of captions
1221        """
1222        captions = []
1223        for Id in self.plot_panels.keys():
1224            captions.append(self.plot_panels[Id].window_caption)
1225       
1226        return captions
1227       
1228    def _setup_tool_bar(self):
1229        """
1230        add toolbar to the frame
1231        """
1232        self._toolbar = GUIToolBar(self)
1233        # The legacy code doesn't work well for wx 3.0
1234        # but the old code produces better results with wx 2.8
1235        if not IS_WIN and wx.VERSION_STRING >= '3.0.0.0':
1236            sizer = wx.BoxSizer(wx.VERTICAL)
1237            sizer.Add(self._toolbar, 0, wx.EXPAND)
1238            self.SetSizer(sizer)
1239        else:
1240            self.SetToolBar(self._toolbar)
1241        self._update_toolbar_helper()
1242        self._on_toggle_toolbar(event=None)
1243   
1244    def _update_toolbar_helper(self):
1245        """
1246        Helping to update the toolbar
1247        """
1248        application_name = 'No Selected Analysis'
1249        panel_name = 'No Panel on Focus'
1250        c_panel = self.cpanel_on_focus       
1251        if self._toolbar is  None:
1252            return
1253        if c_panel is not None:
1254            self.reset_bookmark_menu(self.cpanel_on_focus)
1255        if self._current_perspective is not None:
1256            application_name = self._current_perspective.sub_menu
1257        c_panel_state = c_panel
1258        if c_panel is not None:
1259            panel_name = c_panel.window_caption
1260            if not c_panel.IsShownOnScreen():
1261                c_panel_state = None
1262        self._toolbar.update_toolbar(c_panel_state)
1263        self._toolbar.update_button(application_name=application_name, 
1264                                        panel_name=panel_name)
1265        self._toolbar.Realize()
1266       
1267    def _add_menu_tool(self):
1268        """
1269        Tools menu
1270        Go through plug-ins and find tools to populate the tools menu
1271        """
1272        style = self.__gui_style & GUIFRAME.CALCULATOR_ON
1273        if style == GUIFRAME.CALCULATOR_ON:
1274            self._tool_menu = None
1275            for item in self.plugins:
1276                if hasattr(item, "get_tools"):
1277                    for tool in item.get_tools():
1278                        # Only create a menu if we have at least one tool
1279                        if self._tool_menu is None:
1280                            self._tool_menu = wx.Menu()
1281                        if tool[0].lower().count('python') > 0:
1282                            self._tool_menu.AppendSeparator()
1283                        id = wx.NewId()
1284                        self._tool_menu.Append(id, tool[0], tool[1])
1285                        wx.EVT_MENU(self, id, tool[2])
1286            if self._tool_menu is not None:
1287                self._menubar.Append(self._tool_menu, '&Tool')
1288               
1289    def _add_current_plugin_menu(self):
1290        """
1291        add current plugin menu
1292        Look for plug-in menus
1293        Add available plug-in sub-menus.
1294        """
1295        if self._menubar is None or self._current_perspective is None \
1296            or self._menubar.GetMenuCount()==0:
1297            return
1298        #replace or add a new menu for the current plugin
1299       
1300        pos = self._menubar.FindMenu(str(self._applications_menu_name))
1301        if pos != -1:
1302            menu_list = self._current_perspective.populate_menu(self)
1303            if menu_list:
1304                for (menu, name) in menu_list:
1305                    hidden_menu = self._menubar.Replace(pos, menu, name) 
1306                    self._applications_menu_name = name
1307                #self._applications_menu_pos = pos
1308            else:
1309                hidden_menu = self._menubar.Remove(pos)
1310                self._applications_menu_name = None
1311            #get the position of the menu when it first added
1312            self._applications_menu_pos = pos
1313           
1314        else:
1315            menu_list = self._current_perspective.populate_menu(self)
1316            if menu_list:
1317                for (menu, name) in menu_list:
1318                    if self._applications_menu_pos == -1:
1319                        self._menubar.Append(menu, name)
1320                    else:
1321                        self._menubar.Insert(self._applications_menu_pos, 
1322                                             menu, name)
1323                    self._applications_menu_name = name
1324                 
1325    def _add_help_menu(self):
1326        """
1327        add help menu
1328        """
1329        # Help menu
1330        self._help_menu = wx.Menu()
1331        style = self.__gui_style & GUIFRAME.WELCOME_PANEL_ON
1332        if style == GUIFRAME.WELCOME_PANEL_ON or custom_config != None:
1333            # add the welcome panel menu item
1334            if config.WELCOME_PANEL_ON and self.defaultPanel is not None:
1335                id = wx.NewId()
1336                self._help_menu.Append(id, '&Welcome', '')
1337                self._help_menu.AppendSeparator()
1338                wx.EVT_MENU(self, id, self.show_welcome_panel)
1339        # Look for help item in plug-ins
1340        for item in self.plugins:
1341            if hasattr(item, "help"):
1342                id = wx.NewId()
1343                self._help_menu.Append(id,'&%s Help' % item.sub_menu, '')
1344                wx.EVT_MENU(self, id, item.help)
1345        if config._do_tutorial and (IS_WIN or sys.platform =='darwin'):
1346            self._help_menu.AppendSeparator()
1347            id = wx.NewId()
1348            self._help_menu.Append(id, '&Tutorial', 'Software tutorial')
1349            wx.EVT_MENU(self, id, self._onTutorial)
1350           
1351        if config._do_aboutbox:
1352            self._help_menu.AppendSeparator()
1353            self._help_menu.Append(wx.ID_ABOUT, '&About', 'Software information')
1354            wx.EVT_MENU(self, wx.ID_ABOUT, self._onAbout)
1355       
1356        # Checking for updates
1357        id = wx.NewId()
1358        self._help_menu.Append(id,'&Check for update', 
1359         'Check for the latest version of %s' % config.__appname__)
1360        wx.EVT_MENU(self, id, self._check_update)
1361        self._menubar.Append(self._help_menu, '&Help')
1362           
1363    def _add_menu_view(self):
1364        """
1365        add menu items under view menu
1366        """
1367        if not VIEW_MENU:
1368            return
1369        self._view_menu = wx.Menu()
1370       
1371        id = wx.NewId()
1372        hint = "Display the Grid Window for batch results etc."
1373        self._view_menu.Append(id, '&Show Grid Window', hint) 
1374        wx.EVT_MENU(self, id, self.show_batch_frame)
1375       
1376        self._view_menu.AppendSeparator()
1377        style = self.__gui_style & GUIFRAME.MANAGER_ON
1378        id = wx.NewId()
1379        self._data_panel_menu = self._view_menu.Append(id,
1380                                                '&Show Data Explorer', '')
1381        wx.EVT_MENU(self, id, self.show_data_panel)
1382        if style == GUIFRAME.MANAGER_ON:
1383            self._data_panel_menu.SetText('Hide Data Explorer')
1384        else:
1385            self._data_panel_menu.SetText('Show Data Explorer')
1386 
1387        self._view_menu.AppendSeparator()
1388        id = wx.NewId()
1389        style1 = self.__gui_style & GUIFRAME.TOOLBAR_ON
1390        if style1 == GUIFRAME.TOOLBAR_ON:
1391            self._toolbar_menu = self._view_menu.Append(id, '&Hide Toolbar', '')
1392        else:
1393            self._toolbar_menu = self._view_menu.Append(id, '&Show Toolbar', '')
1394        wx.EVT_MENU(self, id, self._on_toggle_toolbar)
1395
1396        if custom_config != None:
1397            self._view_menu.AppendSeparator()
1398            id = wx.NewId()
1399            hint_ss = "Select the current/default configuration "
1400            hint_ss += "as a startup setting"
1401            preference_menu = self._view_menu.Append(id, 'Startup Setting', 
1402                                                     hint_ss)
1403            wx.EVT_MENU(self, id, self._on_preference_menu)
1404           
1405        id = wx.NewId()
1406        self._view_menu.AppendSeparator()
1407        self._view_menu.Append(id, 'Category Manager', 'Edit model categories')
1408        wx.EVT_MENU(self, id, self._on_category_manager)
1409
1410        self._menubar.Append(self._view_menu, '&View')   
1411         
1412    def show_batch_frame(self, event=None):
1413        """
1414        show the grid of result
1415        """
1416        # Show(False) before Show(True) in order to bring it to the front
1417        self.batch_frame.Show(False)
1418        self.batch_frame.Show(True)
1419   
1420    def  on_category_panel(self, event): 
1421        """
1422        On cat panel
1423        """
1424        self._on_category_manager(event)
1425         
1426    def _on_category_manager(self, event):
1427        """
1428        Category manager frame
1429        """
1430        frame = CategoryManager(self, -1, 'Model Category Manager')
1431        icon = self.GetIcon()
1432        frame.SetIcon(icon)
1433
1434    def _on_preference_menu(self, event):     
1435        """
1436        Build a panel to allow to edit Mask
1437        """
1438        from sans.guiframe.startup_configuration \
1439        import StartupConfiguration as ConfDialog
1440       
1441        dialog = ConfDialog(parent=self, gui=self.__gui_style)
1442        result = dialog.ShowModal()
1443        if result == wx.ID_OK:
1444            dialog.write_custom_config()
1445            # post event for info
1446            wx.PostEvent(self, StatusEvent(status="Wrote custom configuration", info='info'))
1447        dialog.Destroy()
1448       
1449    def _add_menu_application(self):
1450        """
1451        # Attach a menu item for each defined perspective or application.
1452        # Only add the perspective menu if there are more than one perspectives
1453        add menu application
1454        """
1455        if self._num_perspectives  > 1:
1456            plug_data_count = False
1457            plug_no_data_count = False
1458            self._applications_menu = wx.Menu()
1459            pos = 0
1460            separator = self._applications_menu.AppendSeparator()
1461            for plug in self.plugins:
1462                if len(plug.get_perspective()) > 0:
1463                    id = wx.NewId()
1464                    if plug.use_data():
1465                       
1466                        self._applications_menu.InsertCheckItem(pos, id, plug.sub_menu,
1467                                      "Switch to analysis: %s" % plug.sub_menu)
1468                        plug_data_count = True
1469                        pos += 1
1470                    else:
1471                        plug_no_data_count = True
1472                        self._applications_menu.AppendCheckItem(id, plug.sub_menu,
1473                                      "Switch to analysis: %s" % plug.sub_menu)
1474                    wx.EVT_MENU(self, id, plug.on_perspective)
1475
1476            if (not plug_data_count or not plug_no_data_count):
1477                self._applications_menu.RemoveItem(separator)
1478            self._menubar.Append(self._applications_menu, '&Analysis')
1479            self._check_applications_menu()
1480           
1481    def _populate_file_menu(self):
1482        """
1483        Insert menu item under file menu
1484        """
1485        for plugin in self.plugins:
1486            if len(plugin.populate_file_menu()) > 0:
1487                for item in plugin.populate_file_menu():
1488                    m_name, m_hint, m_handler = item
1489                    id = wx.NewId()
1490                    self._file_menu.Append(id, m_name, m_hint)
1491                    wx.EVT_MENU(self, id, m_handler)
1492                self._file_menu.AppendSeparator()
1493               
1494        style1 = self.__gui_style & GUIFRAME.MULTIPLE_APPLICATIONS
1495        if OPEN_SAVE_MENU:
1496            id = wx.NewId()
1497            hint_load_file = "read all analysis states saved previously"
1498            self._save_appl_menu = self._file_menu.Append(id, 
1499                                    '&Open Project', hint_load_file)
1500            wx.EVT_MENU(self, id, self._on_open_state_project)
1501           
1502        if style1 == GUIFRAME.MULTIPLE_APPLICATIONS:
1503            # some menu of plugin to be seen under file menu
1504            hint_load_file = "Read a status files and load"
1505            hint_load_file += " them into the analysis"
1506            id = wx.NewId()
1507            self._save_appl_menu = self._file_menu.Append(id, 
1508                                    '&Open Analysis', hint_load_file)
1509            wx.EVT_MENU(self, id, self._on_open_state_application)
1510        if OPEN_SAVE_MENU:       
1511            self._file_menu.AppendSeparator()
1512            id = wx.NewId()
1513            self._file_menu.Append(id, '&Save Project',
1514                                 'Save the state of the whole analysis')
1515            wx.EVT_MENU(self, id, self._on_save_project)
1516        if style1 == GUIFRAME.MULTIPLE_APPLICATIONS:
1517            id = wx.NewId()
1518            self._save_appl_menu = self._file_menu.Append(id, 
1519                                                      '&Save Analysis',
1520                        'Save state of the current active analysis panel')
1521            wx.EVT_MENU(self, id, self._on_save_application)
1522        if not sys.platform =='darwin':
1523            self._file_menu.AppendSeparator()
1524            id = wx.NewId()
1525            self._file_menu.Append(id, '&Quit', 'Exit') 
1526            wx.EVT_MENU(self, id, self.Close)
1527       
1528    def _add_menu_file(self):
1529        """
1530        add menu file
1531        """
1532        # File menu
1533        self._file_menu = wx.Menu()
1534        # Add sub menus
1535        self._menubar.Append(self._file_menu, '&File')
1536       
1537    def _add_menu_edit(self):
1538        """
1539        add menu edit
1540        """
1541        if not EDIT_MENU:
1542            return
1543        # Edit Menu
1544        self._edit_menu = wx.Menu()
1545        self._edit_menu.Append(GUIFRAME_ID.UNDO_ID, '&Undo', 
1546                               'Undo the previous action')
1547        wx.EVT_MENU(self, GUIFRAME_ID.UNDO_ID, self.on_undo_panel)
1548        self._edit_menu.Append(GUIFRAME_ID.REDO_ID, '&Redo', 
1549                               'Redo the previous action')
1550        wx.EVT_MENU(self, GUIFRAME_ID.REDO_ID, self.on_redo_panel)
1551        self._edit_menu.AppendSeparator()
1552        self._edit_menu.Append(GUIFRAME_ID.COPY_ID, '&Copy Params', 
1553                               'Copy parameter values')
1554        wx.EVT_MENU(self, GUIFRAME_ID.COPY_ID, self.on_copy_panel)
1555        self._edit_menu.Append(GUIFRAME_ID.PASTE_ID, '&Paste Params', 
1556                               'Paste parameter values')
1557        wx.EVT_MENU(self, GUIFRAME_ID.PASTE_ID, self.on_paste_panel)
1558
1559        self._edit_menu.AppendSeparator()
1560
1561        self._edit_menu_copyas = wx.Menu()
1562        #Sub menu for Copy As...
1563        self._edit_menu_copyas.Append(GUIFRAME_ID.COPYEX_ID, 'Copy current tab to Excel',
1564                               'Copy parameter values in tabular format')
1565        wx.EVT_MENU(self, GUIFRAME_ID.COPYEX_ID, self.on_copy_panel)
1566
1567        self._edit_menu_copyas.Append(GUIFRAME_ID.COPYLAT_ID, 'Copy current tab to LaTeX',
1568                               'Copy parameter values in tabular format')
1569        wx.EVT_MENU(self, GUIFRAME_ID.COPYLAT_ID, self.on_copy_panel)
1570
1571
1572        self._edit_menu.AppendMenu(GUIFRAME_ID.COPYAS_ID, 'Copy Params as...', self._edit_menu_copyas,
1573                               'Copy parameter values in various formats')
1574
1575
1576        self._edit_menu.AppendSeparator()
1577       
1578        self._edit_menu.Append(GUIFRAME_ID.PREVIEW_ID, '&Report Results',
1579                               'Preview current panel')
1580        wx.EVT_MENU(self, GUIFRAME_ID.PREVIEW_ID, self.on_preview_panel)
1581
1582        self._edit_menu.Append(GUIFRAME_ID.RESET_ID, '&Reset Page', 
1583                               'Reset current panel')
1584        wx.EVT_MENU(self, GUIFRAME_ID.RESET_ID, self.on_reset_panel)
1585   
1586        self._menubar.Append(self._edit_menu,  '&Edit')
1587        self.enable_edit_menu()
1588       
1589    def get_style(self):
1590        """
1591        Return the gui style
1592        """
1593        return  self.__gui_style
1594   
1595    def _add_menu_data(self):
1596        """
1597        Add menu item item data to menu bar
1598        """
1599        if self._data_plugin is not None:
1600            menu_list = self._data_plugin.populate_menu(self)
1601            if menu_list:
1602                for (menu, name) in menu_list:
1603                    self._menubar.Append(menu, name)
1604       
1605                       
1606    def _on_toggle_toolbar(self, event=None):
1607        """
1608        hide or show toolbar
1609        """
1610        if self._toolbar is None:
1611            return
1612        if self._toolbar.IsShown():
1613            if self._toolbar_menu is not None:
1614                self._toolbar_menu.SetItemLabel('Show Toolbar')
1615            self._toolbar.Hide()
1616        else:
1617            if self._toolbar_menu is not None:
1618                self._toolbar_menu.SetItemLabel('Hide Toolbar')
1619            self._toolbar.Show()
1620        self._toolbar.Realize()
1621       
1622    def _on_status_event(self, evt):
1623        """
1624        Display status message
1625        """
1626        # This CallAfter fixes many crashes on MAC.
1627        wx.CallAfter(self.sb.set_status, evt)
1628       
1629    def on_view(self, evt):
1630        """
1631        A panel was selected to be shown. If it's not already
1632        shown, display it.
1633       
1634        :param evt: menu event
1635       
1636        """
1637        panel_id = str(evt.GetId())
1638        self.on_set_plot_focus(self.panels[panel_id])
1639        wx.CallLater(5*TIME_FACTOR, self.set_schedule(True))
1640        self.set_plot_unfocus()
1641 
1642    def show_welcome_panel(self, event):
1643        """   
1644        Display the welcome panel
1645        """
1646        if self.defaultPanel is None:
1647            return 
1648        frame = self.panels['default'].get_frame()
1649        if frame == None:
1650            return
1651        # Show default panel
1652        if not frame.IsShown():
1653            frame.Show(True)
1654           
1655    def on_close_welcome_panel(self):
1656        """
1657        Close the welcome panel
1658        """
1659        if self.defaultPanel is None:
1660            return 
1661        default_panel = self.panels["default"].frame
1662        if default_panel.IsShown():
1663            default_panel.Show(False) 
1664               
1665    def delete_panel(self, uid):
1666        """
1667        delete panel given uid
1668        """
1669        ID = str(uid)
1670        config.printEVT("delete_panel: %s" % ID)
1671        try:
1672            caption = self.panels[ID].window_caption
1673        except:
1674            logging.error("delete_panel: No such plot id as %s" % ID)
1675            return
1676        if ID in self.panels.keys():
1677            self.panel_on_focus = None
1678            panel = self.panels[ID]
1679
1680            if hasattr(panel, "connect"):
1681                panel.connect.disconnect()
1682            self._plotting_plugin.delete_panel(panel.group_id)
1683
1684            if panel in self.schedule_full_draw_list:
1685                self.schedule_full_draw_list.remove(panel) 
1686           
1687            #delete uid number not str(uid)
1688            if ID in self.plot_panels.keys():
1689                del self.plot_panels[ID]
1690            if ID in self.panels.keys():
1691                del self.panels[ID]
1692            return 
1693           
1694    def create_gui_data(self, data, path=None):
1695        """
1696        """
1697        return self._data_manager.create_gui_data(data, path)
1698   
1699    def get_data(self, path):
1700        """
1701        """
1702        message = ""
1703        log_msg = ''
1704        output = []
1705        error_message = ""
1706        basename  = os.path.basename(path)
1707        root, extension = os.path.splitext(basename)
1708        if extension.lower() not in EXTENSIONS:
1709            log_msg = "File Loader cannot "
1710            log_msg += "load: %s\n" % str(basename)
1711            log_msg += "Try Data opening...."
1712            logging.error(log_msg)
1713            return
1714       
1715        #reading a state file
1716        for plug in self.plugins:
1717            reader, ext = plug.get_extensions()
1718            if reader is not None:
1719                #read the state of the single plugin
1720                if extension == ext:
1721                    reader.read(path)
1722                    return
1723                elif extension == APPLICATION_STATE_EXTENSION:
1724                    try:
1725                        reader.read(path)
1726                    except:
1727                        msg = "DataLoader Error: Encounted Non-ASCII character"
1728                        msg += "\n(%s)"% sys.exc_value
1729                        wx.PostEvent(self, StatusEvent(status=msg, 
1730                                                info="error", type="stop"))
1731                        return
1732       
1733        style = self.__gui_style & GUIFRAME.MANAGER_ON
1734        if style == GUIFRAME.MANAGER_ON:
1735            if self._data_panel is not None:
1736                self._data_panel.frame.Show(True)
1737     
1738    def load_from_cmd(self,  path):   
1739        """
1740        load data from cmd or application
1741        """ 
1742        if path is None:
1743            return
1744        else:
1745            path = os.path.abspath(path)
1746            if not os.path.isfile(path) and not os.path.isdir(path):
1747                return
1748           
1749            if os.path.isdir(path):
1750                self.load_folder(path)
1751                return
1752
1753        basename  = os.path.basename(path)
1754        root, extension = os.path.splitext(basename)
1755        if extension.lower() not in EXTENSIONS:
1756            self.load_data(path)
1757        else:
1758            self.load_state(path)
1759
1760        self._default_save_location = os.path.dirname(path)
1761       
1762    def load_state(self, path, is_project=False):   
1763        """
1764        load data from command line or application
1765        """
1766        if path and (path is not None) and os.path.isfile(path):
1767            basename  = os.path.basename(path)
1768            if APPLICATION_STATE_EXTENSION is not None \
1769                and basename.endswith(APPLICATION_STATE_EXTENSION):
1770                if is_project:
1771                    for ID in self.plot_panels.keys():
1772                        panel = self.plot_panels[ID]
1773                        panel.on_close(None)
1774            self.get_data(path)
1775            wx.PostEvent(self, StatusEvent(status="Completed loading."))
1776        else:
1777            wx.PostEvent(self, StatusEvent(status=" "))
1778           
1779    def load_data(self, path):
1780        """
1781        load data from command line
1782        """
1783        if not os.path.isfile(path):
1784            return
1785        basename  = os.path.basename(path)
1786        root, extension = os.path.splitext(basename)
1787        if extension.lower() in EXTENSIONS:
1788            log_msg = "Data Loader cannot "
1789            log_msg += "load: %s\n" % str(path)
1790            log_msg += "Try File opening ...."
1791            logging.error(log_msg)
1792            return
1793        log_msg = ''
1794        output = {}
1795        error_message = ""
1796        try:
1797            logging.info("Loading Data...:\n" + str(path) + "\n")
1798            temp =  self.loader.load(path)
1799            if temp.__class__.__name__ == "list":
1800                for item in temp:
1801                    data = self.create_gui_data(item, path)
1802                    output[data.id] = data
1803            else:
1804                data = self.create_gui_data(temp, path)
1805                output[data.id] = data
1806           
1807            self.add_data(data_list=output)
1808        except:
1809            error_message = "Error while loading"
1810            error_message += " Data from cmd:\n %s\n" % str(path)
1811            error_message += str(sys.exc_value) + "\n"
1812            logging.error(error_message)
1813 
1814    def load_folder(self, path):
1815        """
1816        Load entire folder
1817        """   
1818        if not os.path.isdir(path):
1819            return
1820        if self._data_plugin is None:
1821            return
1822        try:
1823            if path is not None:
1824                self._default_save_location = os.path.dirname(path)
1825                file_list = self._data_plugin.get_file_path(path)
1826                self._data_plugin.get_data(file_list)
1827            else:
1828                return 
1829        except:
1830            error_message = "Error while loading"
1831            error_message += " Data folder from cmd:\n %s\n" % str(path)
1832            error_message += str(sys.exc_value) + "\n"
1833            logging.error(error_message)
1834           
1835    def _on_open_state_application(self, event):
1836        """
1837        """
1838        path = None
1839        if self._default_save_location == None:
1840            self._default_save_location = os.getcwd()
1841        wx.PostEvent(self, StatusEvent(status="Loading Analysis file..."))
1842        plug_wlist = self._on_open_state_app_helper()
1843        dlg = wx.FileDialog(self, 
1844                            "Choose a file", 
1845                            self._default_save_location, "",
1846                            plug_wlist)
1847        if dlg.ShowModal() == wx.ID_OK:
1848            path = dlg.GetPath()
1849            if path is not None:
1850                self._default_save_location = os.path.dirname(path)
1851        dlg.Destroy()
1852        self.load_state(path=path) 
1853   
1854    def _on_open_state_app_helper(self):
1855        """
1856        Helps '_on_open_state_application()' to find the extension of
1857        the current perspective/application
1858        """
1859        # No current perspective or no extension attr
1860        if self._current_perspective is None:
1861            return PLUGINS_WLIST
1862        try:
1863            # Find the extension of the perspective
1864            # and get that as 1st item in list
1865            ind = None
1866            app_ext = self._current_perspective._extensions
1867            plug_wlist = config.PLUGINS_WLIST
1868            for ext in set(plug_wlist):
1869                if ext.count(app_ext) > 0:
1870                    ind = ext
1871                    break
1872            # Found the extension
1873            if ind != None:
1874                plug_wlist.remove(ind)
1875                plug_wlist.insert(0, ind)
1876                try:
1877                    plug_wlist = '|'.join(plug_wlist)
1878                except:
1879                    plug_wlist = ''
1880
1881        except:
1882            plug_wlist = PLUGINS_WLIST
1883           
1884        return plug_wlist
1885           
1886    def _on_open_state_project(self, event):
1887        """
1888        """
1889        path = None
1890        if self._default_save_location == None:
1891            self._default_save_location = os.getcwd()
1892        wx.PostEvent(self, StatusEvent(status="Loading Project file..."))
1893        dlg = wx.FileDialog(self, 
1894                            "Choose a file", 
1895                            self._default_save_location, "",
1896                             APPLICATION_WLIST)
1897        if dlg.ShowModal() == wx.ID_OK:
1898            path = dlg.GetPath()
1899            if path is not None:
1900                self._default_save_location = os.path.dirname(path)
1901        dlg.Destroy()
1902       
1903        self.load_state(path=path, is_project=True)
1904       
1905    def _on_save_application(self, event):
1906        """
1907        save the state of the current active application
1908        """
1909        if self.cpanel_on_focus is not None:
1910            try:
1911                wx.PostEvent(self, 
1912                             StatusEvent(status="Saving Analysis file..."))
1913                self.cpanel_on_focus.on_save(event)
1914                wx.PostEvent(self, 
1915                             StatusEvent(status="Completed saving."))
1916            except:
1917                msg = "Error occurred while saving: "
1918                msg += "To save, the application panel should have a data set.."
1919                wx.PostEvent(self, StatusEvent(status=msg)) 
1920           
1921    def _on_save_project(self, event):
1922        """
1923        save the state of the SasView as *.svs
1924        """
1925        if self._current_perspective is  None:
1926            return
1927        wx.PostEvent(self, StatusEvent(status="Saving Project file..."))
1928        reader, ext = self._current_perspective.get_extensions()
1929        path = None
1930        extension = '*' + APPLICATION_STATE_EXTENSION
1931        dlg = wx.FileDialog(self, "Save Project file",
1932                            self._default_save_location, "sasview_proj",
1933                             extension, 
1934                             wx.SAVE)
1935        if dlg.ShowModal() == wx.ID_OK:
1936            path = dlg.GetPath()
1937            self._default_save_location = os.path.dirname(path)
1938        else:
1939            return None
1940        dlg.Destroy()
1941        try:
1942            if path is None:
1943                return
1944            # default cansas xml doc
1945            doc = None
1946            for panel in self.panels.values():
1947                temp = panel.save_project(doc)
1948                if temp is not None:
1949                    doc = temp
1950           
1951            # Write the XML document
1952            extens = APPLICATION_STATE_EXTENSION
1953            fName = os.path.splitext(path)[0] + extens
1954            if doc != None:
1955                fd = open(fName, 'w')
1956                fd.write(doc.toprettyxml())
1957                fd.close()
1958                wx.PostEvent(self, StatusEvent(status="Completed Saving."))
1959            else:
1960                msg = "No perspective windows are loaded. "
1961                msg += "No data was saved to %s\n" % (str(path))
1962                logging.error(msg)
1963                wx.PostEvent(self,StatusEvent(status=msg))
1964        except:
1965            msg = "Error occurred while saving: "
1966            msg += "To save, at least one application panel "
1967            msg += "should have a data set.."
1968            wx.PostEvent(self, StatusEvent(status=msg))   
1969                   
1970    def on_save_helper(self, doc, reader, panel, path):
1971        """
1972        Save state into a file
1973        """
1974        try:
1975            if reader is not None:
1976                # case of a panel with multi-pages
1977                if hasattr(panel, "opened_pages"):
1978                    for uid, page in panel.opened_pages.iteritems():
1979                        data = page.get_data()
1980                        # state must be cloned
1981                        state = page.get_state().clone()
1982                        if data is not None:
1983                            new_doc = reader.write_toXML(data, state)
1984                            if doc != None and hasattr(doc, "firstChild"):
1985                                child = new_doc.firstChild.firstChild
1986                                doc.firstChild.appendChild(child) 
1987                            else:
1988                                doc = new_doc
1989                # case of only a panel
1990                else:
1991                    data = panel.get_data()
1992                    state = panel.get_state()
1993                    if data is not None:
1994                        new_doc = reader.write_toXML(data, state)
1995                        if doc != None and hasattr(doc, "firstChild"):
1996                            child = new_doc.firstChild.firstChild
1997                            doc.firstChild.appendChild(child) 
1998                        else:
1999                            doc = new_doc
2000        except: 
2001            raise
2002            #pass
2003
2004        return doc
2005
2006    def quit_guiframe(self):
2007        """
2008        Pop up message to make sure the user wants to quit the application
2009        """
2010        message = "\nDo you really want to exit this application?        \n\n"
2011        dial = wx.MessageDialog(self, message, 'Confirm Exit',
2012                           wx.YES_NO|wx.YES_DEFAULT|wx.ICON_QUESTION)
2013        if dial.ShowModal() == wx.ID_YES:
2014            return True
2015        else:
2016            return False   
2017       
2018    def WindowClose(self, event=None):
2019        """
2020        Quit the application from x icon
2021        """
2022        flag = self.quit_guiframe()
2023        if flag:
2024            _pylab_helpers.Gcf.figs = {}
2025            self.Close()
2026           
2027    def Close(self, event=None):
2028        """
2029        Quit the application
2030        """
2031        wx.Exit()
2032        sys.exit()
2033           
2034    def _check_update(self, event=None): 
2035        """
2036        Check with the deployment server whether a new version
2037        of the application is available.
2038        A thread is started for the connecting with the server. The thread calls
2039        a call-back method when the current version number has been obtained.
2040        """
2041        try:
2042            conn = httplib.HTTPConnection(config.__update_URL__[0], 
2043                              timeout=3.0)
2044            conn.request("GET", config.__update_URL__[1])
2045            res = conn.getresponse()
2046            content = res.read()
2047            conn.close()
2048        except:
2049            content = "0.0.0"
2050       
2051        version = content.strip()
2052        if len(re.findall('\d+\.\d+\.\d+$', version)) < 0:
2053            content = "0.0.0"
2054        self._process_version(content, standalone=event==None)
2055   
2056    def _process_version(self, version, standalone=True):
2057        """
2058        Call-back method for the process of checking for updates.
2059        This methods is called by a VersionThread object once the current
2060        version number has been obtained. If the check is being done in the
2061        background, the user will not be notified unless there's an update.
2062       
2063        :param version: version string
2064        :param standalone: True of the update is being checked in
2065           the background, False otherwise.
2066           
2067        """
2068        try:
2069            if version == "0.0.0":
2070                msg = "Could not connect to the application server."
2071                msg += " Please try again later."
2072                self.SetStatusText(msg)
2073            elif cmp(version, config.__version__) > 0:
2074                msg = "Version %s is available! " % str(version)
2075                if not standalone:
2076                    import webbrowser
2077                    webbrowser.open(config.__download_page__)
2078                else:
2079                    msg +=  "See the help menu to download it." 
2080                self.SetStatusText(msg)
2081            else:
2082                if not standalone:
2083                    msg = "You have the latest version"
2084                    msg += " of %s" % str(config.__appname__)
2085                    self.SetStatusText(msg)
2086        except:
2087            msg = "guiframe: could not get latest application"
2088            msg += " version number\n  %s" % sys.exc_value
2089            logging.error(msg)
2090            if not standalone:
2091                msg = "Could not connect to the application server."
2092                msg += " Please try again later."
2093                self.SetStatusText(msg)
2094                   
2095    def _onAbout(self, evt):
2096        """
2097        Pop up the about dialog
2098       
2099        :param evt: menu event
2100       
2101        """
2102        if config._do_aboutbox:
2103            import sans.guiframe.aboutbox as AboutBox 
2104            dialog = AboutBox.DialogAbout(None, -1, "")
2105            dialog.ShowModal()   
2106                     
2107    def _onTutorial(self, evt):
2108        """
2109        Pop up the tutorial dialog
2110       
2111        :param evt: menu event
2112       
2113        """
2114        if config._do_tutorial:   
2115            path = config.TUTORIAL_PATH
2116            if IS_WIN:
2117                try:
2118                    from sans.guiframe.pdfview import PDFFrame
2119                    dialog = PDFFrame(None, -1, "Tutorial", path)
2120                    # put icon
2121                    self.put_icon(dialog) 
2122                    dialog.Show(True) 
2123                except:
2124                    logging.error("Error in _onTutorial: %s" % sys.exc_value)
2125                    try:
2126                        #in case when the pdf default set other than acrobat
2127                        import ho.pisa as pisa
2128                        pisa.startViewer(path)
2129                    except:
2130                        msg = "This feature requires 'PDF Viewer'\n"
2131                        msg += "Please install it first (Free)..."
2132                        wx.MessageBox(msg, 'Error')
2133            else:
2134                try:
2135                    command = "open '%s'" % path
2136                    os.system(command)
2137                except:
2138                    try:
2139                        #in case when the pdf default set other than preview
2140                        import ho.pisa as pisa
2141                        pisa.startViewer(path)
2142                    except:
2143                        msg = "This feature requires 'Preview' Application\n"
2144                        msg += "Please install it first..."
2145                        wx.MessageBox(msg, 'Error')
2146
2147                     
2148    def set_manager(self, manager):
2149        """
2150        Sets the application manager for this frame
2151       
2152        :param manager: frame manager
2153        """
2154        self.app_manager = manager
2155       
2156    def post_init(self):
2157        """
2158        This initialization method is called after the GUI
2159        has been created and all plug-ins loaded. It calls
2160        the post_init() method of each plug-in (if it exists)
2161        so that final initialization can be done.
2162        """
2163        for item in self.plugins:
2164            if hasattr(item, "post_init"):
2165                item.post_init()
2166       
2167    def set_default_perspective(self):
2168        """
2169        Choose among the plugin the first plug-in that has
2170        "set_default_perspective" method and its return value is True will be
2171        as a default perspective when the welcome page is closed
2172        """
2173        for item in self.plugins:
2174            if hasattr(item, "set_default_perspective"):
2175                if item.set_default_perspective():
2176                    item.on_perspective(event=None)
2177                    return 
2178       
2179    def set_perspective(self, panels):
2180        """
2181        Sets the perspective of the GUI.
2182        Opens all the panels in the list, and closes
2183        all the others.
2184       
2185        :param panels: list of panels
2186        """
2187        for item in self.panels.keys():
2188            # Check whether this is a sticky panel
2189            if hasattr(self.panels[item], "ALWAYS_ON"):
2190                if self.panels[item].ALWAYS_ON:
2191                    continue 
2192            if self.panels[item] == None:
2193                continue
2194            if self.panels[item].window_name in panels:
2195                frame = self.panels[item].get_frame()
2196                if not frame.IsShown():
2197                    frame.Show(True)
2198            else:
2199                # always show the data panel if enable
2200                style = self.__gui_style & GUIFRAME.MANAGER_ON
2201                if (style == GUIFRAME.MANAGER_ON) and self.panels[item] == self._data_panel:
2202                    if 'data_panel' in self.panels.keys():
2203                        frame = self.panels['data_panel'].get_frame()
2204                        if frame == None:
2205                            continue
2206                        flag = frame.IsShown()
2207                        frame.Show(flag)
2208                else:
2209                    frame = self.panels[item].get_frame()
2210                    if frame == None:
2211                        continue
2212
2213                    if frame.IsShown():
2214                        frame.Show(False)
2215       
2216    def show_data_panel(self, event=None, action=True):
2217        """
2218        show the data panel
2219        """
2220        if self._data_panel_menu == None:
2221            return
2222        label = self._data_panel_menu.GetText()
2223        pane = self.panels["data_panel"]
2224        frame = pane.get_frame()
2225        if label == 'Show Data Explorer':
2226            if action: 
2227                frame.Show(True)
2228            self.__gui_style = self.__gui_style | GUIFRAME.MANAGER_ON
2229            self._data_panel_menu.SetText('Hide Data Explorer')
2230        else:
2231            if action:
2232                frame.Show(False)
2233            self.__gui_style = self.__gui_style & (~GUIFRAME.MANAGER_ON)
2234            self._data_panel_menu.SetText('Show Data Explorer')
2235
2236    def add_data_helper(self, data_list):
2237        """
2238        """
2239        if self._data_manager is not None:
2240            self._data_manager.add_data(data_list)
2241       
2242    def add_data(self, data_list):
2243        """
2244        receive a dictionary of data from loader
2245        store them its data manager if possible
2246        send to data the current active perspective if the data panel
2247        is not active.
2248        :param data_list: dictionary of data's ID and value Data
2249        """
2250        #Store data into manager
2251        self.add_data_helper(data_list)
2252        # set data in the data panel
2253        if self._data_panel is not None:
2254            data_state = self._data_manager.get_data_state(data_list.keys())
2255            self._data_panel.load_data_list(data_state)
2256        #if the data panel is shown wait for the user to press a button
2257        #to send data to the current perspective. if the panel is not
2258        #show  automatically send the data to the current perspective
2259        style = self.__gui_style & GUIFRAME.MANAGER_ON
2260        if style == GUIFRAME.MANAGER_ON:
2261            #wait for button press from the data panel to set_data
2262            if self._data_panel is not None:
2263                self._data_panel.frame.Show(True)
2264        else:
2265            #automatically send that to the current perspective
2266            self.set_data(data_id=data_list.keys())
2267       
2268    def set_data(self, data_id, theory_id=None): 
2269        """
2270        set data to current perspective
2271        """
2272        list_data, _ = self._data_manager.get_by_id(data_id)
2273        if self._current_perspective is not None:
2274            self._current_perspective.set_data(list_data.values())
2275
2276        else:
2277            msg = "Guiframe does not have a current perspective"
2278            logging.info(msg)
2279           
2280    def set_theory(self, state_id, theory_id=None):
2281        """
2282        """
2283        _, list_theory = self._data_manager.get_by_id(theory_id)
2284        if self._current_perspective is not None:
2285            try:
2286                self._current_perspective.set_theory(list_theory.values())
2287            except:
2288                msg = "Guiframe set_theory: \n" + str(sys.exc_value)
2289                logging.info(msg)
2290                wx.PostEvent(self, StatusEvent(status=msg, info="error"))
2291        else:
2292            msg = "Guiframe does not have a current perspective"
2293            logging.info(msg)
2294           
2295    def plot_data(self,  state_id, data_id=None,
2296                  theory_id=None, append=False):
2297        """
2298        send a list of data to plot
2299        """
2300        total_plot_list = []
2301        data_list, _ = self._data_manager.get_by_id(data_id)
2302        _, temp_list_theory = self._data_manager.get_by_id(theory_id)
2303        total_plot_list = data_list.values()
2304        for item in temp_list_theory.values():
2305            theory_data, theory_state = item
2306            total_plot_list.append(theory_data)
2307        GROUP_ID = wx.NewId()
2308        for new_plot in total_plot_list:
2309            if append:
2310                if self.panel_on_focus is None:
2311                    message = "cannot append plot. No plot panel on focus!"
2312                    message += "please click on any available plot to set focus"
2313                    wx.PostEvent(self, StatusEvent(status=message, 
2314                                                   info='warning'))
2315                    return 
2316                else:
2317                    if self.enable_add_data(new_plot):
2318                        new_plot.group_id = self.panel_on_focus.group_id
2319                    else:
2320                        message = "Only 1D Data can be append to"
2321                        message += " plot panel containing 1D data.\n"
2322                        message += "%s not be appended.\n" %str(new_plot.name)
2323                        message += "try new plot option.\n"
2324                        wx.PostEvent(self, StatusEvent(status=message, 
2325                                                   info='warning'))
2326            else:
2327                #if not append then new plot
2328                from sans.guiframe.dataFitting import Data2D
2329                if issubclass(Data2D, new_plot.__class__):
2330                    #for 2 D always plot in a separated new plot
2331                    new_plot.group_id = wx.NewId()
2332                else:
2333                    # plot all 1D in a new plot
2334                    new_plot.group_id = GROUP_ID
2335            title = "PLOT " + str(new_plot.title)
2336            wx.PostEvent(self, NewPlotEvent(plot=new_plot,
2337                                                  title=title,
2338                                                  group_id = new_plot.group_id))
2339           
2340    def remove_data(self, data_id, theory_id=None):
2341        """
2342        Delete data state if data_id is provide
2343        delete theory created with data of id data_id if theory_id is provide
2344        if delete all true: delete the all state
2345        else delete theory
2346        """
2347        temp = data_id + theory_id
2348        for plug in self.plugins:
2349            plug.delete_data(temp)
2350        total_plot_list = []
2351        data_list, _ = self._data_manager.get_by_id(data_id)
2352        _, temp_list_theory = self._data_manager.get_by_id(theory_id)
2353        total_plot_list = data_list.values()
2354        for item in temp_list_theory.values():
2355            theory_data, theory_state = item
2356            total_plot_list.append(theory_data)
2357        for new_plot in total_plot_list:
2358            id = new_plot.id
2359            for group_id in new_plot.list_group_id:
2360                wx.PostEvent(self, NewPlotEvent(id=id,
2361                                                   group_id=group_id,
2362                                                   action='remove'))
2363                #remove res plot: Todo: improve
2364                wx.CallAfter(self._remove_res_plot, id)
2365        self._data_manager.delete_data(data_id=data_id, 
2366                                       theory_id=theory_id)
2367       
2368    def _remove_res_plot(self, id):
2369        """
2370        Try to remove corresponding res plot
2371       
2372        : param id: id of the data
2373        """
2374        try:
2375            wx.PostEvent(self, NewPlotEvent(id=("res"+str(id)),
2376                                           group_id=("res"+str(id)),
2377                                           action='remove'))
2378        except:
2379            pass
2380   
2381    def save_data1d(self, data, fname):
2382        """
2383        Save data dialog
2384        """
2385        default_name = fname
2386        wildcard = "Text files (*.txt)|*.txt|"\
2387                    "CanSAS 1D files(*.xml)|*.xml" 
2388        path = None
2389        dlg = wx.FileDialog(self, "Choose a file",
2390                            self._default_save_location,
2391                            default_name, wildcard , wx.SAVE)
2392       
2393        if dlg.ShowModal() == wx.ID_OK:
2394            path = dlg.GetPath()
2395            # ext_num = 0 for .txt, ext_num = 1 for .xml
2396            # This is MAC Fix
2397            ext_num = dlg.GetFilterIndex()
2398            if ext_num == 0:
2399                format = '.txt'
2400            else:
2401                format = '.xml'
2402            path = os.path.splitext(path)[0] + format
2403            mypath = os.path.basename(path)
2404           
2405            #TODO: This is bad design. The DataLoader is designed
2406            #to recognize extensions.
2407            # It should be a simple matter of calling the .
2408            #save(file, data, '.xml') method
2409            # of the sans.dataloader.loader.Loader class.
2410            from sans.dataloader.loader import  Loader
2411            #Instantiate a loader
2412            loader = Loader() 
2413            format = ".txt"
2414            if os.path.splitext(mypath)[1].lower() == format:
2415                # Make sure the ext included in the file name
2416                # especially on MAC
2417                fName = os.path.splitext(path)[0] + format
2418                self._onsaveTXT(data, fName)
2419            format = ".xml"
2420            if os.path.splitext(mypath)[1].lower() == format:
2421                # Make sure the ext included in the file name
2422                # especially on MAC
2423                fName = os.path.splitext(path)[0] + format
2424                loader.save(fName, data, format)
2425            try:
2426                self._default_save_location = os.path.dirname(path)
2427            except:
2428                pass   
2429        dlg.Destroy()
2430       
2431       
2432    def _onsaveTXT(self, data, path):
2433        """
2434        Save file as txt 
2435        :TODO: Refactor and remove this method. See TODO in _onSave.
2436        """
2437        if not path == None:
2438            out = open(path, 'w')
2439            has_errors = True
2440            if data.dy == None or data.dy == []:
2441                has_errors = False
2442            # Sanity check
2443            if has_errors:
2444                try:
2445                    if len(data.y) != len(data.dy):
2446                        has_errors = False
2447                except:
2448                    has_errors = False
2449            if has_errors:
2450                if data.dx != None and data.dx != []:
2451                    out.write("<X>   <Y>   <dY>   <dX>\n")
2452                else:
2453                    out.write("<X>   <Y>   <dY>\n")
2454            else:
2455                out.write("<X>   <Y>\n")
2456               
2457            for i in range(len(data.x)):
2458                if has_errors:
2459                    if data.dx != None and data.dx != []:
2460                        if  data.dx[i] != None:
2461                            out.write("%g  %g  %g  %g\n" % (data.x[i], 
2462                                                        data.y[i],
2463                                                        data.dy[i],
2464                                                        data.dx[i]))
2465                        else:
2466                            out.write("%g  %g  %g\n" % (data.x[i], 
2467                                                        data.y[i],
2468                                                        data.dy[i]))
2469                    else:
2470                        out.write("%g  %g  %g\n" % (data.x[i], 
2471                                                    data.y[i],
2472                                                    data.dy[i]))
2473                else:
2474                    out.write("%g  %g\n" % (data.x[i], 
2475                                            data.y[i]))
2476            out.close() 
2477                             
2478    def show_data1d(self, data, name):
2479        """
2480        Show data dialog
2481        """   
2482        try:
2483            xmin = min(data.x)
2484            ymin = min(data.y)
2485        except:
2486            msg = "Unable to find min/max of \n data named %s"% \
2487                        data.filename 
2488            wx.PostEvent(self, StatusEvent(status=msg,
2489                                       info="error"))
2490            raise ValueError, msg
2491        ## text = str(data)
2492        text = data.__str__()
2493        text += 'Data Min Max:\n'
2494        text += 'X_min = %s:  X_max = %s\n'% (xmin, max(data.x))
2495        text += 'Y_min = %s:  Y_max = %s\n'% (ymin, max(data.y))
2496        if data.dy != None:
2497            text += 'dY_min = %s:  dY_max = %s\n'% (min(data.dy), max(data.dy))
2498        text += '\nData Points:\n'
2499        x_st = "X"
2500        for index in range(len(data.x)):
2501            if data.dy != None and len(data.dy) > index:
2502                dy_val = data.dy[index]
2503            else:
2504                dy_val = 0.0
2505            if data.dx != None and len(data.dx) > index:
2506                dx_val = data.dx[index]
2507            else:
2508                dx_val = 0.0
2509            if data.dxl != None and len(data.dxl) > index:
2510                if index == 0: 
2511                    x_st = "Xl"
2512                dx_val = data.dxl[index]
2513            elif data.dxw != None and len(data.dxw) > index:
2514                if index == 0: 
2515                    x_st = "Xw"
2516                dx_val = data.dxw[index]
2517           
2518            if index == 0:
2519                text += "<index> \t<X> \t<Y> \t<dY> \t<d%s>\n"% x_st
2520            text += "%s \t%s \t%s \t%s \t%s\n" % (index,
2521                                            data.x[index], 
2522                                            data.y[index],
2523                                            dy_val,
2524                                            dx_val)
2525        from pdfview import TextFrame
2526        frame = TextFrame(None, -1, "Data Info: %s"% data.name, text) 
2527        # put icon
2528        self.put_icon(frame) 
2529        frame.Show(True) 
2530           
2531    def save_data2d(self, data, fname):   
2532        """
2533        Save data2d dialog
2534        """
2535        default_name = fname
2536        wildcard = "IGOR/DAT 2D file in Q_map (*.dat)|*.DAT"
2537        dlg = wx.FileDialog(self, "Choose a file",
2538                            self._default_save_location,
2539                            default_name, wildcard , wx.SAVE)
2540       
2541        if dlg.ShowModal() == wx.ID_OK:
2542            path = dlg.GetPath()
2543            # ext_num = 0 for .txt, ext_num = 1 for .xml
2544            # This is MAC Fix
2545            ext_num = dlg.GetFilterIndex()
2546            if ext_num == 0:
2547                format = '.dat'
2548            else:
2549                format = ''
2550            path = os.path.splitext(path)[0] + format
2551            mypath = os.path.basename(path)
2552           
2553            #TODO: This is bad design. The DataLoader is designed
2554            #to recognize extensions.
2555            # It should be a simple matter of calling the .
2556            #save(file, data, '.xml') method
2557            # of the DataLoader.loader.Loader class.
2558            from sans.dataloader.loader import  Loader
2559            #Instantiate a loader
2560            loader = Loader() 
2561
2562            format = ".dat"
2563            if os.path.splitext(mypath)[1].lower() == format:
2564                # Make sure the ext included in the file name
2565                # especially on MAC
2566                fileName = os.path.splitext(path)[0] + format
2567                loader.save(fileName, data, format)
2568            try:
2569                self._default_save_location = os.path.dirname(path)
2570            except:
2571                pass   
2572        dlg.Destroy() 
2573                             
2574    def show_data2d(self, data, name):
2575        """
2576        Show data dialog
2577        """   
2578
2579        wx.PostEvent(self, StatusEvent(status = "Gathering Data2D Info.", 
2580                                       type = 'start' ))
2581        text = data.__str__() 
2582        text += 'Data Min Max:\n'
2583        text += 'I_min = %s\n'% min(data.data)
2584        text += 'I_max = %s\n\n'% max(data.data)
2585        text += 'Data (First 2501) Points:\n'
2586        text += 'Data columns include err(I).\n'
2587        text += 'ASCII data starts here.\n'
2588        text += "<index> \t<Qx> \t<Qy> \t<I> \t<dI> \t<dQparal> \t<dQperp>\n"
2589        di_val = 0.0
2590        dx_val = 0.0
2591        dy_val = 0.0
2592        #mask_val = True
2593        len_data = len(data.qx_data)
2594        for index in xrange(0, len_data):
2595            x_val = data.qx_data[index]
2596            y_val = data.qy_data[index]
2597            i_val = data.data[index]
2598            if data.err_data != None: 
2599                di_val = data.err_data[index]
2600            if data.dqx_data != None: 
2601                dx_val = data.dqx_data[index]
2602            if data.dqy_data != None: 
2603                dy_val = data.dqy_data[index]
2604 
2605            text += "%s \t%s \t%s \t%s \t%s \t%s \t%s\n" % (index,
2606                                            x_val, 
2607                                            y_val,
2608                                            i_val,
2609                                            di_val,
2610                                            dx_val,
2611                                            dy_val)
2612            # Takes too long time for typical data2d: Break here
2613            if index >= 2500:
2614                text += ".............\n"
2615                break
2616
2617        from pdfview import TextFrame
2618        frame = TextFrame(None, -1, "Data Info: %s"% data.name, text) 
2619        # put icon
2620        self.put_icon(frame)
2621        frame.Show(True) 
2622        wx.PostEvent(self, StatusEvent(status = "Data2D Info Displayed", 
2623                                       type = 'stop' ))
2624                                 
2625    def set_current_perspective(self, perspective):
2626        """
2627        set the current active perspective
2628        """
2629        self._current_perspective = perspective
2630        name = "No current analysis selected"
2631        if self._current_perspective is not None:
2632            self._add_current_plugin_menu()
2633            for panel in self.panels.values():
2634                if hasattr(panel, 'CENTER_PANE') and panel.CENTER_PANE:
2635                    for name in self._current_perspective.get_perspective():
2636                        frame = panel.get_frame()
2637                        if frame != None:
2638                            if name == panel.window_name:
2639                                panel.on_set_focus(event=None)
2640                                frame.Show(True)
2641                            else:
2642                                frame.Show(False)
2643                            #break               
2644            name = self._current_perspective.sub_menu
2645            if self._data_panel is not None:
2646                self._data_panel.set_active_perspective(name)
2647                self._check_applications_menu()
2648            #Set the SasView title
2649            self._set_title_name(name)
2650           
2651    def _set_title_name(self, name):
2652        """
2653        Set the SasView title w/ the current application name
2654       
2655        : param name: application name [string]
2656        """
2657        # Set SanView Window title w/ application anme
2658        title = self.title + "  - " + name + " -"
2659        self.SetTitle(title)
2660           
2661    def _check_applications_menu(self):
2662        """
2663        check the menu of the current application
2664        """
2665        if self._applications_menu is not None:
2666            for menu in self._applications_menu.GetMenuItems():
2667                if self._current_perspective is not None:
2668                    name = self._current_perspective.sub_menu
2669                    if menu.IsCheckable():
2670                        if menu.GetLabel() == name:
2671                            menu.Check(True)
2672                        else:
2673                            menu.Check(False) 
2674           
2675    def enable_add_data(self, new_plot):
2676        """
2677        Enable append data on a plot panel
2678        """
2679
2680        if self.panel_on_focus not in self._plotting_plugin.plot_panels.values():
2681            return
2682        is_theory = len(self.panel_on_focus.plots) <= 1 and \
2683            self.panel_on_focus.plots.values()[0].__class__.__name__ == "Theory1D"
2684           
2685        is_data2d = hasattr(new_plot, 'data')
2686       
2687        is_data1d = self.panel_on_focus.__class__.__name__ == "ModelPanel1D"\
2688            and self.panel_on_focus.group_id is not None
2689        has_meta_data = hasattr(new_plot, 'meta_data')
2690       
2691        #disable_add_data if the data is being recovered from  a saved state file.
2692        is_state_data = False
2693        if has_meta_data:
2694            if 'invstate' in new_plot.meta_data: 
2695                is_state_data = True
2696            if  'prstate' in new_plot.meta_data: 
2697                is_state_data = True
2698            if  'fitstate' in new_plot.meta_data: 
2699                is_state_data = True
2700   
2701        return is_data1d and not is_data2d and not is_theory and not is_state_data
2702   
2703    def check_multimode(self, perspective=None):
2704        """
2705        Check the perspective have batch mode capablitity
2706        """
2707        if perspective == None or self._data_panel == None:
2708            return
2709        flag = perspective.get_batch_capable()
2710        flag_on = perspective.batch_on
2711        if flag:
2712            self._data_panel.rb_single_mode.SetValue(not flag_on)
2713            self._data_panel.rb_batch_mode.SetValue(flag_on)
2714        else:
2715            self._data_panel.rb_single_mode.SetValue(True)
2716            self._data_panel.rb_batch_mode.SetValue(False)
2717        self._data_panel.rb_single_mode.Enable(flag)
2718        self._data_panel.rb_batch_mode.Enable(flag)
2719               
2720
2721   
2722    def enable_edit_menu(self):
2723        """
2724        enable menu item under edit menu depending on the panel on focus
2725        """
2726        if self.cpanel_on_focus is not None and self._edit_menu is not None:
2727            flag = self.cpanel_on_focus.get_undo_flag()
2728            self._edit_menu.Enable(GUIFRAME_ID.UNDO_ID, flag)
2729            flag = self.cpanel_on_focus.get_redo_flag()
2730            self._edit_menu.Enable(GUIFRAME_ID.REDO_ID, flag)
2731            flag = self.cpanel_on_focus.get_copy_flag()
2732            self._edit_menu.Enable(GUIFRAME_ID.COPY_ID, flag)
2733            flag = self.cpanel_on_focus.get_paste_flag()
2734            self._edit_menu.Enable(GUIFRAME_ID.PASTE_ID, flag)
2735
2736            #Copy menu
2737            flag = self.cpanel_on_focus.get_copy_flag()
2738            #self._edit_menu.ENABLE(GUIFRAME_ID.COPYAS_ID,flag)
2739            self._edit_menu_copyas.Enable(GUIFRAME_ID.COPYEX_ID, flag)
2740            self._edit_menu_copyas.Enable(GUIFRAME_ID.COPYLAT_ID, flag)
2741
2742            flag = self.cpanel_on_focus.get_preview_flag()
2743            self._edit_menu.Enable(GUIFRAME_ID.PREVIEW_ID, flag)
2744            flag = self.cpanel_on_focus.get_reset_flag()
2745            self._edit_menu.Enable(GUIFRAME_ID.RESET_ID, flag)
2746        else:
2747            flag = False
2748            self._edit_menu.Enable(GUIFRAME_ID.UNDO_ID, flag)
2749            self._edit_menu.Enable(GUIFRAME_ID.REDO_ID, flag)
2750            self._edit_menu.Enable(GUIFRAME_ID.COPY_ID, flag)
2751            self._edit_menu.Enable(GUIFRAME_ID.PASTE_ID, flag)
2752            #self._edit_menu.Enable(GUIFRAME_ID.COPYEX_ID, flag)
2753            #self._edit_menu.Enable(GUIFRAME_ID.COPYLAT_ID, flag)
2754            #self._edit_menu.Enable(GUIFRAME_ID.COPYAS_ID, flag)
2755            self._edit_menu.Enable(GUIFRAME_ID.PREVIEW_ID, flag)
2756            self._edit_menu.Enable(GUIFRAME_ID.RESET_ID, flag)
2757           
2758    def on_undo_panel(self, event=None):
2759        """
2760        undo previous action of the last panel on focus if possible
2761        """
2762        if self.cpanel_on_focus is not None:
2763            self.cpanel_on_focus.on_undo(event)
2764           
2765    def on_redo_panel(self, event=None):
2766        """
2767        redo the last cancel action done on the last panel on focus
2768        """
2769        if self.cpanel_on_focus is not None:
2770            self.cpanel_on_focus.on_redo(event)
2771           
2772    def on_copy_panel(self, event=None):
2773        """
2774        copy the last panel on focus if possible
2775        """
2776        if self.cpanel_on_focus is not None:
2777            self.cpanel_on_focus.on_copy(event)
2778           
2779    def on_paste_panel(self, event=None):
2780        """
2781        paste clipboard to the last panel on focus
2782        """
2783        if self.cpanel_on_focus is not None:
2784            self.cpanel_on_focus.on_paste(event)
2785                   
2786    def on_bookmark_panel(self, event=None):
2787        """
2788        bookmark panel
2789        """
2790        if self.cpanel_on_focus is not None:
2791            self.cpanel_on_focus.on_bookmark(event)
2792           
2793    def append_bookmark(self, event=None):
2794        """
2795        Bookmark available information of the panel on focus
2796        """
2797        self._toolbar.append_bookmark(event)
2798           
2799    def on_save_panel(self, event=None):
2800        """
2801        save possible information on the current panel
2802        """
2803        if self.cpanel_on_focus is not None:
2804            self.cpanel_on_focus.on_save(event)
2805           
2806    def on_preview_panel(self, event=None):
2807        """
2808        preview information on the panel on focus
2809        """
2810        if self.cpanel_on_focus is not None:
2811            self.cpanel_on_focus.on_preview(event)
2812           
2813    def on_print_panel(self, event=None):
2814        """
2815        print available information on the last panel on focus
2816        """
2817        if self.cpanel_on_focus is not None:
2818            self.cpanel_on_focus.on_print(event)
2819           
2820    def on_zoom_panel(self, event=None):
2821        """
2822        zoom on the current panel if possible
2823        """
2824        if self.cpanel_on_focus is not None:
2825            self.cpanel_on_focus.on_zoom(event)
2826           
2827    def on_zoom_in_panel(self, event=None):
2828        """
2829        zoom in of the panel on focus
2830        """
2831        if self.cpanel_on_focus is not None:
2832            self.cpanel_on_focus.on_zoom_in(event)
2833           
2834    def on_zoom_out_panel(self, event=None):
2835        """
2836        zoom out on the panel on focus
2837        """
2838        if self.cpanel_on_focus is not None:
2839            self.cpanel_on_focus.on_zoom_out(event)
2840           
2841    def on_drag_panel(self, event=None):
2842        """
2843        drag apply to the panel on focus
2844        """
2845        if self.cpanel_on_focus is not None:
2846            self.cpanel_on_focus.on_drag(event)
2847           
2848    def on_reset_panel(self, event=None):
2849        """
2850        reset the current panel
2851        """
2852        if self.cpanel_on_focus is not None:
2853            self.cpanel_on_focus.on_reset(event)
2854           
2855    def on_change_caption(self, name, old_caption, new_caption):     
2856        """
2857        Change the panel caption
2858       
2859        :param name: window_name of the pane
2860        :param old_caption: current caption [string]
2861        :param new_caption: new caption [string]
2862        """
2863        # wx.aui.AuiPaneInfo
2864        pane_info = self.get_paneinfo(old_caption) 
2865        # update the data_panel.cb_plotpanel
2866        if 'data_panel' in self.panels.keys():
2867            # remove from data_panel combobox
2868            data_panel = self.panels["data_panel"]
2869            if data_panel.cb_plotpanel is not None:
2870                # Check if any panel has the same caption
2871                has_newstring = data_panel.cb_plotpanel.FindString\
2872                                                            (str(new_caption)) 
2873                caption = new_caption
2874                if has_newstring != wx.NOT_FOUND:
2875                    captions = self._get_plotpanel_captions()
2876                    # Append nummber
2877                    inc = 1
2878                    while (1):
2879                        caption = new_caption + '_%s'% str(inc)
2880                        if caption not in captions:
2881                            break
2882                        inc += 1
2883                    # notify to users
2884                    msg = "Found Same Title: Added '_%s'"% str(inc)
2885                    wx.PostEvent(self, StatusEvent(status=msg))
2886                # update data_panel cb
2887                pos = data_panel.cb_plotpanel.FindString(str(old_caption)) 
2888                if pos != wx.NOT_FOUND:
2889                    data_panel.cb_plotpanel.SetString(pos, caption)
2890                    data_panel.cb_plotpanel.SetStringSelection(caption)
2891        # New Caption
2892        pane_info.SetTitle(caption)
2893        return caption
2894       
2895    def get_paneinfo(self, name):
2896        """
2897        Get pane Caption from window_name
2898       
2899        :param name: window_name in AuiPaneInfo
2900        : return: AuiPaneInfo of the name
2901        """
2902        for panel in self.plot_panels.values():
2903            if panel.frame.GetTitle() == name:
2904                return panel.frame
2905        return None
2906   
2907    def enable_undo(self):
2908        """
2909        enable undo related control
2910        """
2911        if self.cpanel_on_focus is not None:
2912            self._toolbar.enable_undo(self.cpanel_on_focus)
2913           
2914    def enable_redo(self):
2915        """
2916        enable redo
2917        """
2918        if self.cpanel_on_focus is not None:
2919            self._toolbar.enable_redo(self.cpanel_on_focus)
2920           
2921    def enable_copy(self):
2922        """
2923        enable copy related control
2924        """
2925        if self.cpanel_on_focus is not None:
2926            self._toolbar.enable_copy(self.cpanel_on_focus)
2927           
2928    def enable_paste(self):
2929        """
2930        enable paste
2931        """
2932        if self.cpanel_on_focus is not None:
2933            self._toolbar.enable_paste(self.cpanel_on_focus)
2934                       
2935    def enable_bookmark(self):
2936        """
2937        Bookmark
2938        """
2939        if self.cpanel_on_focus is not None:
2940            self._toolbar.enable_bookmark(self.cpanel_on_focus)
2941           
2942    def enable_save(self):
2943        """
2944        save
2945        """
2946        if self.cpanel_on_focus is not None:
2947            self._toolbar.enable_save(self.cpanel_on_focus)
2948           
2949    def enable_preview(self):
2950        """
2951        preview
2952        """
2953        if self.cpanel_on_focus is not None:
2954            self._toolbar.enable_preview(self.cpanel_on_focus)
2955           
2956    def enable_print(self):
2957        """
2958        print
2959        """
2960        if self.cpanel_on_focus is not None:
2961            self._toolbar.enable_print(self.cpanel_on_focus)
2962           
2963    def enable_zoom(self):
2964        """
2965        zoom
2966        """
2967        if self.cpanel_on_focus is not None:
2968            self._toolbar.enable_zoom(self.panel_on_focus)
2969           
2970    def enable_zoom_in(self):
2971        """
2972        zoom in
2973        """
2974        if self.cpanel_on_focus is not None:
2975            self._toolbar.enable_zoom_in(self.panel_on_focus)
2976           
2977    def enable_zoom_out(self):
2978        """
2979        zoom out
2980        """
2981        if self.cpanel_on_focus is not None:
2982            self._toolbar.enable_zoom_out(self.panel_on_focus)
2983           
2984    def enable_drag(self, event=None):
2985        """
2986        drag
2987        """
2988        #Not implemeted
2989           
2990    def enable_reset(self):
2991        """
2992        reset the current panel
2993        """
2994        if self.cpanel_on_focus is not None:
2995            self._toolbar.enable_reset(self.panel_on_focus)
2996           
2997    def get_toolbar_height(self):
2998        """
2999        """
3000        size_y = 0
3001        if self.GetToolBar() != None and self.GetToolBar().IsShown():
3002            if not IS_LINUX:
3003                _, size_y = self.GetToolBar().GetSizeTuple()
3004        return size_y
3005   
3006    def set_schedule_full_draw(self, panel=None, func='del'):
3007        """
3008        Add/subtract the schedule full draw list with the panel given
3009       
3010        :param panel: plot panel
3011        :param func: append or del [string]
3012        """
3013
3014        # append this panel in the schedule list if not in yet
3015        if func == 'append':
3016            if not panel in self.schedule_full_draw_list:
3017                self.schedule_full_draw_list.append(panel) 
3018        # remove this panel from schedule list
3019        elif func == 'del':
3020            if len(self.schedule_full_draw_list) > 0:
3021                if panel in self.schedule_full_draw_list:
3022                    self.schedule_full_draw_list.remove(panel)
3023
3024        # reset the schdule
3025        if len(self.schedule_full_draw_list) == 0:
3026            self.schedule = False
3027        else:
3028            self.schedule = True   
3029       
3030    def full_draw(self):
3031        """
3032        Draw the panels with axes in the schedule to full dwar list
3033        """
3034       
3035        count = len(self.schedule_full_draw_list)
3036        #if not self.schedule:
3037        if count < 1:
3038            self.set_schedule(False)
3039            return
3040
3041        else:
3042            ind = 0
3043            # if any of the panel is shown do full_draw
3044            for panel in self.schedule_full_draw_list:
3045                ind += 1
3046                if panel.frame.IsShown():
3047                    break
3048                # otherwise, return
3049                if ind == count:
3050                    return
3051        #Simple redraw only for a panel shown
3052        def f_draw(panel):
3053            """
3054            Draw A panel in the full draw list
3055            """
3056            try:
3057                # This checking of GetCapture is to stop redrawing
3058                # while any panel is capture.
3059                frame = panel.frame
3060               
3061                if not frame.GetCapture():
3062                    # draw if possible
3063                    panel.set_resizing(False)
3064                    #panel.Show(True)
3065                    panel.draw_plot()
3066                # Check if the panel is not shown
3067                flag = frame.IsShown()
3068                frame.Show(flag) 
3069            except:
3070                pass
3071     
3072        # Draw all panels
3073        if count == 1:
3074            f_draw(self.schedule_full_draw_list[0]) 
3075        else:
3076            map(f_draw, self.schedule_full_draw_list)
3077        # Reset the attr 
3078        if len(self.schedule_full_draw_list) == 0:
3079            self.set_schedule(False)
3080        else:
3081            self.set_schedule(True)
3082       
3083    def set_schedule(self, schedule=False): 
3084        """
3085        Set schedule
3086        """
3087        self.schedule = schedule
3088               
3089    def get_schedule(self): 
3090        """
3091        Get schedule
3092        """
3093        return self.schedule
3094   
3095    def on_set_plot_focus(self, panel):
3096        """
3097        Set focus on a plot panel
3098        """
3099        if panel == None:
3100            return
3101        #self.set_plot_unfocus()
3102        panel.on_set_focus(None) 
3103        # set focusing panel
3104        self.panel_on_focus = panel 
3105        self.set_panel_on_focus(None)
3106
3107    def set_plot_unfocus(self): 
3108        """
3109        Un focus all plot panels
3110        """
3111        for plot in self.plot_panels.values():
3112            plot.on_kill_focus(None)
3113   
3114    def get_window_size(self):
3115        """
3116        Get window size
3117       
3118        :return size: tuple
3119        """
3120        width, height = self.GetSizeTuple()
3121        if not IS_WIN:
3122            # Subtract toolbar height to get real window side
3123            if self._toolbar.IsShown():
3124                height -= 45
3125        return (width, height)
3126           
3127    def _onDrawIdle(self, *args, **kwargs):
3128        """
3129        ReDraw with axes
3130        """
3131        try:
3132            # check if it is time to redraw
3133            if self.GetCapture() == None:
3134                # Draw plot, changes resizing too
3135                self.full_draw()
3136        except:
3137            pass
3138           
3139        # restart idle       
3140        self._redraw_idle(*args, **kwargs)
3141
3142           
3143    def _redraw_idle(self, *args, **kwargs):
3144        """
3145        Restart Idle
3146        """
3147        # restart idle   
3148        self.idletimer.Restart(100*TIME_FACTOR, *args, **kwargs)
3149
3150       
3151class DefaultPanel(wx.Panel, PanelBase):
3152    """
3153    Defines the API for a panels to work with
3154    the GUI manager
3155    """
3156    ## Internal nickname for the window, used by the AUI manager
3157    window_name = "default"
3158    ## Name to appear on the window title bar
3159    window_caption = "Welcome panel"
3160    ## Flag to tell the AUI manager to put this panel in the center pane
3161    CENTER_PANE = True
3162    def __init__(self, parent, *args, **kwds):
3163        wx.Panel.__init__(self, parent, *args, **kwds)
3164        PanelBase.__init__(self, parent)
3165   
3166
3167
3168class ViewApp(wx.App):
3169    """
3170    Toy application to test this Frame
3171    """
3172    def OnInit(self):
3173        """
3174        When initialised
3175        """
3176        pos, size, self.is_max = self.window_placement((GUIFRAME_WIDTH, 
3177                                           GUIFRAME_HEIGHT))     
3178        self.frame = ViewerFrame(parent=None, 
3179                             title=APPLICATION_NAME, 
3180                             pos=pos, 
3181                             gui_style = DEFAULT_STYLE,
3182                             size=size)
3183        self.frame.Hide()
3184        if not IS_WIN:
3185            self.frame.EnableCloseButton(False)
3186        self.s_screen = None
3187
3188        try:
3189            self.open_file()
3190        except:
3191            msg = "%s Could not load " % str(APPLICATION_NAME)
3192            msg += "input file from command line.\n"
3193            logging.error(msg)
3194        # Display a splash screen on top of the frame.
3195        try:
3196            if os.path.isfile(SPLASH_SCREEN_PATH):
3197                self.s_screen = self.display_splash_screen(parent=self.frame, 
3198                                        path=SPLASH_SCREEN_PATH)
3199            else:
3200                self.frame.Show()   
3201        except:
3202            if self.s_screen is not None:
3203                self.s_screen.Close()
3204            msg = "Cannot display splash screen\n"
3205            msg += str (sys.exc_value)
3206            logging.error(msg)
3207            self.frame.Show()
3208
3209        self.SetTopWindow(self.frame)
3210 
3211        return True
3212   
3213    def maximize_win(self):
3214        """
3215        Maximize the window after the frame shown
3216        """
3217        if self.is_max:
3218            if self.frame.IsShown():
3219                # Max window size
3220                self.frame.Maximize(self.is_max)
3221
3222    def open_file(self):
3223        """
3224        open a state file at the start of the application
3225        """
3226        input_file = None
3227        if len(sys.argv) >= 2:
3228            cmd = sys.argv[0].lower()
3229            basename  = os.path.basename(cmd)
3230            app_base = str(APPLICATION_NAME).lower()
3231            if os.path.isfile(cmd) or basename.lower() == app_base:
3232                app_py = app_base + '.py'
3233                app_exe = app_base + '.exe'
3234                app_app = app_base + '.app'
3235                if basename.lower() in [app_py, app_exe, app_app, app_base]:
3236                    data_base = sys.argv[1]
3237                    input_file = os.path.normpath(os.path.join(DATAPATH, 
3238                                                               data_base))
3239        if input_file is None:
3240            return
3241        if self.frame is not None:
3242            self.frame.set_input_file(input_file=input_file)
3243         
3244    def clean_plugin_models(self, path): 
3245        """
3246        Delete plugin models  in app folder
3247       
3248        :param path: path of the plugin_models folder in app
3249        """
3250        # do it only the first time app loaded
3251        # delete unused model folder   
3252        model_folder = os.path.join(PATH_APP, path)
3253        if os.path.exists(model_folder) and os.path.isdir(model_folder):
3254            if len(os.listdir(model_folder)) > 0:
3255                try:
3256                    for file in os.listdir(model_folder):
3257                        file_path = os.path.join(model_folder, file)
3258                        if os.path.isfile(file_path):
3259                            os.remove(file_path)
3260                except:
3261                    logging.error("gui_manager.clean_plugin_models:\n  %s" \
3262                                  % sys.exc_value)
3263             
3264    def set_manager(self, manager):
3265        """
3266        Sets a reference to the application manager
3267        of the GUI manager (Frame)
3268        """
3269        self.frame.set_manager(manager)
3270       
3271    def build_gui(self):
3272        """
3273        Build the GUI
3274        """
3275        #try to load file at the start
3276        try:
3277            self.open_file()
3278        except:
3279            raise
3280        self.frame.build_gui()
3281       
3282    def set_welcome_panel(self, panel_class):
3283        """
3284        Set the welcome panel
3285       
3286        :param panel_class: class of the welcome panel to be instantiated
3287       
3288        """
3289        self.frame.welcome_panel_class = panel_class
3290       
3291    def add_perspective(self, perspective):
3292        """
3293        Manually add a perspective to the application GUI
3294        """
3295        self.frame.add_perspective(perspective)
3296   
3297    def window_placement(self, size):
3298        """
3299        Determines the position and size of the application frame such that it
3300        fits on the user's screen without obstructing (or being obstructed by)
3301        the Windows task bar.  The maximum initial size in pixels is bounded by
3302        WIDTH x HEIGHT.  For most monitors, the application
3303        will be centered on the screen; for very large monitors it will be
3304        placed on the left side of the screen.
3305        """
3306        is_maximized = False
3307        # Get size of screen without
3308        for screenCount in range(wx.Display().GetCount()):
3309            screen = wx.Display(screenCount)
3310            if screen.IsPrimary():
3311                displayRect = screen.GetClientArea()
3312                break
3313       
3314        posX, posY, displayWidth, displayHeight = displayRect       
3315        customWidth, customHeight = size
3316       
3317        # If the custom size is default, set 90% of the screen size
3318        if customWidth <= 0 and customHeight <= 0:
3319            if customWidth == 0 and customHeight == 0:
3320                is_maximized = True
3321            customWidth = displayWidth * 0.9
3322            customHeight = displayHeight * 0.9
3323        else:
3324            # If the custom screen is bigger than the
3325            # window screen than make maximum size
3326            if customWidth > displayWidth:
3327                customWidth = displayWidth
3328            if customHeight > displayHeight:
3329                customHeight = displayHeight
3330           
3331        # Note that when running Linux and using an Xming (X11) server on a PC
3332        # with a dual  monitor configuration, the reported display size may be
3333        # that of both monitors combined with an incorrect display count of 1.
3334        # To avoid displaying this app across both monitors, we check for
3335        # screen 'too big'.  If so, we assume a smaller width which means the
3336        # application will be placed towards the left hand side of the screen.
3337       
3338        # If dual screen registered as 1 screen. Make width half.
3339        # MAC just follows the default behavior of pos
3340        if IS_WIN:
3341            if displayWidth > (displayHeight*2):
3342                if (customWidth == displayWidth):
3343                    customWidth = displayWidth/2
3344                # and set the position to be the corner of the screen.
3345                posX = 0
3346                posY = 0
3347               
3348            # Make the position the middle of the screen. (Not 0,0)
3349            else:
3350                posX = (displayWidth - customWidth)/2
3351                posY = (displayHeight - customHeight)/2
3352        # Return the suggested position and size for the application frame.   
3353        return (posX, posY), (customWidth, customHeight), is_maximized
3354
3355   
3356    def display_splash_screen(self, parent, 
3357                              path=SPLASH_SCREEN_PATH):
3358        """Displays the splash screen.  It will exactly cover the main frame."""
3359       
3360        # Prepare the picture.  On a 2GHz intel cpu, this takes about a second.
3361        image = wx.Image(path, wx.BITMAP_TYPE_PNG)
3362        image.Rescale(SPLASH_SCREEN_WIDTH, 
3363                      SPLASH_SCREEN_HEIGHT, wx.IMAGE_QUALITY_HIGH)
3364        bm = image.ConvertToBitmap()
3365
3366        # Create and show the splash screen.  It will disappear only when the
3367        # program has entered the event loop AND either the timeout has expired
3368        # or the user has left clicked on the screen.  Thus any processing
3369        # performed in this routine (including sleeping) or processing in the
3370        # calling routine (including doing imports) will prevent the splash
3371        # screen from disappearing.
3372        #
3373        # Note that on Linux, the timeout appears to occur immediately in which
3374        # case the splash screen disappears upon entering the event loop.
3375        s_screen = wx.SplashScreen(bitmap=bm,
3376                                   splashStyle=(wx.SPLASH_TIMEOUT|
3377                                                wx.SPLASH_CENTRE_ON_SCREEN),
3378                                   style=(wx.SIMPLE_BORDER|
3379                                          wx.FRAME_NO_TASKBAR|
3380                                          wx.FRAME_FLOAT_ON_PARENT),
3381                                   milliseconds=SS_MAX_DISPLAY_TIME,
3382                                   parent=parent,
3383                                   id=wx.ID_ANY)
3384        from sans.guiframe.gui_statusbar import SPageStatusbar
3385        statusBar = SPageStatusbar(s_screen)
3386        s_screen.SetStatusBar(statusBar)
3387        s_screen.Bind(wx.EVT_CLOSE, self.on_close_splash_screen)
3388        s_screen.Show()
3389        return s_screen
3390       
3391       
3392    def on_close_splash_screen(self, event):
3393        """
3394        When the splash screen is closed.
3395        """
3396        self.frame.Show(True)
3397        event.Skip()
3398        self.maximize_win()
3399
3400
3401class MDIFrame(CHILD_FRAME):
3402    """
3403    Frame for panels
3404    """
3405    def __init__(self, parent, panel, title="Untitled", size=(300,200)):
3406        """
3407        comment
3408        :param parent: parent panel/container
3409        """
3410        # Initialize the Frame object
3411        CHILD_FRAME.__init__(self, parent=parent, id=wx.ID_ANY, title=title, size=size)
3412        self.parent = parent
3413        self.name = "Untitled"
3414        self.batch_on = self.parent.batch_on
3415        self.panel = panel
3416        if panel != None:
3417            self.set_panel(panel)
3418        self.Show(False)
3419   
3420    def show_data_panel(self, action):
3421        """
3422        """
3423        self.parent.show_data_panel(action)
3424       
3425    def set_panel(self, panel):
3426        """
3427        """
3428        self.panel = panel
3429        self.name = panel.window_name
3430        self.SetTitle(panel.window_caption)
3431        self.SetHelpText(panel.help_string)
3432        width, height = self.parent._get_panels_size(panel)
3433        if hasattr(panel, "CENTER_PANE") and panel.CENTER_PANE:
3434            width *= 0.6 
3435        self.SetSize((width, height))
3436        self.parent.put_icon(self)
3437        self.Bind(wx.EVT_SET_FOCUS, self.set_panel_focus)
3438        self.Bind(wx.EVT_CLOSE, self.OnClose)
3439        self.Show(False)
3440   
3441    def set_panel_focus(self, event):
3442        """
3443        """
3444        if self.parent.panel_on_focus != self.panel:
3445            self.panel.SetFocus()
3446            self.parent.panel_on_focus = self.panel
3447       
3448    def OnClose(self, event):
3449        """
3450        On Close event
3451        """
3452        self.panel.on_close(event)
3453       
3454if __name__ == "__main__": 
3455    app = ViewApp(0)
3456    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.