source: sasview/src/sans/guiframe/gui_manager.py @ 49d019c

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 49d019c was 49d019c, checked in by Mathieu Doucet <doucetm@…>, 10 years ago

Shorten the splash screen timeout. Fix data panel position on OSX.

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