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

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

best main frame size on mac

  • 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 + 1, 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.SetMaxSize((-1, win_height))
1053            else:
1054                self.SetSize((self._window_width, win_height))
1055       
1056    def update_data(self, prev_data, new_data):
1057        """
1058        Update the data.
1059        """
1060        prev_id, data_state = self._data_manager.update_data(
1061                              prev_data=prev_data, new_data=new_data)
1062       
1063        self._data_panel.remove_by_id(prev_id)
1064        self._data_panel.load_data_list(data_state)
1065       
1066    def update_theory(self, data_id, theory, state=None):
1067        """
1068        Update the theory
1069        """ 
1070        data_state = self._data_manager.update_theory(data_id=data_id, 
1071                                         theory=theory,
1072                                         state=state) 
1073        wx.CallAfter(self._data_panel.load_data_list, data_state)
1074       
1075    def onfreeze(self, theory_id):
1076        """
1077        """
1078        data_state_list = self._data_manager.freeze(theory_id)
1079        self._data_panel.load_data_list(list=data_state_list)
1080        for data_state in data_state_list.values():
1081            new_plot = data_state.get_data()
1082           
1083            wx.PostEvent(self, NewPlotEvent(plot=new_plot,
1084                                             title=new_plot.title))
1085       
1086    def freeze(self, data_id, theory_id):
1087        """
1088        """
1089        data_state_list = self._data_manager.freeze_theory(data_id=data_id, 
1090                                                theory_id=theory_id)
1091        self._data_panel.load_data_list(list=data_state_list)
1092        for data_state in data_state_list.values():
1093            new_plot = data_state.get_data()
1094            wx.PostEvent(self, NewPlotEvent(plot=new_plot,
1095                                             title=new_plot.title))
1096       
1097    def delete_data(self, data):
1098        """
1099        Delete the data.
1100        """
1101        self._current_perspective.delete_data(data)
1102       
1103   
1104    def get_context_menu(self, plotpanel=None):
1105        """
1106        Get the context menu items made available
1107        by the different plug-ins.
1108        This function is used by the plotting module
1109        """
1110        if plotpanel is None:
1111            return
1112        menu_list = []
1113        for item in self.plugins:
1114            menu_list.extend(item.get_context_menu(plotpanel=plotpanel))
1115        return menu_list
1116       
1117    def get_current_context_menu(self, plotpanel=None):
1118        """
1119        Get the context menu items made available
1120        by the current plug-in.
1121        This function is used by the plotting module
1122        """
1123        if plotpanel is None:
1124            return
1125        menu_list = []
1126        item = self._current_perspective
1127        if item != None:
1128            menu_list.extend(item.get_context_menu(plotpanel=plotpanel))
1129        return menu_list
1130           
1131    def on_panel_close(self, event):
1132        """
1133        Gets called when the close event for a panel runs.
1134        This will check which panel has been closed and
1135        delete it.
1136        """
1137        frame = event.GetEventObject()
1138        #panel = frame.panel
1139        #if panel.IsMaximized():
1140        #    self._mgr.RestoreMaximizedPane()
1141        for ID in self.plot_panels.keys():
1142            if self.plot_panels[ID].window_name == frame.name:
1143                self.disable_app_menu(self.plot_panels[ID])
1144                self.delete_panel(ID)
1145                break
1146        self.cpanel_on_focus.SetFocus()
1147   
1148   
1149    def popup_panel(self, p):
1150        """
1151        Add a panel object to the AUI manager
1152       
1153        :param p: panel object to add to the AUI manager
1154       
1155        :return: ID of the event associated with the new panel [int]
1156       
1157        """
1158        ID = wx.NewId()
1159        self.panels[str(ID)] = p
1160        ## Check and set the size
1161        if PLOPANEL_WIDTH < 0:
1162            p_panel_width = int(self._window_width * 0.45)
1163        else:
1164            p_panel_width = PLOPANEL_WIDTH
1165        p_panel_height = int(p_panel_width * 0.76)
1166        p.frame.SetSize((p_panel_width, p_panel_height))
1167        """
1168        dw, dh = self._get_panels_size(self._data_panel)
1169        dw += p_panel_width
1170        _, dh = self.GetToolBar().GetSizeTuple()
1171        p.frame.SetPosition((dw, -dh))
1172        """
1173        self.graph_num += 1
1174        if p.window_caption.split()[0] in NOT_SO_GRAPH_LIST:
1175            windowcaption = p.window_caption
1176        else:
1177            windowcaption = 'Graph'#p.window_caption
1178        windowname = p.window_name
1179
1180        # Append nummber
1181        captions = self._get_plotpanel_captions()
1182        while (1):
1183            caption = windowcaption + '%s'% str(self.graph_num)
1184            if caption not in captions:
1185                break
1186            self.graph_num += 1
1187            # protection from forever-loop: max num = 1000
1188            if self.graph_num > 1000:
1189                break
1190        if p.window_caption.split()[0] not in NOT_SO_GRAPH_LIST:
1191            p.window_caption = caption
1192        p.window_name = windowname + str(self.graph_num)
1193       
1194        #style1 = self.__gui_style & GUIFRAME.FIXED_PANEL
1195        #style2 = self.__gui_style & GUIFRAME.FLOATING_PANEL
1196       
1197        p.frame.SetTitle(p.window_caption)
1198        p.frame.name = p.window_name
1199        if not IS_WIN:
1200            p.frame.Center()
1201            x_pos, _ = p.frame.GetPositionTuple()
1202            p.frame.SetPosition((x_pos, 112))
1203        p.frame.Show(True)
1204
1205        # Register for showing/hiding the panel
1206        wx.EVT_MENU(self, ID, self.on_view)
1207        if p not in self.plot_panels.values() and p.group_id != None:
1208            self.plot_panels[ID] = p
1209            if len(self.plot_panels) == 1:
1210                self.panel_on_focus = p
1211                self.set_panel_on_focus(None)
1212            if self._data_panel is not None and \
1213                self._plotting_plugin is not None:
1214                ind = self._data_panel.cb_plotpanel.FindString('None')
1215                if ind != wx.NOT_FOUND:
1216                    self._data_panel.cb_plotpanel.Delete(ind)
1217                if caption not in self._data_panel.cb_plotpanel.GetItems():
1218                    self._data_panel.cb_plotpanel.Append(str(caption), p)
1219        return ID
1220   
1221    def _get_plotpanel_captions(self):
1222        """
1223        Get all the plotpanel cations
1224       
1225        : return: list of captions
1226        """
1227        captions = []
1228        for Id in self.plot_panels.keys():
1229            captions.append(self.plot_panels[Id].window_caption)
1230       
1231        return captions
1232         
1233    def _setup_menus(self):
1234        """
1235        Set up the application menus
1236        """
1237        # Menu
1238        self._menubar = wx.MenuBar()
1239        self._add_menu_file()
1240        self._add_menu_edit()
1241        self._add_menu_view()
1242        #self._add_menu_data()
1243        self._add_menu_application()
1244        self._add_menu_tool()
1245        self._add_current_plugin_menu()
1246        #self._add_menu_window()
1247        self._add_help_menu()
1248        self.SetMenuBar(self._menubar)
1249       
1250    def _setup_tool_bar(self):
1251        """
1252        add toolbar to the frame
1253        """
1254        #set toolbar
1255        self._toolbar = GUIToolBar(self, -1)
1256        self.SetToolBar(self._toolbar)
1257        self._update_toolbar_helper()
1258        self._on_toggle_toolbar(event=None)
1259   
1260    def _update_toolbar_helper(self):
1261        """
1262        Helping to update the toolbar
1263        """
1264        application_name = 'No Selected Analysis'
1265        panel_name = 'No Panel on Focus'
1266        c_panel = self.cpanel_on_focus       
1267        if self._toolbar is  None:
1268            return
1269        if c_panel is not None:
1270            self.reset_bookmark_menu(self.cpanel_on_focus)
1271        if self._current_perspective is not None:
1272            application_name = self._current_perspective.sub_menu
1273        c_panel_state = c_panel
1274        if c_panel is not None:
1275            panel_name = c_panel.window_caption
1276            if not c_panel.IsShownOnScreen():
1277                c_panel_state = None
1278        self._toolbar.update_toolbar(c_panel_state)
1279        self._toolbar.update_button(application_name=application_name, 
1280                                        panel_name=panel_name)
1281        self._toolbar.Realize()
1282       
1283    def _add_menu_tool(self):
1284        """
1285        Tools menu
1286        Go through plug-ins and find tools to populate the tools menu
1287        """
1288        style = self.__gui_style & GUIFRAME.CALCULATOR_ON
1289        if style == GUIFRAME.CALCULATOR_ON:
1290            self._tool_menu = None
1291            for item in self.plugins:
1292                if hasattr(item, "get_tools"):
1293                    for tool in item.get_tools():
1294                        # Only create a menu if we have at least one tool
1295                        if self._tool_menu is None:
1296                            self._tool_menu = wx.Menu()
1297                        if tool[0].lower().count('python') > 0:
1298                            self._tool_menu.AppendSeparator()
1299                        id = wx.NewId()
1300                        self._tool_menu.Append(id, tool[0], tool[1])
1301                        wx.EVT_MENU(self, id, tool[2])
1302            if self._tool_menu is not None:
1303                self._menubar.Append(self._tool_menu, '&Tool')
1304               
1305    def _add_current_plugin_menu(self):
1306        """
1307        add current plugin menu
1308        Look for plug-in menus
1309        Add available plug-in sub-menus.
1310        """
1311        if (self._menubar is None) or (self._current_perspective is None):
1312            return
1313        #replace or add a new menu for the current plugin
1314       
1315        pos = self._menubar.FindMenu(str(self._applications_menu_name))
1316        if pos != -1:
1317            menu_list = self._current_perspective.populate_menu(self)
1318            if menu_list:
1319                for (menu, name) in menu_list:
1320                    hidden_menu = self._menubar.Replace(pos, menu, name) 
1321                    self._applications_menu_name = name
1322                #self._applications_menu_pos = pos
1323            else:
1324                hidden_menu = self._menubar.Remove(pos)
1325                self._applications_menu_name = None
1326            #get the position of the menu when it first added
1327            self._applications_menu_pos = pos
1328           
1329        else:
1330            menu_list = self._current_perspective.populate_menu(self)
1331            if menu_list:
1332                for (menu, name) in menu_list:
1333                    if self._applications_menu_pos == -1:
1334                        self._menubar.Append(menu, name)
1335                    else:
1336                        self._menubar.Insert(self._applications_menu_pos, 
1337                                             menu, name)
1338                    self._applications_menu_name = name
1339                 
1340    def _add_help_menu(self):
1341        """
1342        add help menu
1343        """
1344        # Help menu
1345        self._help_menu = wx.Menu()
1346        style = self.__gui_style & GUIFRAME.WELCOME_PANEL_ON
1347        if style == GUIFRAME.WELCOME_PANEL_ON or custom_config != None:
1348            # add the welcome panel menu item
1349            if config.WELCOME_PANEL_ON and self.defaultPanel is not None:
1350                id = wx.NewId()
1351                self._help_menu.Append(id, '&Welcome', '')
1352                self._help_menu.AppendSeparator()
1353                wx.EVT_MENU(self, id, self.show_welcome_panel)
1354        # Look for help item in plug-ins
1355        for item in self.plugins:
1356            if hasattr(item, "help"):
1357                id = wx.NewId()
1358                self._help_menu.Append(id,'&%s Help' % item.sub_menu, '')
1359                wx.EVT_MENU(self, id, item.help)
1360        if config._do_tutorial and (IS_WIN or sys.platform =='darwin'):
1361            self._help_menu.AppendSeparator()
1362            id = wx.NewId()
1363            self._help_menu.Append(id, '&Tutorial', 'Software tutorial')
1364            wx.EVT_MENU(self, id, self._onTutorial)
1365           
1366        if config._do_aboutbox:
1367            self._help_menu.AppendSeparator()
1368            id = wx.NewId()
1369            self._help_menu.Append(id, '&About', 'Software information')
1370            wx.EVT_MENU(self, id, self._onAbout)
1371       
1372        # Checking for updates
1373        id = wx.NewId()
1374        self._help_menu.Append(id,'&Check for update', 
1375         'Check for the latest version of %s' % config.__appname__)
1376        wx.EVT_MENU(self, id, self._check_update)
1377        self._menubar.Append(self._help_menu, '&Help')
1378           
1379    def _add_menu_view(self):
1380        """
1381        add menu items under view menu
1382        """
1383        if not VIEW_MENU:
1384            return
1385        self._view_menu = wx.Menu()
1386       
1387        id = wx.NewId()
1388        hint = "Display the Grid Window for batch results etc."
1389        self._view_menu.Append(id, '&Show Grid Window', hint) 
1390        wx.EVT_MENU(self, id, self.show_batch_frame)
1391       
1392        self._view_menu.AppendSeparator()
1393        style = self.__gui_style & GUIFRAME.MANAGER_ON
1394        id = wx.NewId()
1395        self._data_panel_menu = self._view_menu.Append(id,
1396                                                '&Show Data Explorer', '')
1397        wx.EVT_MENU(self, id, self.show_data_panel)
1398        if style == GUIFRAME.MANAGER_ON:
1399            self._data_panel_menu.SetText('Hide Data Explorer')
1400        else:
1401            self._data_panel_menu.SetText('Show Data Explorer')
1402 
1403        self._view_menu.AppendSeparator()
1404        id = wx.NewId()
1405        style1 = self.__gui_style & GUIFRAME.TOOLBAR_ON
1406        if style1 == GUIFRAME.TOOLBAR_ON:
1407            self._toolbar_menu = self._view_menu.Append(id, '&Hide Toolbar', '')
1408        else:
1409            self._toolbar_menu = self._view_menu.Append(id, '&Show Toolbar', '')
1410        wx.EVT_MENU(self, id, self._on_toggle_toolbar)
1411
1412        if custom_config != None:
1413            self._view_menu.AppendSeparator()
1414            id = wx.NewId()
1415            hint_ss = "Select the current/default configuration "
1416            hint_ss += "as a startup setting"
1417            preference_menu = self._view_menu.Append(id, 'Startup Setting', 
1418                                                     hint_ss)
1419            wx.EVT_MENU(self, id, self._on_preference_menu)
1420           
1421        id = wx.NewId()
1422        self._view_menu.AppendSeparator()
1423        self._view_menu.Append(id, 'Category Manager', 'Edit model categories')
1424        wx.EVT_MENU(self, id, self._on_category_manager)
1425
1426        self._menubar.Append(self._view_menu, '&View')   
1427         
1428    def show_batch_frame(self, event=None):
1429        """
1430        show the grid of result
1431        """
1432        # Show(False) before Show(True) in order to bring it to the front
1433        self.batch_frame.Show(False)
1434        self.batch_frame.Show(True)
1435   
1436    def  on_category_panel(self, event): 
1437        """
1438        On cat panel
1439        """
1440        self._on_category_manager(event)
1441         
1442    def _on_category_manager(self, event):
1443        """
1444        Category manager frame
1445        """
1446        frame = CategoryManager(self, -1, 'Model Category Manager')
1447        icon = self.GetIcon()
1448        frame.SetIcon(icon)
1449
1450    def _on_preference_menu(self, event):     
1451        """
1452        Build a panel to allow to edit Mask
1453        """
1454       
1455        from sans.guiframe.startup_configuration \
1456        import StartupConfiguration as ConfDialog
1457       
1458        self.panel = ConfDialog(parent=self, gui=self.__gui_style)
1459        self.panel.ShowModal()
1460
1461               
1462    def _add_menu_application(self):
1463        """
1464        # Attach a menu item for each defined perspective or application.
1465        # Only add the perspective menu if there are more than one perspectives
1466        add menu application
1467        """
1468        #style = self.__gui_style & GUIFRAME.MULTIPLE_APPLICATIONS
1469        #if style == GUIFRAME.MULTIPLE_APPLICATIONS:
1470        if self._num_perspectives  > 1:
1471            plug_data_count = False
1472            plug_no_data_count = False
1473            self._applications_menu = wx.Menu()
1474            pos = 0
1475            separator = self._applications_menu.AppendSeparator()
1476            for plug in self.plugins:
1477                if len(plug.get_perspective()) > 0:
1478                    id = wx.NewId()
1479                    if plug.use_data():
1480                       
1481                        self._applications_menu.InsertCheckItem(pos, id, plug.sub_menu,
1482                                      "Switch to analysis: %s" % plug.sub_menu)
1483                        plug_data_count = True
1484                        pos += 1
1485                    else:
1486                        plug_no_data_count = True
1487                        self._applications_menu.AppendCheckItem(id, plug.sub_menu,
1488                                      "Switch to analysis: %s" % plug.sub_menu)
1489                    wx.EVT_MENU(self, id, plug.on_perspective)
1490
1491            #self._applications_menu.
1492            if (not plug_data_count or not plug_no_data_count):
1493                self._applications_menu.RemoveItem(separator)
1494            self._menubar.Append(self._applications_menu, '&Analysis')
1495            self._check_applications_menu()
1496           
1497    def _populate_file_menu(self):
1498        """
1499        Insert menu item under file menu
1500        """
1501        for plugin in self.plugins:
1502            if len(plugin.populate_file_menu()) > 0:
1503                for item in plugin.populate_file_menu():
1504                    m_name, m_hint, m_handler = item
1505                    id = wx.NewId()
1506                    self._file_menu.Append(id, m_name, m_hint)
1507                    wx.EVT_MENU(self, id, m_handler)
1508                self._file_menu.AppendSeparator()
1509               
1510    def _add_menu_file(self):
1511        """
1512        add menu file
1513        """
1514       
1515        # File menu
1516        self._file_menu = wx.Menu()
1517        #append item from plugin under menu file if necessary
1518        self._populate_file_menu()
1519        style1 = self.__gui_style & GUIFRAME.MULTIPLE_APPLICATIONS
1520        if OPEN_SAVE_MENU:
1521            id = wx.NewId()
1522            hint_load_file = "read all analysis states saved previously"
1523            self._save_appl_menu = self._file_menu.Append(id, 
1524                                    '&Open Project', hint_load_file)
1525            wx.EVT_MENU(self, id, self._on_open_state_project)
1526           
1527        if style1 == GUIFRAME.MULTIPLE_APPLICATIONS:
1528            # some menu of plugin to be seen under file menu
1529            hint_load_file = "Read a status files and load"
1530            hint_load_file += " them into the analysis"
1531            id = wx.NewId()
1532            self._save_appl_menu = self._file_menu.Append(id, 
1533                                    '&Open Analysis', hint_load_file)
1534            wx.EVT_MENU(self, id, self._on_open_state_application)
1535        if OPEN_SAVE_MENU:       
1536            self._file_menu.AppendSeparator()
1537            id = wx.NewId()
1538            self._file_menu.Append(id, '&Save Project',
1539                                 'Save the state of the whole analysis')
1540            wx.EVT_MENU(self, id, self._on_save_project)
1541        if style1 == GUIFRAME.MULTIPLE_APPLICATIONS:
1542            #self._file_menu.AppendSeparator()
1543            id = wx.NewId()
1544            self._save_appl_menu = self._file_menu.Append(id, 
1545                                                      '&Save Analysis',
1546                        'Save state of the current active analysis panel')
1547            wx.EVT_MENU(self, id, self._on_save_application)
1548            self._file_menu.AppendSeparator()
1549       
1550        id = wx.NewId()
1551        self._file_menu.Append(id, '&Quit', 'Exit') 
1552        wx.EVT_MENU(self, id, self.Close)
1553        # Add sub menus
1554        self._menubar.Append(self._file_menu, '&File')
1555       
1556    def _add_menu_edit(self):
1557        """
1558        add menu edit
1559        """
1560        if not EDIT_MENU:
1561            return
1562        # Edit Menu
1563        self._edit_menu = wx.Menu()
1564        self._edit_menu.Append(GUIFRAME_ID.UNDO_ID, '&Undo', 
1565                               'Undo the previous action')
1566        wx.EVT_MENU(self, GUIFRAME_ID.UNDO_ID, self.on_undo_panel)
1567        self._edit_menu.Append(GUIFRAME_ID.REDO_ID, '&Redo', 
1568                               'Redo the previous action')
1569        wx.EVT_MENU(self, GUIFRAME_ID.REDO_ID, self.on_redo_panel)
1570        self._edit_menu.AppendSeparator()
1571        self._edit_menu.Append(GUIFRAME_ID.COPY_ID, '&Copy Params', 
1572                               'Copy parameter values')
1573        wx.EVT_MENU(self, GUIFRAME_ID.COPY_ID, self.on_copy_panel)
1574        self._edit_menu.Append(GUIFRAME_ID.PASTE_ID, '&Paste Params', 
1575                               'Paste parameter values')
1576        wx.EVT_MENU(self, GUIFRAME_ID.PASTE_ID, self.on_paste_panel)
1577        self._edit_menu.AppendSeparator()
1578       
1579        self._edit_menu.Append(GUIFRAME_ID.PREVIEW_ID, '&Report Results',
1580                               'Preview current panel')
1581        wx.EVT_MENU(self, GUIFRAME_ID.PREVIEW_ID, self.on_preview_panel)
1582
1583        self._edit_menu.Append(GUIFRAME_ID.RESET_ID, '&Reset Page', 
1584                               'Reset current panel')
1585        wx.EVT_MENU(self, GUIFRAME_ID.RESET_ID, self.on_reset_panel)
1586   
1587        self._menubar.Append(self._edit_menu,  '&Edit')
1588        self.enable_edit_menu()
1589       
1590    def get_style(self):
1591        """
1592        Return the gui style
1593        """
1594        return  self.__gui_style
1595   
1596    def _add_menu_data(self):
1597        """
1598        Add menu item item data to menu bar
1599        """
1600        if self._data_plugin is not None:
1601            menu_list = self._data_plugin.populate_menu(self)
1602            if menu_list:
1603                for (menu, name) in menu_list:
1604                    self._menubar.Append(menu, name)
1605       
1606                       
1607    def _on_toggle_toolbar(self, event=None):
1608        """
1609        hide or show toolbar
1610        """
1611        if self._toolbar is None:
1612            return
1613        if self._toolbar.IsShown():
1614            if self._toolbar_menu is not None:
1615                self._toolbar_menu.SetItemLabel('Show Toolbar')
1616            self._toolbar.Hide()
1617        else:
1618            if self._toolbar_menu is not None:
1619                self._toolbar_menu.SetItemLabel('Hide Toolbar')
1620            self._toolbar.Show()
1621        self._toolbar.Realize()
1622       
1623    def _on_status_event(self, evt):
1624        """
1625        Display status message
1626        """
1627        # This CallAfter fixes many crashes on MAC.
1628        wx.CallAfter(self.sb.set_status, evt)
1629       
1630    def on_view(self, evt):
1631        """
1632        A panel was selected to be shown. If it's not already
1633        shown, display it.
1634       
1635        :param evt: menu event
1636       
1637        """
1638        panel_id = str(evt.GetId())
1639        self.on_set_plot_focus(self.panels[panel_id])
1640        self.show_panel(evt.GetId(), 'on')     
1641        wx.CallLater(5*TIME_FACTOR, self.set_schedule(True))
1642        self.set_plot_unfocus()
1643 
1644    def show_welcome_panel(self, event):
1645        """   
1646        Display the welcome panel
1647        """
1648        if self.defaultPanel is None:
1649            return 
1650        frame = self.panels['default'].get_frame()
1651        if frame == None:
1652            return
1653        # Show default panel
1654        if not frame.IsShown():
1655            frame.Show(True)
1656           
1657    def on_close_welcome_panel(self):
1658        """
1659        Close the welcome panel
1660        """
1661        if self.defaultPanel is None:
1662            return 
1663        default_panel = self.panels["default"].frame
1664        if default_panel.IsShown():
1665            default_panel.Show(False) 
1666               
1667    def delete_panel(self, uid):
1668        """
1669        delete panel given uid
1670        """
1671        ID = str(uid)
1672        config.printEVT("delete_panel: %s" % ID)
1673        try:
1674            caption = self.panels[ID].window_caption
1675        except:
1676            print "delete_panel: No such plot id as %s" % ID
1677            return
1678        if ID in self.panels.keys():
1679            self.panel_on_focus = None
1680            panel = self.panels[ID]
1681            #self._mgr.DetachPane(panel)
1682            if hasattr(panel, "connect"):
1683                panel.connect.disconnect()
1684            self._plotting_plugin.delete_panel(panel.group_id)
1685            #panel.Hide()
1686            #panel.clear()
1687            #panel.Close()
1688            if panel in self.schedule_full_draw_list:
1689                self.schedule_full_draw_list.remove(panel) 
1690           
1691            #delete uid number not str(uid)
1692            if ID in self.plot_panels.keys():
1693                del self.plot_panels[ID]
1694            if ID in self.panels.keys():
1695                del self.panels[ID]
1696            return 
1697           
1698    def create_gui_data(self, data, path=None):
1699        """
1700        """
1701        return self._data_manager.create_gui_data(data, path)
1702   
1703    def get_data(self, path):
1704        """
1705        """
1706        message = ""
1707        log_msg = ''
1708        output = []
1709        error_message = ""
1710        basename  = os.path.basename(path)
1711        root, extension = os.path.splitext(basename)
1712        if extension.lower() not in EXTENSIONS:
1713            log_msg = "File Loader cannot "
1714            log_msg += "load: %s\n" % str(basename)
1715            log_msg += "Try Data opening...."
1716            logging.info(log_msg)
1717            print log_msg
1718            #self.load_complete(output=output, error_message=error_message,
1719            #       message=log_msg, path=path)   
1720            return
1721       
1722        #reading a state file
1723        for plug in self.plugins:
1724            reader, ext = plug.get_extensions()
1725            if reader is not None:
1726                #read the state of the single plugin
1727                if extension == ext:
1728                    reader.read(path)
1729                    return
1730                elif extension == APPLICATION_STATE_EXTENSION:
1731                    try:
1732                        reader.read(path)
1733                    except:
1734                        msg = "DataLoader Error: Encounted Non-ASCII character"
1735                        msg += "\n(%s)"% sys.exc_value
1736                        wx.PostEvent(self, StatusEvent(status=msg, 
1737                                                info="error", type="stop"))
1738                        return
1739       
1740        style = self.__gui_style & GUIFRAME.MANAGER_ON
1741        if style == GUIFRAME.MANAGER_ON:
1742            if self._data_panel is not None:
1743                self._data_panel.frame.Show(True)
1744     
1745    def load_from_cmd(self,  path):   
1746        """
1747        load data from cmd or application
1748        """ 
1749        if path is None:
1750            return
1751        else:
1752            path = os.path.abspath(path)
1753            if not os.path.isfile(path) and not os.path.isdir(path):
1754                return
1755           
1756            if os.path.isdir(path):
1757                self.load_folder(path)
1758                return
1759
1760        basename  = os.path.basename(path)
1761        root, extension = os.path.splitext(basename)
1762        if extension.lower() not in EXTENSIONS:
1763            self.load_data(path)
1764        else:
1765            self.load_state(path)
1766
1767        self._default_save_location = os.path.dirname(path)
1768       
1769    def show_panel(self, uid, show=None):
1770        """
1771        Shows the panel with the given id
1772       
1773        :param uid: unique ID number of the panel to show
1774       
1775        """
1776        #Not implemeted
1777        return
1778       
1779    def load_state(self, path, is_project=False):   
1780        """
1781        load data from command line or application
1782        """
1783        if path and (path is not None) and os.path.isfile(path):
1784            basename  = os.path.basename(path)
1785            if APPLICATION_STATE_EXTENSION is not None \
1786                and basename.endswith(APPLICATION_STATE_EXTENSION):
1787                if is_project:
1788                    for ID in self.plot_panels.keys():
1789                        panel = self.plot_panels[ID]
1790                        panel.on_close(None)
1791            self.get_data(path)
1792            wx.PostEvent(self, StatusEvent(status="Completed loading."))
1793        else:
1794            wx.PostEvent(self, StatusEvent(status=" "))
1795           
1796    def load_data(self, path):
1797        """
1798        load data from command line
1799        """
1800        if not os.path.isfile(path):
1801            return
1802        basename  = os.path.basename(path)
1803        root, extension = os.path.splitext(basename)
1804        if extension.lower() in EXTENSIONS:
1805            log_msg = "Data Loader cannot "
1806            log_msg += "load: %s\n" % str(path)
1807            log_msg += "Try File opening ...."
1808            print log_msg
1809            return
1810        log_msg = ''
1811        output = {}
1812        error_message = ""
1813        try:
1814            print "Loading Data...:\n" + str(path) + "\n"
1815            temp =  self.loader.load(path)
1816            if temp.__class__.__name__ == "list":
1817                for item in temp:
1818                    data = self.create_gui_data(item, path)
1819                    output[data.id] = data
1820            else:
1821                data = self.create_gui_data(temp, path)
1822                output[data.id] = data
1823           
1824            self.add_data(data_list=output)
1825        except:
1826            error_message = "Error while loading"
1827            error_message += " Data from cmd:\n %s\n" % str(path)
1828            error_message += str(sys.exc_value) + "\n"
1829            print error_message
1830 
1831    def load_folder(self, path):
1832        """
1833        Load entire folder
1834        """   
1835        if not os.path.isdir(path):
1836            return
1837        if self._data_plugin is None:
1838            return
1839        try:
1840            if path is not None:
1841                self._default_save_location = os.path.dirname(path)
1842                file_list = self._data_plugin.get_file_path(path)
1843                self._data_plugin.get_data(file_list)
1844            else:
1845                return 
1846        except:
1847            error_message = "Error while loading"
1848            error_message += " Data folder from cmd:\n %s\n" % str(path)
1849            error_message += str(sys.exc_value) + "\n"
1850            print error_message
1851           
1852    def _on_open_state_application(self, event):
1853        """
1854        """
1855        path = None
1856        if self._default_save_location == None:
1857            self._default_save_location = os.getcwd()
1858        wx.PostEvent(self, StatusEvent(status="Loading Analysis file..."))
1859        plug_wlist = self._on_open_state_app_helper()
1860        dlg = wx.FileDialog(self, 
1861                            "Choose a file", 
1862                            self._default_save_location, "",
1863                            plug_wlist)
1864        if dlg.ShowModal() == wx.ID_OK:
1865            path = dlg.GetPath()
1866            if path is not None:
1867                self._default_save_location = os.path.dirname(path)
1868        dlg.Destroy()
1869        self.load_state(path=path) 
1870   
1871    def _on_open_state_app_helper(self):
1872        """
1873        Helps '_on_open_state_application()' to find the extension of
1874        the current perspective/application
1875        """
1876        # No current perspective or no extension attr
1877        if self._current_perspective is None:
1878            return PLUGINS_WLIST
1879        try:
1880            # Find the extension of the perspective
1881            # and get that as 1st item in list
1882            ind = None
1883            app_ext = self._current_perspective._extensions
1884            plug_wlist = config.PLUGINS_WLIST
1885            for ext in set(plug_wlist):
1886                if ext.count(app_ext) > 0:
1887                    ind = ext
1888                    break
1889            # Found the extension
1890            if ind != None:
1891                plug_wlist.remove(ind)
1892                plug_wlist.insert(0, ind)
1893                try:
1894                    plug_wlist = '|'.join(plug_wlist)
1895                except:
1896                    plug_wlist = ''
1897
1898        except:
1899            plug_wlist = PLUGINS_WLIST
1900           
1901        return plug_wlist
1902           
1903    def _on_open_state_project(self, event):
1904        """
1905        """
1906        path = None
1907        if self._default_save_location == None:
1908            self._default_save_location = os.getcwd()
1909        wx.PostEvent(self, StatusEvent(status="Loading Project file..."))
1910        dlg = wx.FileDialog(self, 
1911                            "Choose a file", 
1912                            self._default_save_location, "",
1913                             APPLICATION_WLIST)
1914        if dlg.ShowModal() == wx.ID_OK:
1915            path = dlg.GetPath()
1916            if path is not None:
1917                self._default_save_location = os.path.dirname(path)
1918        dlg.Destroy()
1919       
1920        self.load_state(path=path, is_project=True)
1921       
1922    def _on_save_application(self, event):
1923        """
1924        save the state of the current active application
1925        """
1926        if self.cpanel_on_focus is not None:
1927            try:
1928                wx.PostEvent(self, 
1929                             StatusEvent(status="Saving Analysis file..."))
1930                self.cpanel_on_focus.on_save(event)
1931                wx.PostEvent(self, 
1932                             StatusEvent(status="Completed saving."))
1933            except:
1934                msg = "Error occurred while saving: "
1935                msg += "To save, the application panel should have a data set.."
1936                wx.PostEvent(self, StatusEvent(status=msg)) 
1937           
1938    def _on_save_project(self, event):
1939        """
1940        save the state of the SasView as *.svs
1941        """
1942        if self._current_perspective is  None:
1943            return
1944        wx.PostEvent(self, StatusEvent(status="Saving Project file..."))
1945        reader, ext = self._current_perspective.get_extensions()
1946        path = None
1947        extension = '*' + APPLICATION_STATE_EXTENSION
1948        dlg = wx.FileDialog(self, "Save Project file",
1949                            self._default_save_location, "sasview_proj",
1950                             extension, 
1951                             wx.SAVE)
1952        if dlg.ShowModal() == wx.ID_OK:
1953            path = dlg.GetPath()
1954            self._default_save_location = os.path.dirname(path)
1955        else:
1956            return None
1957        dlg.Destroy()
1958        try:
1959            if path is None:
1960                return
1961            # default cansas xml doc
1962            doc = None
1963            for panel in self.panels.values():
1964                temp = panel.save_project(doc)
1965                if temp is not None:
1966                    doc = temp
1967             
1968            # Write the XML document
1969            extens = APPLICATION_STATE_EXTENSION
1970            fName = os.path.splitext(path)[0] + extens
1971            if doc != None:
1972                fd = open(fName, 'w')
1973                fd.write(doc.toprettyxml())
1974                fd.close()
1975                wx.PostEvent(self, StatusEvent(status="Completed Saving."))
1976            else:
1977                msg = "%s cannot read %s\n" % (str(APPLICATION_NAME), str(path))
1978                logging.error(msg)
1979        except:
1980            msg = "Error occurred while saving: "
1981            msg += "To save, at leat one application panel "
1982            msg += "should have a data set.."
1983            wx.PostEvent(self, StatusEvent(status=msg))   
1984                   
1985    def on_save_helper(self, doc, reader, panel, path):
1986        """
1987        Save state into a file
1988        """
1989        try:
1990            if reader is not None:
1991                # case of a panel with multi-pages
1992                if hasattr(panel, "opened_pages"):
1993                    for uid, page in panel.opened_pages.iteritems():
1994                        data = page.get_data()
1995                        # state must be cloned
1996                        state = page.get_state().clone()
1997                        if data is not None:
1998                            new_doc = reader.write_toXML(data, state)
1999                            if doc != None and hasattr(doc, "firstChild"):
2000                                child = new_doc.firstChild.firstChild
2001                                doc.firstChild.appendChild(child) 
2002                            else:
2003                                doc = new_doc
2004                # case of only a panel
2005                else:
2006                    data = panel.get_data()
2007                    state = panel.get_state()
2008                    if data is not None:
2009                        new_doc = reader.write_toXML(data, state)
2010                        if doc != None and hasattr(doc, "firstChild"):
2011                            child = new_doc.firstChild.firstChild
2012                            doc.firstChild.appendChild(child) 
2013                        else:
2014                            doc = new_doc
2015        except: 
2016            raise
2017            #pass
2018
2019        return doc
2020
2021    def quit_guiframe(self):
2022        """
2023        Pop up message to make sure the user wants to quit the application
2024        """
2025        message = "\nDo you really want to exit this application?        \n\n"
2026        dial = wx.MessageDialog(self, message, 'Confirm Exit',
2027                           wx.YES_NO|wx.YES_DEFAULT|wx.ICON_QUESTION)
2028        if dial.ShowModal() == wx.ID_YES:
2029            return True
2030        else:
2031            return False   
2032       
2033    def WindowClose(self, event=None):
2034        """
2035        Quit the application from x icon
2036        """
2037        flag = self.quit_guiframe()
2038        if flag:
2039            _pylab_helpers.Gcf.figs = {}
2040            self.Close()
2041           
2042    def Close(self, event=None):
2043        """
2044        Quit the application
2045        """
2046        wx.Exit()
2047        sys.exit()
2048           
2049    def _check_update(self, event=None): 
2050        """
2051        Check with the deployment server whether a new version
2052        of the application is available.
2053        A thread is started for the connecting with the server. The thread calls
2054        a call-back method when the current version number has been obtained.
2055        """
2056        try:
2057            conn = httplib.HTTPConnection(config.__update_URL__[0], 
2058                              timeout=3.0)
2059            conn.request("GET", config.__update_URL__[1])
2060            res = conn.getresponse()
2061            content = res.read()
2062            conn.close()
2063            #f=urllib2.urlopen(config.__update_URL__,
2064            #                  timeout=1.0)
2065            #content = f.read()
2066        except:
2067            content = "0.0.0"
2068       
2069        version = content.strip()
2070        if len(re.findall('\d+\.\d+\.\d+$', version)) < 0:
2071            content = "0.0.0"
2072        self._process_version(content, standalone=event==None)
2073   
2074    def _process_version(self, version, standalone=True):
2075        """
2076        Call-back method for the process of checking for updates.
2077        This methods is called by a VersionThread object once the current
2078        version number has been obtained. If the check is being done in the
2079        background, the user will not be notified unless there's an update.
2080       
2081        :param version: version string
2082        :param standalone: True of the update is being checked in
2083           the background, False otherwise.
2084           
2085        """
2086        try:
2087            if version == "0.0.0":
2088                msg = "Could not connect to the application server."
2089                msg += " Please try again later."
2090                self.SetStatusText(msg)
2091            elif cmp(version, config.__version__) > 0:
2092                msg = "Version %s is available! " % str(version)
2093                if not standalone:
2094                    import webbrowser
2095                    webbrowser.open(config.__download_page__)
2096                else:
2097                    msg +=  "See the help menu to download it." 
2098                self.SetStatusText(msg)
2099            else:
2100                if not standalone:
2101                    msg = "You have the latest version"
2102                    msg += " of %s" % str(config.__appname__)
2103                    self.SetStatusText(msg)
2104        except:
2105            msg = "guiframe: could not get latest application"
2106            msg += " version number\n  %s" % sys.exc_value
2107            logging.error(msg)
2108            if not standalone:
2109                msg = "Could not connect to the application server."
2110                msg += " Please try again later."
2111                self.SetStatusText(msg)
2112                   
2113    def _onAbout(self, evt):
2114        """
2115        Pop up the about dialog
2116       
2117        :param evt: menu event
2118       
2119        """
2120        if config._do_aboutbox:
2121            import sans.guiframe.aboutbox as AboutBox 
2122            dialog = AboutBox.DialogAbout(None, -1, "")
2123            dialog.ShowModal()   
2124                     
2125    def _onTutorial(self, evt):
2126        """
2127        Pop up the tutorial dialog
2128       
2129        :param evt: menu event
2130       
2131        """
2132        if config._do_tutorial:   
2133            path = config.TUTORIAL_PATH
2134            if IS_WIN:
2135                try:
2136                    from sans.guiframe.pdfview import PDFFrame
2137                    dialog = PDFFrame(None, -1, "Tutorial", path)
2138                    # put icon
2139                    self.put_icon(dialog) 
2140                    dialog.Show(True) 
2141                except:
2142                    print "Error in _onTutorial: %s" % sys.exc_value
2143                    try:
2144                        #in case when the pdf default set other than acrobat
2145                        import ho.pisa as pisa
2146                        pisa.startViewer(path)
2147                    except:
2148                        msg = "This feature requires 'PDF Viewer'\n"
2149                        msg += "Please install it first (Free)..."
2150                        wx.MessageBox(msg, 'Error')
2151            else:
2152                try:
2153                    command = "open '%s'" % path
2154                    os.system(command)
2155                except:
2156                    try:
2157                        #in case when the pdf default set other than preview
2158                        import ho.pisa as pisa
2159                        pisa.startViewer(path)
2160                    except:
2161                        msg = "This feature requires 'Preview' Application\n"
2162                        msg += "Please install it first..."
2163                        wx.MessageBox(msg, 'Error')
2164
2165                     
2166    def set_manager(self, manager):
2167        """
2168        Sets the application manager for this frame
2169       
2170        :param manager: frame manager
2171        """
2172        self.app_manager = manager
2173       
2174    def post_init(self):
2175        """
2176        This initialization method is called after the GUI
2177        has been created and all plug-ins loaded. It calls
2178        the post_init() method of each plug-in (if it exists)
2179        so that final initialization can be done.
2180        """
2181        for item in self.plugins:
2182            if hasattr(item, "post_init"):
2183                item.post_init()
2184       
2185    def set_default_perspective(self):
2186        """
2187        Choose among the plugin the first plug-in that has
2188        "set_default_perspective" method and its return value is True will be
2189        as a default perspective when the welcome page is closed
2190        """
2191        for item in self.plugins:
2192            if hasattr(item, "set_default_perspective"):
2193                if item.set_default_perspective():
2194                    item.on_perspective(event=None)
2195                    return 
2196       
2197    def set_perspective(self, panels):
2198        """
2199        Sets the perspective of the GUI.
2200        Opens all the panels in the list, and closes
2201        all the others.
2202       
2203        :param panels: list of panels
2204        """
2205        for item in self.panels.keys():
2206            # Check whether this is a sticky panel
2207            if hasattr(self.panels[item], "ALWAYS_ON"):
2208                if self.panels[item].ALWAYS_ON:
2209                    continue 
2210            if self.panels[item] == None:
2211                continue
2212            if self.panels[item].window_name in panels:
2213                frame = self.panels[item].get_frame()
2214                if not frame.IsShown():
2215                    frame.Show(True)
2216            else:
2217                # always show the data panel if enable
2218                style = self.__gui_style & GUIFRAME.MANAGER_ON
2219                if (style == GUIFRAME.MANAGER_ON) and self.panels[item] == self._data_panel:
2220                    if 'data_panel' in self.panels.keys():
2221                        frame = self.panels['data_panel'].get_frame()
2222                        if frame == None:
2223                            continue
2224                        flag = frame.IsShown()
2225                        frame.Show(flag)
2226                else:
2227                    frame = self.panels[item].get_frame()
2228                    if frame == None:
2229                        continue
2230
2231                    if frame.IsShown():
2232                        frame.Show(False)
2233       
2234    def show_data_panel(self, event=None, action=True):
2235        """
2236        show the data panel
2237        """
2238        if self._data_panel_menu == None:
2239            return
2240        label = self._data_panel_menu.GetText()
2241        pane = self.panels["data_panel"]
2242        frame = pane.get_frame()
2243        if label == 'Show Data Explorer':
2244            #if not pane.IsShown():
2245            if action: 
2246                frame.Show(True)
2247            self.__gui_style = self.__gui_style | GUIFRAME.MANAGER_ON
2248            self._data_panel_menu.SetText('Hide Data Explorer')
2249        else:
2250            if action:
2251                frame.Show(False)
2252            self.__gui_style = self.__gui_style & (~GUIFRAME.MANAGER_ON)
2253            self._data_panel_menu.SetText('Show Data Explorer')
2254
2255    def add_data_helper(self, data_list):
2256        """
2257        """
2258        if self._data_manager is not None:
2259            self._data_manager.add_data(data_list)
2260       
2261    def add_data(self, data_list):
2262        """
2263        receive a dictionary of data from loader
2264        store them its data manager if possible
2265        send to data the current active perspective if the data panel
2266        is not active.
2267        :param data_list: dictionary of data's ID and value Data
2268        """
2269        #Store data into manager
2270        self.add_data_helper(data_list)
2271        # set data in the data panel
2272        if self._data_panel is not None:
2273            data_state = self._data_manager.get_data_state(data_list.keys())
2274            self._data_panel.load_data_list(data_state)
2275        #if the data panel is shown wait for the user to press a button
2276        #to send data to the current perspective. if the panel is not
2277        #show  automatically send the data to the current perspective
2278        style = self.__gui_style & GUIFRAME.MANAGER_ON
2279        if style == GUIFRAME.MANAGER_ON:
2280            #wait for button press from the data panel to set_data
2281            if self._data_panel is not None:
2282                self._data_panel.frame.Show(True)
2283        else:
2284            #automatically send that to the current perspective
2285            self.set_data(data_id=data_list.keys())
2286       
2287    def set_data(self, data_id, theory_id=None): 
2288        """
2289        set data to current perspective
2290        """
2291        list_data, _ = self._data_manager.get_by_id(data_id)
2292        if self._current_perspective is not None:
2293            self._current_perspective.set_data(list_data.values())
2294
2295        else:
2296            msg = "Guiframe does not have a current perspective"
2297            logging.info(msg)
2298           
2299    def set_theory(self, state_id, theory_id=None):
2300        """
2301        """
2302        _, list_theory = self._data_manager.get_by_id(theory_id)
2303        if self._current_perspective is not None:
2304            try:
2305                self._current_perspective.set_theory(list_theory.values())
2306            except:
2307                msg = "Guiframe set_theory: \n" + str(sys.exc_value)
2308                logging.info(msg)
2309                wx.PostEvent(self, StatusEvent(status=msg, info="error"))
2310        else:
2311            msg = "Guiframe does not have a current perspective"
2312            logging.info(msg)
2313           
2314    def plot_data(self,  state_id, data_id=None,
2315                  theory_id=None, append=False):
2316        """
2317        send a list of data to plot
2318        """
2319        total_plot_list = []
2320        data_list, _ = self._data_manager.get_by_id(data_id)
2321        _, temp_list_theory = self._data_manager.get_by_id(theory_id)
2322        total_plot_list = data_list.values()
2323        for item in temp_list_theory.values():
2324            theory_data, theory_state = item
2325            total_plot_list.append(theory_data)
2326        GROUP_ID = wx.NewId()
2327        for new_plot in total_plot_list:
2328            if append:
2329                if self.panel_on_focus is None:
2330                    message = "cannot append plot. No plot panel on focus!"
2331                    message += "please click on any available plot to set focus"
2332                    wx.PostEvent(self, StatusEvent(status=message, 
2333                                                   info='warning'))
2334                    return 
2335                else:
2336                    if self.enable_add_data(new_plot):
2337                        new_plot.group_id = self.panel_on_focus.group_id
2338                    else:
2339                        message = "Only 1D Data can be append to"
2340                        message += " plot panel containing 1D data.\n"
2341                        message += "%s not be appended.\n" %str(new_plot.name)
2342                        message += "try new plot option.\n"
2343                        wx.PostEvent(self, StatusEvent(status=message, 
2344                                                   info='warning'))
2345            else:
2346                #if not append then new plot
2347                from sans.guiframe.dataFitting import Data2D
2348                if issubclass(Data2D, new_plot.__class__):
2349                    #for 2 D always plot in a separated new plot
2350                    new_plot.group_id = wx.NewId()
2351                else:
2352                    # plot all 1D in a new plot
2353                    new_plot.group_id = GROUP_ID
2354            title = "PLOT " + str(new_plot.title)
2355            wx.PostEvent(self, NewPlotEvent(plot=new_plot,
2356                                                  title=title,
2357                                                  group_id = new_plot.group_id))
2358           
2359    def remove_data(self, data_id, theory_id=None):
2360        """
2361        Delete data state if data_id is provide
2362        delete theory created with data of id data_id if theory_id is provide
2363        if delete all true: delete the all state
2364        else delete theory
2365        """
2366        temp = data_id + theory_id
2367        for plug in self.plugins:
2368            plug.delete_data(temp)
2369        total_plot_list = []
2370        data_list, _ = self._data_manager.get_by_id(data_id)
2371        _, temp_list_theory = self._data_manager.get_by_id(theory_id)
2372        total_plot_list = data_list.values()
2373        for item in temp_list_theory.values():
2374            theory_data, theory_state = item
2375            total_plot_list.append(theory_data)
2376        for new_plot in total_plot_list:
2377            id = new_plot.id
2378            for group_id in new_plot.list_group_id:
2379                wx.PostEvent(self, NewPlotEvent(id=id,
2380                                                   group_id=group_id,
2381                                                   action='remove'))
2382                #remove res plot: Todo: improve
2383                wx.CallAfter(self._remove_res_plot, id)
2384        self._data_manager.delete_data(data_id=data_id, 
2385                                       theory_id=theory_id)
2386       
2387    def _remove_res_plot(self, id):
2388        """
2389        Try to remove corresponding res plot
2390       
2391        : param id: id of the data
2392        """
2393        try:
2394            wx.PostEvent(self, NewPlotEvent(id=("res"+str(id)),
2395                                           group_id=("res"+str(id)),
2396                                           action='remove'))
2397        except:
2398            pass
2399   
2400    def save_data1d(self, data, fname):
2401        """
2402        Save data dialog
2403        """
2404        default_name = fname
2405        wildcard = "Text files (*.txt)|*.txt|"\
2406                    "CanSAS 1D files(*.xml)|*.xml" 
2407        path = None
2408        dlg = wx.FileDialog(self, "Choose a file",
2409                            self._default_save_location,
2410                            default_name, wildcard , wx.SAVE)
2411       
2412        if dlg.ShowModal() == wx.ID_OK:
2413            path = dlg.GetPath()
2414            # ext_num = 0 for .txt, ext_num = 1 for .xml
2415            # This is MAC Fix
2416            ext_num = dlg.GetFilterIndex()
2417            if ext_num == 0:
2418                format = '.txt'
2419            else:
2420                format = '.xml'
2421            path = os.path.splitext(path)[0] + format
2422            mypath = os.path.basename(path)
2423           
2424            #TODO: This is bad design. The DataLoader is designed
2425            #to recognize extensions.
2426            # It should be a simple matter of calling the .
2427            #save(file, data, '.xml') method
2428            # of the sans.dataloader.loader.Loader class.
2429            from sans.dataloader.loader import  Loader
2430            #Instantiate a loader
2431            loader = Loader() 
2432            format = ".txt"
2433            if os.path.splitext(mypath)[1].lower() == format:
2434                # Make sure the ext included in the file name
2435                # especially on MAC
2436                fName = os.path.splitext(path)[0] + format
2437                self._onsaveTXT(data, fName)
2438            format = ".xml"
2439            if os.path.splitext(mypath)[1].lower() == format:
2440                # Make sure the ext included in the file name
2441                # especially on MAC
2442                fName = os.path.splitext(path)[0] + format
2443                loader.save(fName, data, format)
2444            try:
2445                self._default_save_location = os.path.dirname(path)
2446            except:
2447                pass   
2448        dlg.Destroy()
2449       
2450       
2451    def _onsaveTXT(self, data, path):
2452        """
2453        Save file as txt 
2454        :TODO: Refactor and remove this method. See TODO in _onSave.
2455        """
2456        if not path == None:
2457            out = open(path, 'w')
2458            has_errors = True
2459            if data.dy == None or data.dy == []:
2460                has_errors = False
2461            # Sanity check
2462            if has_errors:
2463                try:
2464                    if len(data.y) != len(data.dy):
2465                        has_errors = False
2466                except:
2467                    has_errors = False
2468            if has_errors:
2469                if data.dx != None and data.dx != []:
2470                    out.write("<X>   <Y>   <dY>   <dX>\n")
2471                else:
2472                    out.write("<X>   <Y>   <dY>\n")
2473            else:
2474                out.write("<X>   <Y>\n")
2475               
2476            for i in range(len(data.x)):
2477                if has_errors:
2478                    if data.dx != None and data.dx != []:
2479                        if  data.dx[i] != None:
2480                            out.write("%g  %g  %g  %g\n" % (data.x[i], 
2481                                                        data.y[i],
2482                                                        data.dy[i],
2483                                                        data.dx[i]))
2484                        else:
2485                            out.write("%g  %g  %g\n" % (data.x[i], 
2486                                                        data.y[i],
2487                                                        data.dy[i]))
2488                    else:
2489                        out.write("%g  %g  %g\n" % (data.x[i], 
2490                                                    data.y[i],
2491                                                    data.dy[i]))
2492                else:
2493                    out.write("%g  %g\n" % (data.x[i], 
2494                                            data.y[i]))
2495            out.close() 
2496                             
2497    def show_data1d(self, data, name):
2498        """
2499        Show data dialog
2500        """   
2501        try:
2502            xmin = min(data.x)
2503            ymin = min(data.y)
2504        except:
2505            msg = "Unable to find min/max of \n data named %s"% \
2506                        data.filename 
2507            wx.PostEvent(self, StatusEvent(status=msg,
2508                                       info="error"))
2509            raise ValueError, msg
2510        text = data.__str__() 
2511        text += 'Data Min Max:\n'
2512        text += 'X_min = %s:  X_max = %s\n'% (xmin, max(data.x))
2513        text += 'Y_min = %s:  Y_max = %s\n'% (ymin, max(data.y))
2514        if data.dy != None:
2515            text += 'dY_min = %s:  dY_max = %s\n'% (min(data.dy), max(data.dy))
2516        text += '\nData Points:\n'
2517        x_st = "X"
2518        for index in range(len(data.x)):
2519            if data.dy != None:
2520                dy_val = data.dy[index]
2521            else:
2522                dy_val = 0.0
2523            if data.dx != None:
2524                dx_val = data.dx[index]
2525            else:
2526                dx_val = 0.0
2527            if data.dxl != None:
2528                if index == 0: 
2529                    x_st = "Xl"
2530                dx_val = data.dxl[index]
2531            elif data.dxw != None:
2532                if index == 0: 
2533                    x_st = "Xw"
2534                dx_val = data.dxw[index]
2535           
2536            if index == 0:
2537                text += "<index> \t<X> \t<Y> \t<dY> \t<d%s>\n"% x_st
2538            text += "%s \t%s \t%s \t%s \t%s\n" % (index,
2539                                            data.x[index], 
2540                                            data.y[index],
2541                                            dy_val,
2542                                            dx_val)
2543        from pdfview import TextFrame
2544        frame = TextFrame(None, -1, "Data Info: %s"% data.name, text) 
2545        # put icon
2546        self.put_icon(frame) 
2547        frame.Show(True) 
2548           
2549    def save_data2d(self, data, fname):   
2550        """
2551        Save data2d dialog
2552        """
2553        default_name = fname
2554        wildcard = "IGOR/DAT 2D file in Q_map (*.dat)|*.DAT"
2555        dlg = wx.FileDialog(self, "Choose a file",
2556                            self._default_save_location,
2557                            default_name, wildcard , wx.SAVE)
2558       
2559        if dlg.ShowModal() == wx.ID_OK:
2560            path = dlg.GetPath()
2561            # ext_num = 0 for .txt, ext_num = 1 for .xml
2562            # This is MAC Fix
2563            ext_num = dlg.GetFilterIndex()
2564            if ext_num == 0:
2565                format = '.dat'
2566            else:
2567                format = ''
2568            path = os.path.splitext(path)[0] + format
2569            mypath = os.path.basename(path)
2570           
2571            #TODO: This is bad design. The DataLoader is designed
2572            #to recognize extensions.
2573            # It should be a simple matter of calling the .
2574            #save(file, data, '.xml') method
2575            # of the DataLoader.loader.Loader class.
2576            from sans.dataloader.loader import  Loader
2577            #Instantiate a loader
2578            loader = Loader() 
2579
2580            format = ".dat"
2581            if os.path.splitext(mypath)[1].lower() == format:
2582                # Make sure the ext included in the file name
2583                # especially on MAC
2584                fileName = os.path.splitext(path)[0] + format
2585                loader.save(fileName, data, format)
2586            try:
2587                self._default_save_location = os.path.dirname(path)
2588            except:
2589                pass   
2590        dlg.Destroy() 
2591                             
2592    def show_data2d(self, data, name):
2593        """
2594        Show data dialog
2595        """   
2596
2597        wx.PostEvent(self, StatusEvent(status = "Gathering Data2D Info.", 
2598                                       type = 'start' ))
2599        text = data.__str__() 
2600        text += 'Data Min Max:\n'
2601        text += 'I_min = %s\n'% min(data.data)
2602        text += 'I_max = %s\n\n'% max(data.data)
2603        text += 'Data (First 2501) Points:\n'
2604        text += 'Data columns include err(I).\n'
2605        text += 'ASCII data starts here.\n'
2606        text += "<index> \t<Qx> \t<Qy> \t<I> \t<dI> \t<dQparal> \t<dQperp>\n"
2607        di_val = 0.0
2608        dx_val = 0.0
2609        dy_val = 0.0
2610        #mask_val = True
2611        len_data = len(data.qx_data)
2612        for index in xrange(0, len_data):
2613            x_val = data.qx_data[index]
2614            y_val = data.qy_data[index]
2615            i_val = data.data[index]
2616            if data.err_data != None: 
2617                di_val = data.err_data[index]
2618            if data.dqx_data != None: 
2619                dx_val = data.dqx_data[index]
2620            if data.dqy_data != None: 
2621                dy_val = data.dqy_data[index]
2622            #if data.mask != None: mask_val = data.mask[index]
2623 
2624            text += "%s \t%s \t%s \t%s \t%s \t%s \t%s\n" % (index,
2625                                            x_val, 
2626                                            y_val,
2627                                            i_val,
2628                                            di_val,
2629                                            dx_val,
2630                                            dy_val)
2631            # Takes too long time for typical data2d: Break here
2632            if index >= 2500:
2633                text += ".............\n"
2634                break
2635
2636        from pdfview import TextFrame
2637        frame = TextFrame(None, -1, "Data Info: %s"% data.name, text) 
2638        # put icon
2639        self.put_icon(frame)
2640        frame.Show(True) 
2641        wx.PostEvent(self, StatusEvent(status = "Data2D Info Displayed", 
2642                                       type = 'stop' ))
2643                                 
2644    def set_current_perspective(self, perspective):
2645        """
2646        set the current active perspective
2647        """
2648        self._current_perspective = perspective
2649        name = "No current analysis selected"
2650        if self._current_perspective is not None:
2651            self._add_current_plugin_menu()
2652            for panel in self.panels.values():
2653                if hasattr(panel, 'CENTER_PANE') and panel.CENTER_PANE:
2654                    for name in self._current_perspective.get_perspective():
2655                        frame = panel.get_frame()
2656                        if frame != None:
2657                            if name == panel.window_name:
2658                                panel.on_set_focus(event=None)
2659                                frame.Show(True)
2660                            else:
2661                                frame.Show(False)
2662                            #break               
2663            name = self._current_perspective.sub_menu
2664            if self._data_panel is not None:
2665                self._data_panel.set_active_perspective(name)
2666                self._check_applications_menu()
2667            #Set the SasView title
2668            self._set_title_name(name)
2669           
2670    def _set_title_name(self, name):
2671        """
2672        Set the SasView title w/ the current application name
2673       
2674        : param name: application name [string]
2675        """
2676        # Set SanView Window title w/ application anme
2677        title = self.title + "  - " + name + " -"
2678        self.SetTitle(title)
2679           
2680    def _check_applications_menu(self):
2681        """
2682        check the menu of the current application
2683        """
2684        if self._applications_menu is not None:
2685            for menu in self._applications_menu.GetMenuItems():
2686                if self._current_perspective is not None:
2687                    name = self._current_perspective.sub_menu
2688                    if menu.IsCheckable():
2689                        if menu.GetLabel() == name:
2690                            menu.Check(True)
2691                        else:
2692                            menu.Check(False) 
2693           
2694    def enable_add_data(self, new_plot):
2695        """
2696        Enable append data on a plot panel
2697        """
2698
2699        if self.panel_on_focus not in self._plotting_plugin.plot_panels.values():
2700            return
2701        is_theory = len(self.panel_on_focus.plots) <= 1 and \
2702            self.panel_on_focus.plots.values()[0].__class__.__name__ == "Theory1D"
2703           
2704        is_data2d = hasattr(new_plot, 'data')
2705       
2706        is_data1d = self.panel_on_focus.__class__.__name__ == "ModelPanel1D"\
2707            and self.panel_on_focus.group_id is not None
2708        has_meta_data = hasattr(new_plot, 'meta_data')
2709       
2710        #disable_add_data if the data is being recovered from  a saved state file.
2711        is_state_data = False
2712        if has_meta_data:
2713            if 'invstate' in new_plot.meta_data: 
2714                is_state_data = True
2715            if  'prstate' in new_plot.meta_data: 
2716                is_state_data = True
2717            if  'fitstate' in new_plot.meta_data: 
2718                is_state_data = True
2719   
2720        return is_data1d and not is_data2d and not is_theory and not is_state_data
2721   
2722    def check_multimode(self, perspective=None):
2723        """
2724        Check the perspective have batch mode capablitity
2725        """
2726        if perspective == None or self._data_panel == None:
2727            return
2728        flag = perspective.get_batch_capable()
2729        flag_on = perspective.batch_on
2730        if flag:
2731            self._data_panel.rb_single_mode.SetValue(not flag_on)
2732            self._data_panel.rb_batch_mode.SetValue(flag_on)
2733        else:
2734            self._data_panel.rb_single_mode.SetValue(True)
2735            self._data_panel.rb_batch_mode.SetValue(False)
2736        self._data_panel.rb_single_mode.Enable(flag)
2737        self._data_panel.rb_batch_mode.Enable(flag)
2738               
2739
2740   
2741    def enable_edit_menu(self):
2742        """
2743        enable menu item under edit menu depending on the panel on focus
2744        """
2745        if self.cpanel_on_focus is not None and self._edit_menu is not None:
2746            flag = self.cpanel_on_focus.get_undo_flag()
2747            self._edit_menu.Enable(GUIFRAME_ID.UNDO_ID, flag)
2748            flag = self.cpanel_on_focus.get_redo_flag()
2749            self._edit_menu.Enable(GUIFRAME_ID.REDO_ID, flag)
2750            flag = self.cpanel_on_focus.get_copy_flag()
2751            self._edit_menu.Enable(GUIFRAME_ID.COPY_ID, flag)
2752            flag = self.cpanel_on_focus.get_paste_flag()
2753            self._edit_menu.Enable(GUIFRAME_ID.PASTE_ID, flag)
2754            #flag = self.cpanel_on_focus.get_print_flag()
2755            #self._edit_menu.Enable(GUIFRAME_ID.PRINT_ID, flag)
2756            flag = self.cpanel_on_focus.get_preview_flag()
2757            self._edit_menu.Enable(GUIFRAME_ID.PREVIEW_ID, flag)
2758            flag = self.cpanel_on_focus.get_reset_flag()
2759            self._edit_menu.Enable(GUIFRAME_ID.RESET_ID, flag)
2760        else:
2761            flag = False
2762            self._edit_menu.Enable(GUIFRAME_ID.UNDO_ID, flag)
2763            self._edit_menu.Enable(GUIFRAME_ID.REDO_ID, flag)
2764            self._edit_menu.Enable(GUIFRAME_ID.COPY_ID, flag)
2765            self._edit_menu.Enable(GUIFRAME_ID.PASTE_ID, flag)
2766            #self._edit_menu.Enable(GUIFRAME_ID.PRINT_ID, flag)
2767            self._edit_menu.Enable(GUIFRAME_ID.PREVIEW_ID, flag)
2768            self._edit_menu.Enable(GUIFRAME_ID.RESET_ID, flag)
2769           
2770    def on_undo_panel(self, event=None):
2771        """
2772        undo previous action of the last panel on focus if possible
2773        """
2774        if self.cpanel_on_focus is not None:
2775            self.cpanel_on_focus.on_undo(event)
2776           
2777    def on_redo_panel(self, event=None):
2778        """
2779        redo the last cancel action done on the last panel on focus
2780        """
2781        if self.cpanel_on_focus is not None:
2782            self.cpanel_on_focus.on_redo(event)
2783           
2784    def on_copy_panel(self, event=None):
2785        """
2786        copy the last panel on focus if possible
2787        """
2788        if self.cpanel_on_focus is not None:
2789            self.cpanel_on_focus.on_copy(event)
2790           
2791    def on_paste_panel(self, event=None):
2792        """
2793        paste clipboard to the last panel on focus
2794        """
2795        if self.cpanel_on_focus is not None:
2796            self.cpanel_on_focus.on_paste(event)
2797                   
2798    def on_bookmark_panel(self, event=None):
2799        """
2800        bookmark panel
2801        """
2802        if self.cpanel_on_focus is not None:
2803            self.cpanel_on_focus.on_bookmark(event)
2804           
2805    def append_bookmark(self, event=None):
2806        """
2807        Bookmark available information of the panel on focus
2808        """
2809        self._toolbar.append_bookmark(event)
2810           
2811    def on_save_panel(self, event=None):
2812        """
2813        save possible information on the current panel
2814        """
2815        if self.cpanel_on_focus is not None:
2816            self.cpanel_on_focus.on_save(event)
2817           
2818    def on_preview_panel(self, event=None):
2819        """
2820        preview information on the panel on focus
2821        """
2822        if self.cpanel_on_focus is not None:
2823            self.cpanel_on_focus.on_preview(event)
2824           
2825    def on_print_panel(self, event=None):
2826        """
2827        print available information on the last panel on focus
2828        """
2829        if self.cpanel_on_focus is not None:
2830            self.cpanel_on_focus.on_print(event)
2831           
2832    def on_zoom_panel(self, event=None):
2833        """
2834        zoom on the current panel if possible
2835        """
2836        if self.cpanel_on_focus is not None:
2837            self.cpanel_on_focus.on_zoom(event)
2838           
2839    def on_zoom_in_panel(self, event=None):
2840        """
2841        zoom in of the panel on focus
2842        """
2843        if self.cpanel_on_focus is not None:
2844            self.cpanel_on_focus.on_zoom_in(event)
2845           
2846    def on_zoom_out_panel(self, event=None):
2847        """
2848        zoom out on the panel on focus
2849        """
2850        if self.cpanel_on_focus is not None:
2851            self.cpanel_on_focus.on_zoom_out(event)
2852           
2853    def on_drag_panel(self, event=None):
2854        """
2855        drag apply to the panel on focus
2856        """
2857        if self.cpanel_on_focus is not None:
2858            self.cpanel_on_focus.on_drag(event)
2859           
2860    def on_reset_panel(self, event=None):
2861        """
2862        reset the current panel
2863        """
2864        if self.cpanel_on_focus is not None:
2865            self.cpanel_on_focus.on_reset(event)
2866           
2867    def on_change_caption(self, name, old_caption, new_caption):     
2868        """
2869        Change the panel caption
2870       
2871        :param name: window_name of the pane
2872        :param old_caption: current caption [string]
2873        :param new_caption: new caption [string]
2874        """
2875        # wx.aui.AuiPaneInfo
2876        pane_info = self.get_paneinfo(old_caption) 
2877        # update the data_panel.cb_plotpanel
2878        if 'data_panel' in self.panels.keys():
2879            # remove from data_panel combobox
2880            data_panel = self.panels["data_panel"]
2881            if data_panel.cb_plotpanel is not None:
2882                # Check if any panel has the same caption
2883                has_newstring = data_panel.cb_plotpanel.FindString\
2884                                                            (str(new_caption)) 
2885                caption = new_caption
2886                if has_newstring != wx.NOT_FOUND:
2887                    captions = self._get_plotpanel_captions()
2888                    # Append nummber
2889                    inc = 1
2890                    while (1):
2891                        caption = new_caption + '_%s'% str(inc)
2892                        if caption not in captions:
2893                            break
2894                        inc += 1
2895                    # notify to users
2896                    msg = "Found Same Title: Added '_%s'"% str(inc)
2897                    wx.PostEvent(self, StatusEvent(status=msg))
2898                # update data_panel cb
2899                pos = data_panel.cb_plotpanel.FindString(str(old_caption)) 
2900                if pos != wx.NOT_FOUND:
2901                    data_panel.cb_plotpanel.SetString(pos, caption)
2902                    data_panel.cb_plotpanel.SetStringSelection(caption)
2903        # update window Show menu
2904        if self._window_menu != None:
2905            for item in self._window_menu.GetMenuItems():
2906                pos = self._window_menu.FindItem(old_caption)
2907                if self._window_menu.GetLabel(pos) == str(old_caption):
2908                    self._window_menu.SetLabel(pos, caption)
2909                break
2910        # New Caption
2911        pane_info.SetTitle(caption)
2912        return caption
2913       
2914    def get_paneinfo(self, name):
2915        """
2916        Get pane Caption from window_name
2917       
2918        :param name: window_name in AuiPaneInfo
2919        : return: AuiPaneInfo of the name
2920        """
2921        for panel in self.plot_panels.values():
2922            if panel.frame.GetTitle() == name:
2923                return panel.frame
2924        return None
2925   
2926    def enable_undo(self):
2927        """
2928        enable undo related control
2929        """
2930        if self.cpanel_on_focus is not None:
2931            self._toolbar.enable_undo(self.cpanel_on_focus)
2932           
2933    def enable_redo(self):
2934        """
2935        enable redo
2936        """
2937        if self.cpanel_on_focus is not None:
2938            self._toolbar.enable_redo(self.cpanel_on_focus)
2939           
2940    def enable_copy(self):
2941        """
2942        enable copy related control
2943        """
2944        if self.cpanel_on_focus is not None:
2945            self._toolbar.enable_copy(self.cpanel_on_focus)
2946           
2947    def enable_paste(self):
2948        """
2949        enable paste
2950        """
2951        if self.cpanel_on_focus is not None:
2952            self._toolbar.enable_paste(self.cpanel_on_focus)
2953                       
2954    def enable_bookmark(self):
2955        """
2956        Bookmark
2957        """
2958        if self.cpanel_on_focus is not None:
2959            self._toolbar.enable_bookmark(self.cpanel_on_focus)
2960           
2961    def enable_save(self):
2962        """
2963        save
2964        """
2965        if self.cpanel_on_focus is not None:
2966            self._toolbar.enable_save(self.cpanel_on_focus)
2967           
2968    def enable_preview(self):
2969        """
2970        preview
2971        """
2972        if self.cpanel_on_focus is not None:
2973            self._toolbar.enable_preview(self.cpanel_on_focus)
2974           
2975    def enable_print(self):
2976        """
2977        print
2978        """
2979        if self.cpanel_on_focus is not None:
2980            self._toolbar.enable_print(self.cpanel_on_focus)
2981           
2982    def enable_zoom(self):
2983        """
2984        zoom
2985        """
2986        if self.cpanel_on_focus is not None:
2987            self._toolbar.enable_zoom(self.panel_on_focus)
2988           
2989    def enable_zoom_in(self):
2990        """
2991        zoom in
2992        """
2993        if self.cpanel_on_focus is not None:
2994            self._toolbar.enable_zoom_in(self.panel_on_focus)
2995           
2996    def enable_zoom_out(self):
2997        """
2998        zoom out
2999        """
3000        if self.cpanel_on_focus is not None:
3001            self._toolbar.enable_zoom_out(self.panel_on_focus)
3002           
3003    def enable_drag(self, event=None):
3004        """
3005        drag
3006        """
3007        #if self.cpanel_on_focus is not None:
3008        #    self._toolbar.enable_drag(self.panel_on_focus)
3009           
3010    def enable_reset(self):
3011        """
3012        reset the current panel
3013        """
3014        if self.cpanel_on_focus is not None:
3015            self._toolbar.enable_reset(self.panel_on_focus)
3016           
3017    def get_toolbar_height(self):
3018        """
3019        """
3020        size_y = 0
3021        if self.GetToolBar() != None and self.GetToolBar().IsShown():
3022            if not IS_LINUX:
3023                _, size_y = self.GetToolBar().GetSizeTuple()
3024        return size_y
3025   
3026    def set_schedule_full_draw(self, panel=None, func='del'):
3027        """
3028        Add/subtract the schedule full draw list with the panel given
3029       
3030        :param panel: plot panel
3031        :param func: append or del [string]
3032        """
3033
3034        # append this panel in the schedule list if not in yet
3035        if func == 'append':
3036            if not panel in self.schedule_full_draw_list:
3037                self.schedule_full_draw_list.append(panel) 
3038        # remove this panel from schedule list
3039        elif func == 'del':
3040            if len(self.schedule_full_draw_list) > 0:
3041                if panel in self.schedule_full_draw_list:
3042                    self.schedule_full_draw_list.remove(panel)
3043
3044        # reset the schdule
3045        if len(self.schedule_full_draw_list) == 0:
3046            self.schedule = False
3047        else:
3048            self.schedule = True   
3049       
3050    def full_draw(self):
3051        """
3052        Draw the panels with axes in the schedule to full dwar list
3053        """
3054       
3055        count = len(self.schedule_full_draw_list)
3056        #if not self.schedule:
3057        if count < 1:
3058            self.set_schedule(False)
3059            return
3060
3061        else:
3062            ind = 0
3063            # if any of the panel is shown do full_draw
3064            for panel in self.schedule_full_draw_list:
3065                ind += 1
3066                if panel.frame.IsShown():
3067                    break
3068                # otherwise, return
3069                if ind == count:
3070                    return
3071        #Simple redraw only for a panel shown
3072        def f_draw(panel):
3073            """
3074            Draw A panel in the full dwar list
3075            """
3076            try:
3077                # This checking of GetCapture is to stop redrawing
3078                # while any panel is capture.
3079                frame = panel.frame
3080               
3081                if not frame.GetCapture():
3082                    # draw if possible
3083                    panel.set_resizing(False)
3084                    #panel.Show(True)
3085                    panel.draw_plot()
3086                # Check if the panel is not shown
3087                flag = frame.IsShown()
3088                frame.Show(flag)
3089                   
3090            except:
3091                raise
3092     
3093        # Draw all panels       
3094        #map(f_draw, self.schedule_full_draw_list)
3095        f_draw(self.schedule_full_draw_list[0])
3096        # Reset the attr 
3097        if len(self.schedule_full_draw_list) == 0:
3098            self.set_schedule(False)
3099        else:
3100            self.set_schedule(True)
3101       
3102    def set_schedule(self, schedule=False): 
3103        """
3104        Set schedule
3105        """
3106        self.schedule = schedule
3107               
3108    def get_schedule(self): 
3109        """
3110        Get schedule
3111        """
3112        return self.schedule
3113   
3114    def on_set_plot_focus(self, panel):
3115        """
3116        Set focus on a plot panel
3117        """
3118        if panel == None:
3119            return
3120        #self.set_plot_unfocus()
3121        panel.on_set_focus(None) 
3122        # set focusing panel
3123        self.panel_on_focus = panel 
3124        self.set_panel_on_focus(None)
3125
3126    def set_plot_unfocus(self): 
3127        """
3128        Un focus all plot panels
3129        """
3130        for plot in self.plot_panels.values():
3131            plot.on_kill_focus(None)
3132   
3133    def get_window_size(self):
3134        """
3135        Get window size
3136       
3137        :return size: tuple
3138        """
3139        width, height = self.GetSizeTuple()
3140        if not IS_WIN:
3141            # Subtract toolbar height to get real window side
3142            if self._toolbar.IsShown():
3143                height -= 45
3144        return (width, height)
3145           
3146    def _onDrawIdle(self, *args, **kwargs):
3147        """
3148        ReDraw with axes
3149        """
3150        try:
3151            # check if it is time to redraw
3152            if self.GetCapture() == None:
3153                # Draw plot, changes resizing too
3154                self.full_draw()
3155        except:
3156            pass
3157           
3158        # restart idle       
3159        self._redraw_idle(*args, **kwargs)
3160
3161           
3162    def _redraw_idle(self, *args, **kwargs):
3163        """
3164        Restart Idle
3165        """
3166        # restart idle   
3167        self.idletimer.Restart(100*TIME_FACTOR, *args, **kwargs)
3168
3169       
3170class DefaultPanel(wx.Panel, PanelBase):
3171    """
3172    Defines the API for a panels to work with
3173    the GUI manager
3174    """
3175    ## Internal nickname for the window, used by the AUI manager
3176    window_name = "default"
3177    ## Name to appear on the window title bar
3178    window_caption = "Welcome panel"
3179    ## Flag to tell the AUI manager to put this panel in the center pane
3180    CENTER_PANE = True
3181    def __init__(self, parent, *args, **kwds):
3182        wx.Panel.__init__(self, parent, *args, **kwds)
3183        PanelBase.__init__(self, parent)
3184   
3185
3186
3187class ViewApp(wx.App):
3188    """
3189    Toy application to test this Frame
3190    """
3191    def OnInit(self):
3192        """
3193        When initialised
3194        """
3195        pos, size, self.is_max = self.window_placement((GUIFRAME_WIDTH, 
3196                                           GUIFRAME_HEIGHT))     
3197        self.frame = ViewerFrame(parent=None, 
3198                             title=APPLICATION_NAME, 
3199                             pos=pos, 
3200                             gui_style = DEFAULT_STYLE,
3201                             size=size)
3202        self.frame.Hide()
3203        if not IS_WIN:
3204            self.frame.EnableCloseButton(False)
3205        self.s_screen = None
3206
3207        try:
3208            self.open_file()
3209        except:
3210            msg = "%s Could not load " % str(APPLICATION_NAME)
3211            msg += "input file from command line.\n"
3212            logging.error(msg)
3213        # Display a splash screen on top of the frame.
3214        try:
3215            if os.path.isfile(SPLASH_SCREEN_PATH):
3216                self.s_screen = self.display_splash_screen(parent=self.frame, 
3217                                        path=SPLASH_SCREEN_PATH)
3218            else:
3219                self.frame.Show()   
3220        except:
3221            if self.s_screen is not None:
3222                self.s_screen.Close()
3223            msg = "Cannot display splash screen\n"
3224            msg += str (sys.exc_value)
3225            logging.error(msg)
3226            self.frame.Show()
3227 
3228        #if hasattr(self.frame, 'special'):
3229        #    self.frame.special.SetCurrent()
3230        self.SetTopWindow(self.frame)
3231 
3232        return True
3233   
3234    def maximize_win(self):
3235        """
3236        Maximize the window after the frame shown
3237        """
3238        if self.is_max:
3239            if self.frame.IsShown():
3240                # Max window size
3241                self.frame.Maximize(self.is_max)
3242
3243    def open_file(self):
3244        """
3245        open a state file at the start of the application
3246        """
3247        input_file = None
3248        if len(sys.argv) >= 2:
3249            cmd = sys.argv[0].lower()
3250            basename  = os.path.basename(cmd)
3251            app_base = str(APPLICATION_NAME).lower()
3252            if os.path.isfile(cmd) or basename.lower() == app_base:
3253                app_py = app_base + '.py'
3254                app_exe = app_base + '.exe'
3255                app_app = app_base + '.app'
3256                if basename.lower() in [app_py, app_exe, app_app, app_base]:
3257                    data_base = sys.argv[1]
3258                    input_file = os.path.normpath(os.path.join(DATAPATH, 
3259                                                               data_base))
3260        if input_file is None:
3261            return
3262        if self.frame is not None:
3263            self.frame.set_input_file(input_file=input_file)
3264         
3265    def clean_plugin_models(self, path): 
3266        """
3267        Delete plugin models  in app folder
3268       
3269        :param path: path of the plugin_models folder in app
3270        """
3271        # do it only the first time app loaded
3272        # delete unused model folder   
3273        model_folder = os.path.join(PATH_APP, path)
3274        if os.path.exists(model_folder) and os.path.isdir(model_folder):
3275            if len(os.listdir(model_folder)) > 0:
3276                try:
3277                    for file in os.listdir(model_folder):
3278                        file_path = os.path.join(model_folder, file)
3279                        if os.path.isfile(file_path):
3280                            os.remove(file_path)
3281                except:
3282                    logging.error("gui_manager.clean_plugin_models:\n  %s" \
3283                                  % sys.exc_value)
3284             
3285    def set_manager(self, manager):
3286        """
3287        Sets a reference to the application manager
3288        of the GUI manager (Frame)
3289        """
3290        self.frame.set_manager(manager)
3291       
3292    def build_gui(self):
3293        """
3294        Build the GUI
3295        """
3296        #try to load file at the start
3297        try:
3298            self.open_file()
3299        except:
3300            raise
3301        self.frame.build_gui()
3302        #if self.s_screen is not None and self.s_screen.IsShown():
3303        #    self.s_screen.Close()
3304       
3305    def set_welcome_panel(self, panel_class):
3306        """
3307        Set the welcome panel
3308       
3309        :param panel_class: class of the welcome panel to be instantiated
3310       
3311        """
3312        self.frame.set_welcome_panel(panel_class)
3313       
3314    def add_perspective(self, perspective):
3315        """
3316        Manually add a perspective to the application GUI
3317        """
3318        self.frame.add_perspective(perspective)
3319   
3320    def window_placement(self, size):
3321        """
3322        Determines the position and size of the application frame such that it
3323        fits on the user's screen without obstructing (or being obstructed by)
3324        the Windows task bar.  The maximum initial size in pixels is bounded by
3325        WIDTH x HEIGHT.  For most monitors, the application
3326        will be centered on the screen; for very large monitors it will be
3327        placed on the left side of the screen.
3328        """
3329        is_maximized = False
3330        # Get size of screen without
3331        for screenCount in range(wx.Display().GetCount()):
3332            screen = wx.Display(screenCount)
3333            if screen.IsPrimary():
3334                displayRect = screen.GetClientArea()
3335                break
3336       
3337        posX, posY, displayWidth, displayHeight = displayRect       
3338        customWidth, customHeight = size
3339       
3340        # If the custom size is default, set 90% of the screen size
3341        if customWidth <= 0 and customHeight <= 0:
3342            if customWidth == 0 and customHeight == 0:
3343                is_maximized = True
3344            customWidth = displayWidth * 0.9
3345            customHeight = displayHeight * 0.9
3346        else:
3347            # If the custom screen is bigger than the
3348            # window screen than make maximum size
3349            if customWidth > displayWidth:
3350                customWidth = displayWidth
3351            if customHeight > displayHeight:
3352                customHeight = displayHeight
3353           
3354        # Note that when running Linux and using an Xming (X11) server on a PC
3355        # with a dual  monitor configuration, the reported display size may be
3356        # that of both monitors combined with an incorrect display count of 1.
3357        # To avoid displaying this app across both monitors, we check for
3358        # screen 'too big'.  If so, we assume a smaller width which means the
3359        # application will be placed towards the left hand side of the screen.
3360       
3361        # If dual screen registered as 1 screen. Make width half.
3362        # MAC just follows the default behavior of pos
3363        if IS_WIN:
3364            if displayWidth > (displayHeight*2):
3365                if (customWidth == displayWidth):
3366                    customWidth = displayWidth/2
3367                # and set the position to be the corner of the screen.
3368                posX = 0
3369                posY = 0
3370               
3371            # Make the position the middle of the screen. (Not 0,0)
3372            else:
3373                posX = (displayWidth - customWidth)/2
3374                posY = (displayHeight - customHeight)/2
3375        # Return the suggested position and size for the application frame.   
3376        return (posX, posY), (customWidth, customHeight), is_maximized
3377
3378   
3379    def display_splash_screen(self, parent, 
3380                              path=SPLASH_SCREEN_PATH):
3381        """Displays the splash screen.  It will exactly cover the main frame."""
3382       
3383        # Prepare the picture.  On a 2GHz intel cpu, this takes about a second.
3384        image = wx.Image(path, wx.BITMAP_TYPE_PNG)
3385        image.Rescale(SPLASH_SCREEN_WIDTH, 
3386                      SPLASH_SCREEN_HEIGHT, wx.IMAGE_QUALITY_HIGH)
3387        bm = image.ConvertToBitmap()
3388
3389        # Create and show the splash screen.  It will disappear only when the
3390        # program has entered the event loop AND either the timeout has expired
3391        # or the user has left clicked on the screen.  Thus any processing
3392        # performed in this routine (including sleeping) or processing in the
3393        # calling routine (including doing imports) will prevent the splash
3394        # screen from disappearing.
3395        #
3396        # Note that on Linux, the timeout appears to occur immediately in which
3397        # case the splash screen disappears upon entering the event loop.
3398        s_screen = wx.SplashScreen(bitmap=bm,
3399                         splashStyle=(wx.SPLASH_TIMEOUT|
3400                                              wx.SPLASH_CENTRE_ON_SCREEN),
3401                                 style=(wx.SIMPLE_BORDER|
3402                                        wx.FRAME_NO_TASKBAR|
3403                                        wx.FRAME_FLOAT_ON_PARENT),
3404                                       
3405                        milliseconds=SS_MAX_DISPLAY_TIME,
3406                        parent=parent,
3407                        id=wx.ID_ANY)
3408        from sans.guiframe.gui_statusbar import SPageStatusbar
3409        statusBar = SPageStatusbar(s_screen)
3410        s_screen.SetStatusBar(statusBar)
3411        s_screen.Bind(wx.EVT_CLOSE, self.on_close_splash_screen)
3412        s_screen.Show()
3413        return s_screen
3414       
3415       
3416    def on_close_splash_screen(self, event):
3417        """
3418        When the splash screen is closed.
3419        """
3420        self.frame.Show(True)
3421        event.Skip()
3422        self.maximize_win()
3423
3424
3425class MDIFrame(CHILD_FRAME):
3426    """
3427    Frame for panels
3428    """
3429    def __init__(self, parent, panel, title="Untitled",
3430                size=(300,200), *args, **kwds):
3431        """
3432        comment
3433        :param parent: parent panel/container
3434        """
3435        kwds['size']= size
3436        kwds['title']= title
3437        kwds['style'] = MDI_STYLE
3438        # Initialize the Frame object
3439        CHILD_FRAME.__init__(self, parent, *args, **kwds)
3440        self.parent = parent
3441        self.name = "Untitled"
3442        self.batch_on = self.parent.batch_on
3443        self.panel = panel
3444        if panel != None:
3445            self.set_panel(panel)
3446        self.Show(False)
3447   
3448    def show_data_panel(self, action):
3449        """
3450        """
3451        self.parent.show_data_panel(action)
3452       
3453    def set_panel(self, panel):
3454        """
3455        """
3456        self.panel = panel
3457        self.name = panel.window_name
3458        self.SetTitle(panel.window_caption)
3459        self.SetHelpText(panel.help_string)
3460        width, height = self.parent._get_panels_size(panel)
3461        if hasattr(panel, "CENTER_PANE") and panel.CENTER_PANE:
3462            width *= 0.6 
3463        self.SetSize((width, height))
3464        self.parent.put_icon(self)
3465        self.Bind(wx.EVT_SET_FOCUS, self.set_panel_focus)
3466        self.Bind(wx.EVT_CLOSE, self.OnClose)
3467        self.Show(False)
3468   
3469    def set_panel_focus(self, event):
3470        """
3471        """
3472        if self.parent.panel_on_focus != self.panel:
3473            self.panel.SetFocus()
3474            self.parent.panel_on_focus = self.panel
3475       
3476    def OnClose(self, event):
3477        """
3478        On Close event
3479        """
3480        self.panel.on_close(event)
3481       
3482if __name__ == "__main__": 
3483    app = ViewApp(0)
3484    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.