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

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 eff93b8 was a3b635b, checked in by Jeff Krzywon <jeffery.krzywon@…>, 10 years ago

I have the save and load project working locally, but I am need to download the built executable and test that before saying it is ready.

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