source: sasview/sansguiframe/src/sans/guiframe/gui_manager.py @ 431896cf

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

removed maxsize on mac/linux

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