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

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

faster graph titlebar dragging if it is already focused.

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