source: sasview/src/sas/guiframe/gui_manager.py @ b199493

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 b199493 was 064723b, checked in by krzywon, 10 years ago

Found one small issue with the last commit.

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