source: sasview/src/sas/guiframe/gui_manager.py @ 24386b9

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 24386b9 was 24386b9, checked in by mathieu, 9 years ago

Update version checking

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