source: sasview/src/sas/guiframe/gui_manager.py @ 805f07a

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 805f07a was 805f07a, checked in by butler, 9 years ago

remove redundant logging message

  • Property mode set to 100644
File size: 124.0 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
29
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        except:
2051            msg = traceback.format_exc()
2052            logging.info(msg)
2053            logging.info("Failed to connect to www.sasview.org")
2054            content = "0.0.0"
2055
2056        version = content.strip()
2057        if len(re.findall('\d+\.\d+\.\d+$', version)) < 0:
2058            content = "0.0.0"
2059        self._process_version(content, standalone=event == None)
2060
2061    def _process_version(self, version, standalone=True):
2062        """
2063        Call-back method for the process of checking for updates.
2064        This methods is called by a VersionThread object once the current
2065        version number has been obtained. If the check is being done in the
2066        background, the user will not be notified unless there's an update.
2067
2068        :param version: version string
2069        :param standalone: True of the update is being checked in
2070           the background, False otherwise.
2071
2072        """
2073        try:
2074            if version == "0.0.0":
2075                msg = "Could not connect to the application server."
2076                msg += " Please try again later."
2077                self.SetStatusText(msg)
2078            elif cmp(version, config.__version__) > 0:
2079                msg = "Version %s is available! " % str(version)
2080                if not standalone:
2081                    import webbrowser
2082                    webbrowser.open(config.__download_page__)
2083                else:
2084                    msg += "See the help menu to download it."
2085                self.SetStatusText(msg)
2086            else:
2087                if not standalone:
2088                    msg = "You have the latest version"
2089                    msg += " of %s" % str(config.__appname__)
2090                    self.SetStatusText(msg)
2091        except:
2092            msg = "guiframe: could not get latest application"
2093            msg += " version number\n  %s" % sys.exc_value
2094            logging.error(msg)
2095            if not standalone:
2096                msg = "Could not connect to the application server."
2097                msg += " Please try again later."
2098                self.SetStatusText(msg)
2099
2100    def _onAcknowledge(self, evt):
2101        """
2102        Pop up the acknowledge dialog
2103
2104        :param evt: menu event
2105
2106        """
2107        if config._do_acknowledge:
2108            import sas.guiframe.acknowledgebox as AcknowledgeBox
2109            dialog = AcknowledgeBox.DialogAcknowledge(None, -1, "")
2110            dialog.ShowModal()
2111
2112    def _onAbout(self, evt):
2113        """
2114        Pop up the about dialog
2115
2116        :param evt: menu event
2117
2118        """
2119        if config._do_aboutbox:
2120            import sas.guiframe.aboutbox as AboutBox
2121            dialog = AboutBox.DialogAbout(None, -1, "")
2122            dialog.ShowModal()
2123
2124    def _onTutorial(self, evt):
2125        """
2126        Pop up the tutorial dialog
2127
2128        :param evt: menu event
2129
2130        """
2131        if config._do_tutorial:
2132            path = config.TUTORIAL_PATH
2133            if IS_WIN:
2134                try:
2135                    from sas.guiframe.pdfview import PDFFrame
2136                    dialog = PDFFrame(None, -1, "Tutorial", path)
2137                    # put icon
2138                    self.put_icon(dialog)
2139                    dialog.Show(True)
2140                except:
2141                    logging.error("Error in _onTutorial: %s" % sys.exc_value)
2142                    try:
2143                        # Try an alternate method
2144                        logging.error("Could not open the tutorial pdf, trying xhtml2pdf")
2145                        from xhtml2pdf import pisa
2146                        pisa.startViewer(path)
2147                    except:
2148                        logging.error("Could not open the tutorial pdf with xhtml2pdf")
2149                        msg = "This feature requires 'PDF Viewer'\n"
2150                        wx.MessageBox(msg, 'Error')
2151            else:
2152                try:
2153                    command = "open '%s'" % path
2154                    os.system(command)
2155                except:
2156                    try:
2157                        # Try an alternate method
2158                        logging.error("Could not open the tutorial pdf, trying xhtml2pdf")
2159                        from xhtml2pdf import pisa
2160                        pisa.startViewer(path)
2161                    except:
2162                        logging.error("Could not open the tutorial pdf with xhtml2pdf")
2163                        msg = "This feature requires the 'Preview' application\n"
2164                        wx.MessageBox(msg, 'Error')
2165
2166    def _onSphinxDocs(self, evt):
2167        """
2168        Bring up Sphinx Documentation at top level whenever the menu item
2169        'documentation' is clicked. Calls DocumentationWindow with the top
2170        level path of "index.html"
2171
2172        :param evt: menu event
2173        """
2174        # Running SasView "in-place" using run.py means the docs will be in a
2175        # different place than they would otherwise.
2176        from documentation_window import DocumentationWindow
2177        _TreeLocation = "user/user.html"
2178        DocumentationWindow(self, -1, _TreeLocation, "", "SasView Documentation")
2179
2180    def set_manager(self, manager):
2181        """
2182        Sets the application manager for this frame
2183
2184        :param manager: frame manager
2185        """
2186        self.app_manager = manager
2187
2188    def post_init(self):
2189        """
2190        This initialization method is called after the GUI
2191        has been created and all plug-ins loaded. It calls
2192        the post_init() method of each plug-in (if it exists)
2193        so that final initialization can be done.
2194        """
2195        for item in self.plugins:
2196            if hasattr(item, "post_init"):
2197                item.post_init()
2198
2199    def set_default_perspective(self):
2200        """
2201        Choose among the plugin the first plug-in that has
2202        "set_default_perspective" method and its return value is True will be
2203        as a default perspective when the welcome page is closed
2204        """
2205        for item in self.plugins:
2206            if hasattr(item, "set_default_perspective"):
2207                if item.set_default_perspective():
2208                    item.on_perspective(event=None)
2209                    return
2210
2211    def set_perspective(self, panels):
2212        """
2213        Sets the perspective of the GUI.
2214        Opens all the panels in the list, and closes
2215        all the others.
2216
2217        :param panels: list of panels
2218        """
2219        for item in self.panels.keys():
2220            # Check whether this is a sticky panel
2221            if hasattr(self.panels[item], "ALWAYS_ON"):
2222                if self.panels[item].ALWAYS_ON:
2223                    continue
2224            if self.panels[item] == None:
2225                continue
2226            if self.panels[item].window_name in panels:
2227                frame = self.panels[item].get_frame()
2228                if not frame.IsShown():
2229                    frame.Show(True)
2230            else:
2231                # always show the data panel if enable
2232                style = self.__gui_style & GUIFRAME.MANAGER_ON
2233                if (style == GUIFRAME.MANAGER_ON) and self.panels[item] == self._data_panel:
2234                    if 'data_panel' in self.panels.keys():
2235                        frame = self.panels['data_panel'].get_frame()
2236                        if frame == None:
2237                            continue
2238                        flag = frame.IsShown()
2239                        frame.Show(flag)
2240                else:
2241                    frame = self.panels[item].get_frame()
2242                    if frame == None:
2243                        continue
2244
2245                    if frame.IsShown():
2246                        frame.Show(False)
2247
2248    def show_data_panel(self, event=None, action=True):
2249        """
2250        show the data panel
2251        """
2252        if self._data_panel_menu == None:
2253            return
2254        label = self._data_panel_menu.GetText()
2255        pane = self.panels["data_panel"]
2256        frame = pane.get_frame()
2257        if label == 'Show Data Explorer':
2258            if action:
2259                frame.Show(True)
2260            self.__gui_style = self.__gui_style | GUIFRAME.MANAGER_ON
2261            self._data_panel_menu.SetText('Hide Data Explorer')
2262        else:
2263            if action:
2264                frame.Show(False)
2265            self.__gui_style = self.__gui_style & (~GUIFRAME.MANAGER_ON)
2266            self._data_panel_menu.SetText('Show Data Explorer')
2267
2268    def add_data_helper(self, data_list):
2269        """
2270        """
2271        if self._data_manager is not None:
2272            self._data_manager.add_data(data_list)
2273
2274    def add_data(self, data_list):
2275        """
2276        receive a dictionary of data from loader
2277        store them its data manager if possible
2278        send to data the current active perspective if the data panel
2279        is not active.
2280        :param data_list: dictionary of data's ID and value Data
2281        """
2282        #Store data into manager
2283        self.add_data_helper(data_list)
2284        # set data in the data panel
2285        if self._data_panel is not None:
2286            data_state = self._data_manager.get_data_state(data_list.keys())
2287            self._data_panel.load_data_list(data_state)
2288        #if the data panel is shown wait for the user to press a button
2289        #to send data to the current perspective. if the panel is not
2290        #show  automatically send the data to the current perspective
2291        style = self.__gui_style & GUIFRAME.MANAGER_ON
2292        if style == GUIFRAME.MANAGER_ON:
2293            #wait for button press from the data panel to set_data
2294            if self._data_panel is not None:
2295                self._data_panel.frame.Show(True)
2296        else:
2297            #automatically send that to the current perspective
2298            self.set_data(data_id=data_list.keys())
2299
2300    def set_data(self, data_id, theory_id=None):
2301        """
2302        set data to current perspective
2303        """
2304        list_data, _ = self._data_manager.get_by_id(data_id)
2305        if self._current_perspective is not None:
2306            self._current_perspective.set_data(list_data.values())
2307
2308        else:
2309            msg = "Guiframe does not have a current perspective"
2310            logging.info(msg)
2311
2312    def set_theory(self, state_id, theory_id=None):
2313        """
2314        """
2315        _, list_theory = self._data_manager.get_by_id(theory_id)
2316        if self._current_perspective is not None:
2317            try:
2318                self._current_perspective.set_theory(list_theory.values())
2319            except:
2320                msg = "Guiframe set_theory: \n" + str(sys.exc_value)
2321                logging.info(msg)
2322                wx.PostEvent(self, StatusEvent(status=msg, info="error"))
2323        else:
2324            msg = "Guiframe does not have a current perspective"
2325            logging.info(msg)
2326
2327    def plot_data(self, state_id, data_id=None,
2328                  theory_id=None, append=False):
2329        """
2330        send a list of data to plot
2331        """
2332        data_list, _ = self._data_manager.get_by_id(data_id)
2333        _, temp_list_theory = self._data_manager.get_by_id(theory_id)
2334        total_plot_list = data_list.values()
2335        for item in temp_list_theory.values():
2336            theory_data, theory_state = item
2337            total_plot_list.append(theory_data)
2338        GROUP_ID = wx.NewId()
2339        for new_plot in total_plot_list:
2340            if append:
2341                if self.panel_on_focus is None:
2342                    message = "cannot append plot. No plot panel on focus!"
2343                    message += "please click on any available plot to set focus"
2344                    wx.PostEvent(self, StatusEvent(status=message,
2345                                                   info='warning'))
2346                    return
2347                else:
2348                    if self.enable_add_data(new_plot):
2349                        new_plot.group_id = self.panel_on_focus.group_id
2350                    else:
2351                        message = "Only 1D Data can be append to"
2352                        message += " plot panel containing 1D data.\n"
2353                        message += "%s not be appended.\n" % str(new_plot.name)
2354                        message += "try new plot option.\n"
2355                        wx.PostEvent(self, StatusEvent(status=message,
2356                                                       info='warning'))
2357            else:
2358                #if not append then new plot
2359                from sas.guiframe.dataFitting import Data2D
2360                if issubclass(Data2D, new_plot.__class__):
2361                    #for 2 D always plot in a separated new plot
2362                    new_plot.group_id = wx.NewId()
2363                else:
2364                    # plot all 1D in a new plot
2365                    new_plot.group_id = GROUP_ID
2366            title = "PLOT " + str(new_plot.title)
2367            wx.PostEvent(self, NewPlotEvent(plot=new_plot,
2368                                            title=title,
2369                                            group_id=new_plot.group_id))
2370
2371    def remove_data(self, data_id, theory_id=None):
2372        """
2373        Delete data state if data_id is provide
2374        delete theory created with data of id data_id if theory_id is provide
2375        if delete all true: delete the all state
2376        else delete theory
2377        """
2378        temp = data_id + theory_id
2379        for plug in self.plugins:
2380            plug.delete_data(temp)
2381        data_list, _ = self._data_manager.get_by_id(data_id)
2382        _, temp_list_theory = self._data_manager.get_by_id(theory_id)
2383        total_plot_list = data_list.values()
2384        for item in temp_list_theory.values():
2385            theory_data, theory_state = item
2386            total_plot_list.append(theory_data)
2387        for new_plot in total_plot_list:
2388            id = new_plot.id
2389            for group_id in new_plot.list_group_id:
2390                wx.PostEvent(self, NewPlotEvent(id=id,
2391                                                group_id=group_id,
2392                                                action='remove'))
2393                #remove res plot: Todo: improve
2394                wx.CallAfter(self._remove_res_plot, id)
2395        self._data_manager.delete_data(data_id=data_id,
2396                                       theory_id=theory_id)
2397
2398    def _remove_res_plot(self, id):
2399        """
2400        Try to remove corresponding res plot
2401
2402        : param id: id of the data
2403        """
2404        try:
2405            wx.PostEvent(self, NewPlotEvent(id=("res" + str(id)),
2406                                            group_id=("res" + str(id)),
2407                                            action='remove'))
2408        except:
2409            logging.error(sys.exc_value)
2410
2411    def save_data1d(self, data, fname):
2412        """
2413        Save data dialog
2414        """
2415        default_name = fname
2416        wildcard = "Text files (*.txt)|*.txt|"\
2417                    "CanSAS 1D files(*.xml)|*.xml"
2418        path = None
2419        dlg = wx.FileDialog(self, "Choose a file",
2420                            self._default_save_location,
2421                            default_name, wildcard, wx.SAVE)
2422
2423        if dlg.ShowModal() == wx.ID_OK:
2424            path = dlg.GetPath()
2425            # ext_num = 0 for .txt, ext_num = 1 for .xml
2426            # This is MAC Fix
2427            ext_num = dlg.GetFilterIndex()
2428            if ext_num == 0:
2429                format = '.txt'
2430            else:
2431                format = '.xml'
2432            path = os.path.splitext(path)[0] + format
2433            mypath = os.path.basename(path)
2434
2435            #Instantiate a loader
2436            loader = Loader()
2437            format = ".txt"
2438            if os.path.splitext(mypath)[1].lower() == format:
2439                # Make sure the ext included in the file name
2440                # especially on MAC
2441                fName = os.path.splitext(path)[0] + format
2442                self._onsaveTXT(data, fName)
2443            format = ".xml"
2444            if os.path.splitext(mypath)[1].lower() == format:
2445                # Make sure the ext included in the file name
2446                # especially on MAC
2447                fName = os.path.splitext(path)[0] + format
2448                loader.save(fName, data, format)
2449            try:
2450                self._default_save_location = os.path.dirname(path)
2451            except:
2452                pass
2453        dlg.Destroy()
2454
2455
2456    def _onsaveTXT(self, data, path):
2457        """
2458        Save file as txt
2459        :TODO: Refactor and remove this method. See TODO in _onSave.
2460        """
2461        if not path == None:
2462            out = open(path, 'w')
2463            has_errors = True
2464            if data.dy == None or data.dy == []:
2465                has_errors = False
2466            # Sanity check
2467            if has_errors:
2468                try:
2469                    if len(data.y) != len(data.dy):
2470                        has_errors = False
2471                except:
2472                    has_errors = False
2473            if has_errors:
2474                if data.dx != None and data.dx != []:
2475                    out.write("<X>   <Y>   <dY>   <dX>\n")
2476                else:
2477                    out.write("<X>   <Y>   <dY>\n")
2478            else:
2479                out.write("<X>   <Y>\n")
2480
2481            for i in range(len(data.x)):
2482                if has_errors:
2483                    if data.dx != None and data.dx != []:
2484                        if  data.dx[i] != None:
2485                            out.write("%g  %g  %g  %g\n" % (data.x[i],
2486                                                            data.y[i],
2487                                                            data.dy[i],
2488                                                            data.dx[i]))
2489                        else:
2490                            out.write("%g  %g  %g\n" % (data.x[i],
2491                                                        data.y[i],
2492                                                        data.dy[i]))
2493                    else:
2494                        out.write("%g  %g  %g\n" % (data.x[i],
2495                                                    data.y[i],
2496                                                    data.dy[i]))
2497                else:
2498                    out.write("%g  %g\n" % (data.x[i],
2499                                            data.y[i]))
2500            out.close()
2501
2502    def show_data1d(self, data, name):
2503        """
2504        Show data dialog
2505        """
2506        try:
2507            xmin = min(data.x)
2508            ymin = min(data.y)
2509        except:
2510            msg = "Unable to find min/max of \n data named %s" % \
2511                        data.filename
2512            wx.PostEvent(self, StatusEvent(status=msg,
2513                                           info="error"))
2514            raise ValueError, msg
2515        ## text = str(data)
2516        text = data.__str__()
2517        text += 'Data Min Max:\n'
2518        text += 'X_min = %s:  X_max = %s\n' % (xmin, max(data.x))
2519        text += 'Y_min = %s:  Y_max = %s\n' % (ymin, max(data.y))
2520        if data.dy != None:
2521            text += 'dY_min = %s:  dY_max = %s\n' % (min(data.dy), max(data.dy))
2522        text += '\nData Points:\n'
2523        x_st = "X"
2524        for index in range(len(data.x)):
2525            if data.dy != None and len(data.dy) > index:
2526                dy_val = data.dy[index]
2527            else:
2528                dy_val = 0.0
2529            if data.dx != None and len(data.dx) > index:
2530                dx_val = data.dx[index]
2531            else:
2532                dx_val = 0.0
2533            if data.dxl != None and len(data.dxl) > index:
2534                if index == 0:
2535                    x_st = "Xl"
2536                dx_val = data.dxl[index]
2537            elif data.dxw != None and len(data.dxw) > index:
2538                if index == 0:
2539                    x_st = "Xw"
2540                dx_val = data.dxw[index]
2541
2542            if index == 0:
2543                text += "<index> \t<X> \t<Y> \t<dY> \t<d%s>\n" % x_st
2544            text += "%s \t%s \t%s \t%s \t%s\n" % (index,
2545                                                  data.x[index],
2546                                                  data.y[index],
2547                                                  dy_val,
2548                                                  dx_val)
2549        from pdfview import TextFrame
2550        frame = TextFrame(None, -1, "Data Info: %s" % data.name, text)
2551        # put icon
2552        self.put_icon(frame)
2553        frame.Show(True)
2554
2555    def save_data2d(self, data, fname):
2556        """
2557        Save data2d dialog
2558        """
2559        default_name = fname
2560        wildcard = "IGOR/DAT 2D file in Q_map (*.dat)|*.DAT"
2561        dlg = wx.FileDialog(self, "Choose a file",
2562                            self._default_save_location,
2563                            default_name, wildcard, wx.SAVE)
2564
2565        if dlg.ShowModal() == wx.ID_OK:
2566            path = dlg.GetPath()
2567            # ext_num = 0 for .txt, ext_num = 1 for .xml
2568            # This is MAC Fix
2569            ext_num = dlg.GetFilterIndex()
2570            if ext_num == 0:
2571                format = '.dat'
2572            else:
2573                format = ''
2574            path = os.path.splitext(path)[0] + format
2575            mypath = os.path.basename(path)
2576
2577            #Instantiate a loader
2578            loader = Loader()
2579
2580            format = ".dat"
2581            if os.path.splitext(mypath)[1].lower() == format:
2582                # Make sure the ext included in the file name
2583                # especially on MAC
2584                fileName = os.path.splitext(path)[0] + format
2585                loader.save(fileName, data, format)
2586            try:
2587                self._default_save_location = os.path.dirname(path)
2588            except:
2589                pass
2590        dlg.Destroy()
2591
2592    def show_data2d(self, data, name):
2593        """
2594        Show data dialog
2595        """
2596        wx.PostEvent(self, StatusEvent(status="Gathering Data2D Info.",
2597                                       type='start'))
2598        text = data.__str__()
2599        text += 'Data Min Max:\n'
2600        text += 'I_min = %s\n' % min(data.data)
2601        text += 'I_max = %s\n\n' % max(data.data)
2602        text += 'Data (First 2501) Points:\n'
2603        text += 'Data columns include err(I).\n'
2604        text += 'ASCII data starts here.\n'
2605        text += "<index> \t<Qx> \t<Qy> \t<I> \t<dI> \t<dQparal> \t<dQperp>\n"
2606        di_val = 0.0
2607        dx_val = 0.0
2608        dy_val = 0.0
2609        len_data = len(data.qx_data)
2610        for index in xrange(0, len_data):
2611            x_val = data.qx_data[index]
2612            y_val = data.qy_data[index]
2613            i_val = data.data[index]
2614            if data.err_data != None:
2615                di_val = data.err_data[index]
2616            if data.dqx_data != None:
2617                dx_val = data.dqx_data[index]
2618            if data.dqy_data != None:
2619                dy_val = data.dqy_data[index]
2620
2621            text += "%s \t%s \t%s \t%s \t%s \t%s \t%s\n" % (index,
2622                                                            x_val,
2623                                                            y_val,
2624                                                            i_val,
2625                                                            di_val,
2626                                                            dx_val,
2627                                                            dy_val)
2628            # Takes too long time for typical data2d: Break here
2629            if index >= 2500:
2630                text += ".............\n"
2631                break
2632
2633        from pdfview import TextFrame
2634        frame = TextFrame(None, -1, "Data Info: %s" % data.name, text)
2635        # put icon
2636        self.put_icon(frame)
2637        frame.Show(True)
2638        wx.PostEvent(self, StatusEvent(status="Data2D Info Displayed",
2639                                       type='stop'))
2640
2641    def set_current_perspective(self, perspective):
2642        """
2643        set the current active perspective
2644        """
2645        self._current_perspective = perspective
2646        name = "No current analysis selected"
2647        if self._current_perspective is not None:
2648            self._add_current_plugin_menu()
2649            for panel in self.panels.values():
2650                if hasattr(panel, 'CENTER_PANE') and panel.CENTER_PANE:
2651                    for name in self._current_perspective.get_perspective():
2652                        frame = panel.get_frame()
2653                        if frame != None:
2654                            if name == panel.window_name:
2655                                panel.on_set_focus(event=None)
2656                                frame.Show(True)
2657                            else:
2658                                frame.Show(False)
2659            name = self._current_perspective.sub_menu
2660            if self._data_panel is not None:
2661                self._data_panel.set_active_perspective(name)
2662                self._check_applications_menu()
2663            #Set the SasView title
2664            self._set_title_name(name)
2665
2666    def _set_title_name(self, name):
2667        """
2668        Set the SasView title w/ the current application name
2669
2670        : param name: application name [string]
2671        """
2672        # Set SanView Window title w/ application anme
2673        title = self.title + "  - " + name + " -"
2674        self.SetTitle(title)
2675
2676    def _check_applications_menu(self):
2677        """
2678        check the menu of the current application
2679        """
2680        if self._applications_menu is not None:
2681            for menu in self._applications_menu.GetMenuItems():
2682                if self._current_perspective is not None:
2683                    name = self._current_perspective.sub_menu
2684                    if menu.IsCheckable():
2685                        if menu.GetLabel() == name:
2686                            menu.Check(True)
2687                        else:
2688                            menu.Check(False)
2689
2690    def enable_add_data(self, new_plot):
2691        """
2692        Enable append data on a plot panel
2693        """
2694
2695        if self.panel_on_focus not in self._plotting_plugin.plot_panels.values():
2696            return
2697        is_theory = len(self.panel_on_focus.plots) <= 1 and \
2698            self.panel_on_focus.plots.values()[0].__class__.__name__ == "Theory1D"
2699
2700        is_data2d = hasattr(new_plot, 'data')
2701
2702        is_data1d = self.panel_on_focus.__class__.__name__ == "ModelPanel1D"\
2703            and self.panel_on_focus.group_id is not None
2704        has_meta_data = hasattr(new_plot, 'meta_data')
2705
2706        #disable_add_data if the data is being recovered from  a saved state file.
2707        is_state_data = False
2708        if has_meta_data:
2709            if 'invstate' in new_plot.meta_data:
2710                is_state_data = True
2711            if  'prstate' in new_plot.meta_data:
2712                is_state_data = True
2713            if  'fitstate' in new_plot.meta_data:
2714                is_state_data = True
2715
2716        return is_data1d and not is_data2d and not is_theory and not is_state_data
2717
2718    def check_multimode(self, perspective=None):
2719        """
2720        Check the perspective have batch mode capablitity
2721        """
2722        if perspective == None or self._data_panel == None:
2723            return
2724        flag = perspective.get_batch_capable()
2725        flag_on = perspective.batch_on
2726        if flag:
2727            self._data_panel.rb_single_mode.SetValue(not flag_on)
2728            self._data_panel.rb_batch_mode.SetValue(flag_on)
2729        else:
2730            self._data_panel.rb_single_mode.SetValue(True)
2731            self._data_panel.rb_batch_mode.SetValue(False)
2732        self._data_panel.rb_single_mode.Enable(flag)
2733        self._data_panel.rb_batch_mode.Enable(flag)
2734
2735    def enable_edit_menu(self):
2736        """
2737        enable menu item under edit menu depending on the panel on focus
2738        """
2739        if self.cpanel_on_focus is not None and self._edit_menu is not None:
2740            flag = self.cpanel_on_focus.get_undo_flag()
2741            self._edit_menu.Enable(GUIFRAME_ID.UNDO_ID, flag)
2742            flag = self.cpanel_on_focus.get_redo_flag()
2743            self._edit_menu.Enable(GUIFRAME_ID.REDO_ID, flag)
2744            flag = self.cpanel_on_focus.get_copy_flag()
2745            self._edit_menu.Enable(GUIFRAME_ID.COPY_ID, flag)
2746            flag = self.cpanel_on_focus.get_paste_flag()
2747            self._edit_menu.Enable(GUIFRAME_ID.PASTE_ID, flag)
2748
2749            #Copy menu
2750            flag = self.cpanel_on_focus.get_copy_flag()
2751            self._edit_menu_copyas.Enable(GUIFRAME_ID.COPYEX_ID, flag)
2752            self._edit_menu_copyas.Enable(GUIFRAME_ID.COPYLAT_ID, flag)
2753
2754            flag = self.cpanel_on_focus.get_preview_flag()
2755            self._edit_menu.Enable(GUIFRAME_ID.PREVIEW_ID, flag)
2756            flag = self.cpanel_on_focus.get_reset_flag()
2757            self._edit_menu.Enable(GUIFRAME_ID.RESET_ID, flag)
2758        else:
2759            flag = False
2760            self._edit_menu.Enable(GUIFRAME_ID.UNDO_ID, flag)
2761            self._edit_menu.Enable(GUIFRAME_ID.REDO_ID, flag)
2762            self._edit_menu.Enable(GUIFRAME_ID.COPY_ID, flag)
2763            self._edit_menu.Enable(GUIFRAME_ID.PASTE_ID, flag)
2764            self._edit_menu.Enable(GUIFRAME_ID.PREVIEW_ID, flag)
2765            self._edit_menu.Enable(GUIFRAME_ID.RESET_ID, flag)
2766
2767    def on_undo_panel(self, event=None):
2768        """
2769        undo previous action of the last panel on focus if possible
2770        """
2771        if self.cpanel_on_focus is not None:
2772            self.cpanel_on_focus.on_undo(event)
2773
2774    def on_redo_panel(self, event=None):
2775        """
2776        redo the last cancel action done on the last panel on focus
2777        """
2778        if self.cpanel_on_focus is not None:
2779            self.cpanel_on_focus.on_redo(event)
2780
2781    def on_copy_panel(self, event=None):
2782        """
2783        copy the last panel on focus if possible
2784        """
2785        if self.cpanel_on_focus is not None:
2786            self.cpanel_on_focus.on_copy(event)
2787
2788    def on_paste_panel(self, event=None):
2789        """
2790        paste clipboard to the last panel on focus
2791        """
2792        if self.cpanel_on_focus is not None:
2793            self.cpanel_on_focus.on_paste(event)
2794
2795    def on_bookmark_panel(self, event=None):
2796        """
2797        bookmark panel
2798        """
2799        if self.cpanel_on_focus is not None:
2800            self.cpanel_on_focus.on_bookmark(event)
2801
2802    def append_bookmark(self, event=None):
2803        """
2804        Bookmark available information of the panel on focus
2805        """
2806        self._toolbar.append_bookmark(event)
2807
2808    def on_save_panel(self, event=None):
2809        """
2810        save possible information on the current panel
2811        """
2812        if self.cpanel_on_focus is not None:
2813            self.cpanel_on_focus.on_save(event)
2814
2815    def on_preview_panel(self, event=None):
2816        """
2817        preview information on the panel on focus
2818        """
2819        if self.cpanel_on_focus is not None:
2820            self.cpanel_on_focus.on_preview(event)
2821
2822    def on_print_panel(self, event=None):
2823        """
2824        print available information on the last panel on focus
2825        """
2826        if self.cpanel_on_focus is not None:
2827            self.cpanel_on_focus.on_print(event)
2828
2829    def on_zoom_panel(self, event=None):
2830        """
2831        zoom on the current panel if possible
2832        """
2833        if self.cpanel_on_focus is not None:
2834            self.cpanel_on_focus.on_zoom(event)
2835
2836    def on_zoom_in_panel(self, event=None):
2837        """
2838        zoom in of the panel on focus
2839        """
2840        if self.cpanel_on_focus is not None:
2841            self.cpanel_on_focus.on_zoom_in(event)
2842
2843    def on_zoom_out_panel(self, event=None):
2844        """
2845        zoom out on the panel on focus
2846        """
2847        if self.cpanel_on_focus is not None:
2848            self.cpanel_on_focus.on_zoom_out(event)
2849
2850    def on_drag_panel(self, event=None):
2851        """
2852        drag apply to the panel on focus
2853        """
2854        if self.cpanel_on_focus is not None:
2855            self.cpanel_on_focus.on_drag(event)
2856
2857    def on_reset_panel(self, event=None):
2858        """
2859        reset the current panel
2860        """
2861        if self.cpanel_on_focus is not None:
2862            self.cpanel_on_focus.on_reset(event)
2863
2864    def on_change_caption(self, name, old_caption, new_caption):
2865        """
2866        Change the panel caption
2867
2868        :param name: window_name of the pane
2869        :param old_caption: current caption [string]
2870        :param new_caption: new caption [string]
2871        """
2872        # wx.aui.AuiPaneInfo
2873        pane_info = self.get_paneinfo(old_caption)
2874        # update the data_panel.cb_plotpanel
2875        if 'data_panel' in self.panels.keys():
2876            # remove from data_panel combobox
2877            data_panel = self.panels["data_panel"]
2878            if data_panel.cb_plotpanel is not None:
2879                # Check if any panel has the same caption
2880                has_newstring = data_panel.cb_plotpanel.FindString\
2881                                                            (str(new_caption))
2882                caption = new_caption
2883                if has_newstring != wx.NOT_FOUND:
2884                    captions = self._get_plotpanel_captions()
2885                    # Append nummber
2886                    inc = 1
2887                    #FIXME: fix this terrible loop
2888                    while (1):
2889                        caption = new_caption + '_%s' % str(inc)
2890                        if caption not in captions:
2891                            break
2892                        inc += 1
2893                    # notify to users
2894                    msg = "Found Same Title: Added '_%s'" % str(inc)
2895                    wx.PostEvent(self, StatusEvent(status=msg))
2896                # update data_panel cb
2897                pos = data_panel.cb_plotpanel.FindString(str(old_caption))
2898                if pos != wx.NOT_FOUND:
2899                    data_panel.cb_plotpanel.SetString(pos, caption)
2900                    data_panel.cb_plotpanel.SetStringSelection(caption)
2901        # New Caption
2902        pane_info.SetTitle(caption)
2903        return caption
2904
2905    def get_paneinfo(self, name):
2906        """
2907        Get pane Caption from window_name
2908
2909        :param name: window_name in AuiPaneInfo
2910        :return: AuiPaneInfo of the name
2911        """
2912        for panel in self.plot_panels.values():
2913            if panel.frame.GetTitle() == name:
2914                return panel.frame
2915        return None
2916
2917    def enable_undo(self):
2918        """
2919        enable undo related control
2920        """
2921        if self.cpanel_on_focus is not None:
2922            self._toolbar.enable_undo(self.cpanel_on_focus)
2923
2924    def enable_redo(self):
2925        """
2926        enable redo
2927        """
2928        if self.cpanel_on_focus is not None:
2929            self._toolbar.enable_redo(self.cpanel_on_focus)
2930
2931    def enable_copy(self):
2932        """
2933        enable copy related control
2934        """
2935        if self.cpanel_on_focus is not None:
2936            self._toolbar.enable_copy(self.cpanel_on_focus)
2937
2938    def enable_paste(self):
2939        """
2940        enable paste
2941        """
2942        if self.cpanel_on_focus is not None:
2943            self._toolbar.enable_paste(self.cpanel_on_focus)
2944
2945    def enable_bookmark(self):
2946        """
2947        Bookmark
2948        """
2949        if self.cpanel_on_focus is not None:
2950            self._toolbar.enable_bookmark(self.cpanel_on_focus)
2951
2952    def enable_save(self):
2953        """
2954        save
2955        """
2956        if self.cpanel_on_focus is not None:
2957            self._toolbar.enable_save(self.cpanel_on_focus)
2958
2959    def enable_preview(self):
2960        """
2961        preview
2962        """
2963        if self.cpanel_on_focus is not None:
2964            self._toolbar.enable_preview(self.cpanel_on_focus)
2965
2966    def enable_print(self):
2967        """
2968        print
2969        """
2970        if self.cpanel_on_focus is not None:
2971            self._toolbar.enable_print(self.cpanel_on_focus)
2972
2973    def enable_zoom(self):
2974        """
2975        zoom
2976        """
2977        if self.cpanel_on_focus is not None:
2978            self._toolbar.enable_zoom(self.panel_on_focus)
2979
2980    def enable_zoom_in(self):
2981        """
2982        zoom in
2983        """
2984        if self.cpanel_on_focus is not None:
2985            self._toolbar.enable_zoom_in(self.panel_on_focus)
2986
2987    def enable_zoom_out(self):
2988        """
2989        zoom out
2990        """
2991        if self.cpanel_on_focus is not None:
2992            self._toolbar.enable_zoom_out(self.panel_on_focus)
2993
2994    def enable_drag(self, event=None):
2995        """
2996        drag
2997        """
2998        #Not implemeted
2999
3000    def enable_reset(self):
3001        """
3002        reset the current panel
3003        """
3004        if self.cpanel_on_focus is not None:
3005            self._toolbar.enable_reset(self.panel_on_focus)
3006
3007    def get_toolbar_height(self):
3008        """
3009        """
3010        size_y = 0
3011        if self.GetToolBar() != None and self.GetToolBar().IsShown():
3012            if not IS_LINUX:
3013                _, size_y = self.GetToolBar().GetSizeTuple()
3014        return size_y
3015
3016    def set_schedule_full_draw(self, panel=None, func='del'):
3017        """
3018        Add/subtract the schedule full draw list with the panel given
3019
3020        :param panel: plot panel
3021        :param func: append or del [string]
3022        """
3023
3024        # append this panel in the schedule list if not in yet
3025        if func == 'append':
3026            if not panel in self.schedule_full_draw_list:
3027                self.schedule_full_draw_list.append(panel)
3028        # remove this panel from schedule list
3029        elif func == 'del':
3030            if len(self.schedule_full_draw_list) > 0:
3031                if panel in self.schedule_full_draw_list:
3032                    self.schedule_full_draw_list.remove(panel)
3033
3034        # reset the schdule
3035        if len(self.schedule_full_draw_list) == 0:
3036            self.schedule = False
3037        else:
3038            self.schedule = True
3039
3040    def full_draw(self):
3041        """
3042        Draw the panels with axes in the schedule to full dwar list
3043        """
3044
3045        count = len(self.schedule_full_draw_list)
3046        #if not self.schedule:
3047        if count < 1:
3048            self.set_schedule(False)
3049            return
3050
3051        else:
3052            ind = 0
3053            # if any of the panel is shown do full_draw
3054            for panel in self.schedule_full_draw_list:
3055                ind += 1
3056                if panel.frame.IsShown():
3057                    break
3058                # otherwise, return
3059                if ind == count:
3060                    return
3061        #Simple redraw only for a panel shown
3062        def f_draw(panel):
3063            """
3064            Draw A panel in the full draw list
3065            """
3066            try:
3067                # This checking of GetCapture is to stop redrawing
3068                # while any panel is capture.
3069                frame = panel.frame
3070
3071                if not frame.GetCapture():
3072                    # draw if possible
3073                    panel.set_resizing(False)
3074                    #panel.Show(True)
3075                    panel.draw_plot()
3076                # Check if the panel is not shown
3077                flag = frame.IsShown()
3078                frame.Show(flag)
3079            except:
3080                pass
3081
3082        # Draw all panels
3083        if count == 1:
3084            f_draw(self.schedule_full_draw_list[0])
3085        else:
3086            map(f_draw, self.schedule_full_draw_list)
3087        # Reset the attr 
3088        if len(self.schedule_full_draw_list) == 0:
3089            self.set_schedule(False)
3090        else:
3091            self.set_schedule(True)
3092
3093    def set_schedule(self, schedule=False):
3094        """
3095        Set schedule
3096        """
3097        self.schedule = schedule
3098
3099    def get_schedule(self):
3100        """
3101        Get schedule
3102        """
3103        return self.schedule
3104
3105    def on_set_plot_focus(self, panel):
3106        """
3107        Set focus on a plot panel
3108        """
3109        if panel == None:
3110            return
3111        #self.set_plot_unfocus()
3112        panel.on_set_focus(None)
3113        # set focusing panel
3114        self.panel_on_focus = panel
3115        self.set_panel_on_focus(None)
3116
3117    def set_plot_unfocus(self):
3118        """
3119        Un focus all plot panels
3120        """
3121        for plot in self.plot_panels.values():
3122            plot.on_kill_focus(None)
3123
3124    def get_window_size(self):
3125        """
3126        Get window size
3127
3128        :return size: tuple
3129        """
3130        width, height = self.GetSizeTuple()
3131        if not IS_WIN:
3132            # Subtract toolbar height to get real window side
3133            if self._toolbar.IsShown():
3134                height -= 45
3135        return (width, height)
3136
3137    def _onDrawIdle(self, *args, **kwargs):
3138        """
3139        ReDraw with axes
3140        """
3141        try:
3142            # check if it is time to redraw
3143            if self.GetCapture() == None:
3144                # Draw plot, changes resizing too
3145                self.full_draw()
3146        except:
3147            pass
3148
3149        # restart idle       
3150        self._redraw_idle(*args, **kwargs)
3151
3152
3153    def _redraw_idle(self, *args, **kwargs):
3154        """
3155        Restart Idle
3156        """
3157        # restart idle   
3158        self.idletimer.Restart(100 * TIME_FACTOR, *args, **kwargs)
3159
3160
3161class DefaultPanel(wx.Panel, PanelBase):
3162    """
3163    Defines the API for a panels to work with
3164    the GUI manager
3165    """
3166    ## Internal nickname for the window, used by the AUI manager
3167    window_name = "default"
3168    ## Name to appear on the window title bar
3169    window_caption = "Welcome panel"
3170    ## Flag to tell the AUI manager to put this panel in the center pane
3171    CENTER_PANE = True
3172    def __init__(self, parent, *args, **kwds):
3173        wx.Panel.__init__(self, parent, *args, **kwds)
3174        PanelBase.__init__(self, parent)
3175
3176
3177
3178class ViewApp(wx.App):
3179    """
3180    Toy application to test this Frame
3181    """
3182    def OnInit(self):
3183        """
3184        When initialised
3185        """
3186        pos, size, self.is_max = self.window_placement((GUIFRAME_WIDTH,
3187                                                        GUIFRAME_HEIGHT))
3188        self.frame = ViewerFrame(parent=None,
3189                                 title=APPLICATION_NAME,
3190                                 pos=pos,
3191                                 gui_style=DEFAULT_STYLE,
3192                                 size=size)
3193        self.frame.Hide()
3194        if not IS_WIN:
3195            self.frame.EnableCloseButton(False)
3196        self.s_screen = None
3197
3198        try:
3199            self.open_file()
3200        except:
3201            msg = "%s Could not load " % str(APPLICATION_NAME)
3202            msg += "input file from command line.\n"
3203            logging.error(msg)
3204        # Display a splash screen on top of the frame.
3205        try:
3206            if os.path.isfile(SPLASH_SCREEN_PATH):
3207                self.s_screen = self.display_splash_screen(parent=self.frame,
3208                                                           path=SPLASH_SCREEN_PATH)
3209            else:
3210                self.frame.Show()
3211        except:
3212            if self.s_screen is not None:
3213                self.s_screen.Close()
3214            msg = "Cannot display splash screen\n"
3215            msg += str(sys.exc_value)
3216            logging.error(msg)
3217            self.frame.Show()
3218
3219        self.SetTopWindow(self.frame)
3220
3221        return True
3222
3223    def maximize_win(self):
3224        """
3225        Maximize the window after the frame shown
3226        """
3227        if self.is_max:
3228            if self.frame.IsShown():
3229                # Max window size
3230                self.frame.Maximize(self.is_max)
3231
3232    def open_file(self):
3233        """
3234        open a state file at the start of the application
3235        """
3236        input_file = None
3237        if len(sys.argv) >= 2:
3238            cmd = sys.argv[0].lower()
3239            basename = os.path.basename(cmd)
3240            app_base = str(APPLICATION_NAME).lower()
3241            if os.path.isfile(cmd) or basename.lower() == app_base:
3242                app_py = app_base + '.py'
3243                app_exe = app_base + '.exe'
3244                app_app = app_base + '.app'
3245                if basename.lower() in [app_py, app_exe, app_app, app_base]:
3246                    data_base = sys.argv[1]
3247                    input_file = os.path.normpath(os.path.join(DATAPATH,
3248                                                               data_base))
3249        if input_file is None:
3250            return
3251        if self.frame is not None:
3252            self.frame.set_input_file(input_file=input_file)
3253
3254    def clean_plugin_models(self, path):
3255        """
3256        Delete plugin models  in app folder
3257
3258        :param path: path of the plugin_models folder in app
3259        """
3260        # do it only the first time app loaded
3261        # delete unused model folder
3262        model_folder = os.path.join(PATH_APP, path)
3263        if os.path.exists(model_folder) and os.path.isdir(model_folder):
3264            if len(os.listdir(model_folder)) > 0:
3265                try:
3266                    for file in os.listdir(model_folder):
3267                        file_path = os.path.join(model_folder, file)
3268                        if os.path.isfile(file_path):
3269                            os.remove(file_path)
3270                except:
3271                    logging.error("gui_manager.clean_plugin_models:\n  %s" \
3272                                  % sys.exc_value)
3273
3274    def set_manager(self, manager):
3275        """
3276        Sets a reference to the application manager
3277        of the GUI manager (Frame)
3278        """
3279        self.frame.set_manager(manager)
3280
3281    def build_gui(self):
3282        """
3283        Build the GUI
3284        """
3285        #try to load file at the start
3286        try:
3287            self.open_file()
3288        except:
3289            raise
3290        self.frame.build_gui()
3291
3292    def set_welcome_panel(self, panel_class):
3293        """
3294        Set the welcome panel
3295
3296        :param panel_class: class of the welcome panel to be instantiated
3297
3298        """
3299        self.frame.welcome_panel_class = panel_class
3300
3301    def add_perspective(self, perspective):
3302        """
3303        Manually add a perspective to the application GUI
3304        """
3305        self.frame.add_perspective(perspective)
3306
3307    def window_placement(self, size):
3308        """
3309        Determines the position and size of the application frame such that it
3310        fits on the user's screen without obstructing (or being obstructed by)
3311        the Windows task bar.  The maximum initial size in pixels is bounded by
3312        WIDTH x HEIGHT.  For most monitors, the application
3313        will be centered on the screen; for very large monitors it will be
3314        placed on the left side of the screen.
3315        """
3316        is_maximized = False
3317        # Get size of screen without
3318        for screenCount in range(wx.Display().GetCount()):
3319            screen = wx.Display(screenCount)
3320            if screen.IsPrimary():
3321                displayRect = screen.GetClientArea()
3322                break
3323
3324        posX, posY, displayWidth, displayHeight = displayRect
3325        customWidth, customHeight = size
3326
3327        # If the custom size is default, set 90% of the screen size
3328        if customWidth <= 0 and customHeight <= 0:
3329            if customWidth == 0 and customHeight == 0:
3330                is_maximized = True
3331            customWidth = displayWidth * 0.9
3332            customHeight = displayHeight * 0.9
3333        else:
3334            # If the custom screen is bigger than the
3335            # window screen than make maximum size
3336            if customWidth > displayWidth:
3337                customWidth = displayWidth
3338            if customHeight > displayHeight:
3339                customHeight = displayHeight
3340
3341        # Note that when running Linux and using an Xming (X11) server on a PC
3342        # with a dual  monitor configuration, the reported display size may be
3343        # that of both monitors combined with an incorrect display count of 1.
3344        # To avoid displaying this app across both monitors, we check for
3345        # screen 'too big'.  If so, we assume a smaller width which means the
3346        # application will be placed towards the left hand side of the screen.
3347
3348        # If dual screen registered as 1 screen. Make width half.
3349        # MAC just follows the default behavior of pos
3350        if IS_WIN:
3351            if displayWidth > (displayHeight * 2):
3352                if customWidth == displayWidth:
3353                    customWidth = displayWidth / 2
3354                # and set the position to be the corner of the screen.
3355                posX = 0
3356                posY = 0
3357
3358            # Make the position the middle of the screen. (Not 0,0)
3359            else:
3360                posX = (displayWidth - customWidth) / 2
3361                posY = (displayHeight - customHeight) / 2
3362        # Return the suggested position and size for the application frame.
3363        return (posX, posY), (customWidth, customHeight), is_maximized
3364
3365
3366    def display_splash_screen(self, parent,
3367                              path=SPLASH_SCREEN_PATH):
3368        """Displays the splash screen.  It will exactly cover the main frame."""
3369
3370        # Prepare the picture.  On a 2GHz intel cpu, this takes about a second.
3371        image = wx.Image(path, wx.BITMAP_TYPE_PNG)
3372        image.Rescale(SPLASH_SCREEN_WIDTH,
3373                      SPLASH_SCREEN_HEIGHT, wx.IMAGE_QUALITY_HIGH)
3374        bm = image.ConvertToBitmap()
3375
3376        # Create and show the splash screen.  It will disappear only when the
3377        # program has entered the event loop AND either the timeout has expired
3378        # or the user has left clicked on the screen.  Thus any processing
3379        # performed in this routine (including sleeping) or processing in the
3380        # calling routine (including doing imports) will prevent the splash
3381        # screen from disappearing.
3382        #
3383        # Note that on Linux, the timeout appears to occur immediately in which
3384        # case the splash screen disappears upon entering the event loop.
3385        s_screen = wx.SplashScreen(bitmap=bm,
3386                                   splashStyle=(wx.SPLASH_TIMEOUT |
3387                                                wx.SPLASH_CENTRE_ON_SCREEN),
3388                                   style=(wx.SIMPLE_BORDER |
3389                                          wx.FRAME_NO_TASKBAR |
3390                                          wx.FRAME_FLOAT_ON_PARENT),
3391                                   milliseconds=SS_MAX_DISPLAY_TIME,
3392                                   parent=parent,
3393                                   id=wx.ID_ANY)
3394        from sas.guiframe.gui_statusbar import SPageStatusbar
3395        statusBar = SPageStatusbar(s_screen)
3396        s_screen.SetStatusBar(statusBar)
3397        s_screen.Bind(wx.EVT_CLOSE, self.on_close_splash_screen)
3398        s_screen.Show()
3399        return s_screen
3400
3401
3402    def on_close_splash_screen(self, event):
3403        """
3404        When the splash screen is closed.
3405        """
3406        self.frame.Show(True)
3407        event.Skip()
3408        self.maximize_win()
3409
3410
3411class MDIFrame(CHILD_FRAME):
3412    """
3413    Frame for panels
3414    """
3415    def __init__(self, parent, panel, title="Untitled", size=(300, 200)):
3416        """
3417        comment
3418        :param parent: parent panel/container
3419        """
3420        # Initialize the Frame object
3421        CHILD_FRAME.__init__(self, parent=parent, id=wx.ID_ANY, title=title, size=size)
3422        self.parent = parent
3423        self.name = "Untitled"
3424        self.batch_on = self.parent.batch_on
3425        self.panel = panel
3426        if panel != None:
3427            self.set_panel(panel)
3428        self.Show(False)
3429
3430    def show_data_panel(self, action):
3431        """
3432        """
3433        self.parent.show_data_panel(action)
3434
3435    def set_panel(self, panel):
3436        """
3437        """
3438        self.panel = panel
3439        self.name = panel.window_name
3440        self.SetTitle(panel.window_caption)
3441        self.SetHelpText(panel.help_string)
3442        width, height = self.parent._get_panels_size(panel)
3443        if hasattr(panel, "CENTER_PANE") and panel.CENTER_PANE:
3444            width *= 0.6
3445        self.SetSize((width, height))
3446        self.parent.put_icon(self)
3447        self.Bind(wx.EVT_SET_FOCUS, self.set_panel_focus)
3448        self.Bind(wx.EVT_CLOSE, self.OnClose)
3449        self.Show(False)
3450
3451    def set_panel_focus(self, event):
3452        """
3453        """
3454        if self.parent.panel_on_focus != self.panel:
3455            self.panel.SetFocus()
3456            self.parent.panel_on_focus = self.panel
3457
3458    def OnClose(self, event):
3459        """
3460        On Close event
3461        """
3462        self.panel.on_close(event)
3463
3464if __name__ == "__main__":
3465    app = ViewApp(0)
3466    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.