source: sasview/src/sans/guiframe/gui_manager.py @ d11d1aa

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 d11d1aa was d11d1aa, checked in by Mathieu Doucet <doucetm@…>, 11 years ago

Fix menu order

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