source: sasview/src/sas/sasgui/guiframe/gui_manager.py @ 7d265a8

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 7d265a8 was 0d534de, checked in by butler, 9 years ago

Committed too fast - final fix to menu order

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