source: sasview/src/sans/guiframe/gui_manager.py @ 92c6c36

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

Clean up Window menu

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