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

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

Instead of mdi, use wxframe on linux

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