source: sasview/src/sans/guiframe/gui_manager.py @ f309303

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

make toolbar resizable

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