source: sasview/src/sas/sasgui/guiframe/gui_manager.py @ 48b8f6d

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.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 48b8f6d was adb0851, checked in by mathieu, 8 years ago

Add link to marketplace. Fixes #656

  • Property mode set to 100644
File size: 125.5 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 _on_marketplace_click(self, event):
1336        """
1337            Click event for the help menu item linking to the Marketplace.
1338        """
1339        import webbrowser
1340        webbrowser.open_new(config.marketplace_url)
1341
1342    def _add_help_menu(self):
1343        """
1344        add help menu to menu bar.  Includes welcome page, about page,
1345        tutorial PDF and documentation pages.
1346        """
1347        self._help_menu = wx.Menu()
1348
1349        wx_id = wx.NewId()
1350        self._help_menu.Append(wx_id, '&Documentation', '')
1351        wx.EVT_MENU(self, wx_id, self._onSphinxDocs)
1352
1353        if config._do_tutorial and (IS_WIN or sys.platform == 'darwin'):
1354            wx_id = wx.NewId()
1355            self._help_menu.Append(wx_id, '&Tutorial', 'Software tutorial')
1356            wx.EVT_MENU(self, wx_id, self._onTutorial)
1357
1358        if config._do_acknowledge:
1359            wx_id = wx.NewId()
1360            self._help_menu.Append(wx_id, '&Acknowledge', 'Acknowledging SasView')
1361            wx.EVT_MENU(self, wx_id, self._onAcknowledge)
1362
1363        if config._do_aboutbox:
1364            logging.info("Doing help menu")
1365            wx_id = wx.NewId()
1366            self._help_menu.Append(wx_id, '&About', 'Software information')
1367            wx.EVT_MENU(self, wx_id, self._onAbout)
1368
1369        if config.marketplace_url:
1370            wx_id = wx.NewId()
1371            self._help_menu.Append(wx_id, '&Model marketplace', '')
1372            wx.EVT_MENU(self, wx_id, self._on_marketplace_click)
1373
1374        # Checking for updates
1375        wx_id = wx.NewId()
1376        self._help_menu.Append(wx_id, '&Check for update',
1377                               'Check for the latest version of %s' % config.__appname__)
1378        wx.EVT_MENU(self, wx_id, self._check_update)
1379        self._menubar.Append(self._help_menu, '&Help')
1380
1381    def _add_menu_view(self):
1382        """
1383        add menu items under view menu
1384        """
1385        if not VIEW_MENU:
1386            return
1387        self._view_menu = wx.Menu()
1388
1389        wx_id = wx.NewId()
1390        hint = "Display the Grid Window for batch results etc."
1391        self._view_menu.Append(wx_id, '&Show Grid Window', hint)
1392        wx.EVT_MENU(self, wx_id, self.show_batch_frame)
1393
1394        self._view_menu.AppendSeparator()
1395        style = self.__gui_style & GUIFRAME.MANAGER_ON
1396        wx_id = wx.NewId()
1397        self._data_panel_menu = self._view_menu.Append(wx_id,
1398                                                       '&Show Data Explorer', '')
1399        wx.EVT_MENU(self, wx_id, self.show_data_panel)
1400        if style == GUIFRAME.MANAGER_ON:
1401            self._data_panel_menu.SetText('Hide Data Explorer')
1402        else:
1403            self._data_panel_menu.SetText('Show Data Explorer')
1404
1405        self._view_menu.AppendSeparator()
1406        wx_id = wx.NewId()
1407        style1 = self.__gui_style & GUIFRAME.TOOLBAR_ON
1408        if style1 == GUIFRAME.TOOLBAR_ON:
1409            self._toolbar_menu = self._view_menu.Append(wx_id, '&Hide Toolbar', '')
1410        else:
1411            self._toolbar_menu = self._view_menu.Append(wx_id, '&Show Toolbar', '')
1412        wx.EVT_MENU(self, wx_id, self._on_toggle_toolbar)
1413
1414        if custom_config != None:
1415            self._view_menu.AppendSeparator()
1416            wx_id = wx.NewId()
1417            hint_ss = "Select the current/default configuration "
1418            hint_ss += "as a startup setting"
1419            preference_menu = self._view_menu.Append(wx_id, 'Startup Setting',
1420                                                     hint_ss)
1421            wx.EVT_MENU(self, wx_id, self._on_preference_menu)
1422
1423        wx_id = wx.NewId()
1424        self._view_menu.AppendSeparator()
1425        self._view_menu.Append(wx_id, 'Category Manager', 'Edit model categories')
1426        wx.EVT_MENU(self, wx_id, self._on_category_manager)
1427
1428        self._menubar.Append(self._view_menu, '&View')
1429
1430    def show_batch_frame(self, event=None):
1431        """
1432        show the grid of result
1433        """
1434        # Show(False) before Show(True) in order to bring it to the front
1435        self.batch_frame.Show(False)
1436        self.batch_frame.Show(True)
1437
1438    def  on_category_panel(self, event):
1439        """
1440        On cat panel
1441        """
1442        self._on_category_manager(event)
1443
1444    def _on_category_manager(self, event):
1445        """
1446        Category manager frame
1447        """
1448        frame = CategoryManager(self, -1, 'Model Category Manager')
1449        icon = self.GetIcon()
1450        frame.SetIcon(icon)
1451
1452    def _on_preference_menu(self, event):
1453        """
1454        Build a panel to allow to edit Mask
1455        """
1456        from sas.sasgui.guiframe.startup_configuration \
1457        import StartupConfiguration as ConfDialog
1458
1459        dialog = ConfDialog(parent=self, gui=self.__gui_style)
1460        result = dialog.ShowModal()
1461        if result == wx.ID_OK:
1462            dialog.write_custom_config()
1463            # post event for info
1464            wx.PostEvent(self, StatusEvent(status="Wrote custom configuration", info='info'))
1465        dialog.Destroy()
1466
1467    def _add_menu_application(self):
1468        """
1469        # Attach a menu item for each defined perspective or application.
1470        # Only add the perspective menu if there are more than one perspectives
1471        add menu application
1472        """
1473        if self._num_perspectives > 1:
1474            plug_data_count = False
1475            plug_no_data_count = False
1476            self._applications_menu = wx.Menu()
1477            pos = 0
1478            separator = self._applications_menu.AppendSeparator()
1479            for plug in self.plugins:
1480                if len(plug.get_perspective()) > 0:
1481                    id = wx.NewId()
1482                    if plug.use_data():
1483                        self._applications_menu.InsertCheckItem(pos, id, plug.sub_menu, \
1484                            "Switch to analysis: %s" % plug.sub_menu)
1485                        plug_data_count = True
1486                        pos += 1
1487                    else:
1488                        plug_no_data_count = True
1489                        self._applications_menu.AppendCheckItem(id, plug.sub_menu, \
1490                            "Switch to analysis: %s" % plug.sub_menu)
1491                    wx.EVT_MENU(self, id, plug.on_perspective)
1492
1493            if not plug_data_count or not plug_no_data_count:
1494                self._applications_menu.RemoveItem(separator)
1495            #Windows introduces a "Window" menu item during the layout process
1496            #somehow.  We want it to be next to the last item with Help as
1497            #last. However Analysis gets stuck after Window in normal ordering
1498            #so force it to be next after the Tools menu item.  Should we add
1499            #another menu item will need to check if this is still where we
1500            #want Analysis.  This is NOT an issue on the Mac which does not
1501            #have the extra Window menu item.
1502            #      March 2016 Code Camp  -- PDB
1503            Tools_pos = self._menubar.FindMenu("Tools")
1504            self._menubar.Insert(Tools_pos+1,self._applications_menu,
1505                                 '&Analysis')
1506            self._check_applications_menu()
1507
1508    def _populate_file_menu(self):
1509        """
1510        Insert menu item under file menu
1511        """
1512        for plugin in self.plugins:
1513            if len(plugin.populate_file_menu()) > 0:
1514                for item in plugin.populate_file_menu():
1515                    m_name, m_hint, m_handler = item
1516                    wx_id = wx.NewId()
1517                    self._file_menu.Append(wx_id, m_name, m_hint)
1518                    wx.EVT_MENU(self, wx_id, m_handler)
1519                self._file_menu.AppendSeparator()
1520
1521        style1 = self.__gui_style & GUIFRAME.MULTIPLE_APPLICATIONS
1522        if OPEN_SAVE_MENU:
1523            wx_id = wx.NewId()
1524            hint_load_file = "read all analysis states saved previously"
1525            self._save_appl_menu = self._file_menu.Append(wx_id, '&Open Project', hint_load_file)
1526            wx.EVT_MENU(self, wx_id, self._on_open_state_project)
1527
1528        if style1 == GUIFRAME.MULTIPLE_APPLICATIONS:
1529            # some menu of plugin to be seen under file menu
1530            hint_load_file = "Read a status files and load"
1531            hint_load_file += " them into the analysis"
1532            wx_id = wx.NewId()
1533            self._save_appl_menu = self._file_menu.Append(wx_id,
1534                                                          '&Open Analysis', hint_load_file)
1535            wx.EVT_MENU(self, wx_id, self._on_open_state_application)
1536        if OPEN_SAVE_MENU:
1537            self._file_menu.AppendSeparator()
1538            wx_id = wx.NewId()
1539            self._file_menu.Append(wx_id, '&Save Project',
1540                                   'Save the state of the whole analysis')
1541            wx.EVT_MENU(self, wx_id, self._on_save_project)
1542        if style1 == GUIFRAME.MULTIPLE_APPLICATIONS:
1543            wx_id = wx.NewId()
1544            self._save_appl_menu = self._file_menu.Append(wx_id, \
1545                '&Save Analysis', 'Save state of the current active analysis panel')
1546            wx.EVT_MENU(self, wx_id, self._on_save_application)
1547        if not sys.platform == 'darwin':
1548            self._file_menu.AppendSeparator()
1549            wx_id = wx.NewId()
1550            self._file_menu.Append(wx_id, '&Quit', 'Exit')
1551            wx.EVT_MENU(self, wx_id, self.Close)
1552
1553    def _add_menu_file(self):
1554        """
1555        add menu file
1556        """
1557        # File menu
1558        self._file_menu = wx.Menu()
1559        # Add sub menus
1560        self._menubar.Append(self._file_menu, '&File')
1561
1562    def _add_menu_edit(self):
1563        """
1564        add menu edit
1565        """
1566        if not EDIT_MENU:
1567            return
1568        # Edit Menu
1569        self._edit_menu = wx.Menu()
1570        self._edit_menu.Append(GUIFRAME_ID.UNDO_ID, '&Undo',
1571                               'Undo the previous action')
1572        wx.EVT_MENU(self, GUIFRAME_ID.UNDO_ID, self.on_undo_panel)
1573        self._edit_menu.Append(GUIFRAME_ID.REDO_ID, '&Redo',
1574                               'Redo the previous action')
1575        wx.EVT_MENU(self, GUIFRAME_ID.REDO_ID, self.on_redo_panel)
1576        self._edit_menu.AppendSeparator()
1577        self._edit_menu.Append(GUIFRAME_ID.COPY_ID, '&Copy Params',
1578                               'Copy parameter values')
1579        wx.EVT_MENU(self, GUIFRAME_ID.COPY_ID, self.on_copy_panel)
1580        self._edit_menu.Append(GUIFRAME_ID.PASTE_ID, '&Paste Params',
1581                               'Paste parameter values')
1582        wx.EVT_MENU(self, GUIFRAME_ID.PASTE_ID, self.on_paste_panel)
1583
1584        self._edit_menu.AppendSeparator()
1585
1586        self._edit_menu_copyas = wx.Menu()
1587        #Sub menu for Copy As...
1588        self._edit_menu_copyas.Append(GUIFRAME_ID.COPYEX_ID, 'Copy current tab to Excel',
1589                                      'Copy parameter values in tabular format')
1590        wx.EVT_MENU(self, GUIFRAME_ID.COPYEX_ID, self.on_copy_panel)
1591
1592        self._edit_menu_copyas.Append(GUIFRAME_ID.COPYLAT_ID, 'Copy current tab to LaTeX',
1593                                      'Copy parameter values in tabular format')
1594        wx.EVT_MENU(self, GUIFRAME_ID.COPYLAT_ID, self.on_copy_panel)
1595
1596
1597        self._edit_menu.AppendMenu(GUIFRAME_ID.COPYAS_ID, 'Copy Params as...',
1598                                   self._edit_menu_copyas,
1599                                   'Copy parameter values in various formats')
1600
1601        self._edit_menu.AppendSeparator()
1602
1603        self._edit_menu.Append(GUIFRAME_ID.PREVIEW_ID, '&Report Results',
1604                               'Preview current panel')
1605        wx.EVT_MENU(self, GUIFRAME_ID.PREVIEW_ID, self.on_preview_panel)
1606
1607        self._edit_menu.Append(GUIFRAME_ID.RESET_ID, '&Reset Page',
1608                               'Reset current panel')
1609        wx.EVT_MENU(self, GUIFRAME_ID.RESET_ID, self.on_reset_panel)
1610
1611        self._menubar.Append(self._edit_menu, '&Edit')
1612        self.enable_edit_menu()
1613
1614    def get_style(self):
1615        """
1616        Return the gui style
1617        """
1618        return  self.__gui_style
1619
1620    def _add_menu_data(self):
1621        """
1622        Add menu item item data to menu bar
1623        """
1624        if self._data_plugin is not None:
1625            menu_list = self._data_plugin.populate_menu(self)
1626            if menu_list:
1627                for (menu, name) in menu_list:
1628                    self._menubar.Append(menu, name)
1629
1630    def _on_toggle_toolbar(self, event=None):
1631        """
1632        hide or show toolbar
1633        """
1634        if self._toolbar is None:
1635            return
1636        if self._toolbar.IsShown():
1637            if self._toolbar_menu is not None:
1638                self._toolbar_menu.SetItemLabel('Show Toolbar')
1639            self._toolbar.Hide()
1640        else:
1641            if self._toolbar_menu is not None:
1642                self._toolbar_menu.SetItemLabel('Hide Toolbar')
1643            self._toolbar.Show()
1644        self._toolbar.Realize()
1645
1646    def _on_status_event(self, evt):
1647        """
1648        Display status message
1649        """
1650        # This CallAfter fixes many crashes on MAC.
1651        wx.CallAfter(self.sb.set_status, evt)
1652
1653    def on_view(self, evt):
1654        """
1655        A panel was selected to be shown. If it's not already
1656        shown, display it.
1657
1658        :param evt: menu event
1659
1660        """
1661        panel_id = str(evt.GetId())
1662        self.on_set_plot_focus(self.panels[panel_id])
1663        wx.CallLater(5 * TIME_FACTOR, self.set_schedule(True))
1664        self.set_plot_unfocus()
1665
1666    def show_welcome_panel(self, event):
1667        """
1668        Display the welcome panel
1669        """
1670        if self.defaultPanel is None:
1671            return
1672        frame = self.panels['default'].get_frame()
1673        if frame == None:
1674            return
1675        # Show default panel
1676        if not frame.IsShown():
1677            frame.Show(True)
1678
1679    def on_close_welcome_panel(self):
1680        """
1681        Close the welcome panel
1682        """
1683        if self.defaultPanel is None:
1684            return
1685        default_panel = self.panels["default"].frame
1686        if default_panel.IsShown():
1687            default_panel.Show(False)
1688
1689    def delete_panel(self, uid):
1690        """
1691        delete panel given uid
1692        """
1693        ID = str(uid)
1694        config.printEVT("delete_panel: %s" % ID)
1695        if ID in self.panels.keys():
1696            self.panel_on_focus = None
1697            panel = self.panels[ID]
1698
1699            if hasattr(panel, "connect"):
1700                panel.connect.disconnect()
1701            self._plotting_plugin.delete_panel(panel.group_id)
1702
1703            if panel in self.schedule_full_draw_list:
1704                self.schedule_full_draw_list.remove(panel)
1705
1706            #delete uid number not str(uid)
1707            if ID in self.plot_panels.keys():
1708                del self.plot_panels[ID]
1709            if ID in self.panels.keys():
1710                del self.panels[ID]
1711        else:
1712            logging.error("delete_panel: No such plot id as %s" % ID)
1713
1714    def create_gui_data(self, data, path=None):
1715        """
1716        """
1717        return self._data_manager.create_gui_data(data, path)
1718
1719    def get_data(self, path):
1720        """
1721        """
1722        log_msg = ''
1723        basename = os.path.basename(path)
1724        _, extension = os.path.splitext(basename)
1725        if extension.lower() not in EXTENSIONS:
1726            log_msg = "File Loader cannot "
1727            log_msg += "load: %s\n" % str(basename)
1728            log_msg += "Try Data opening...."
1729            logging.error(log_msg)
1730            return
1731
1732        #reading a state file
1733        for plug in self.plugins:
1734            reader, ext = plug.get_extensions()
1735            if reader is not None:
1736                #read the state of the single plugin
1737                if extension == ext:
1738                    reader.read(path)
1739                    return
1740                elif extension == APPLICATION_STATE_EXTENSION:
1741                    try:
1742                        reader.read(path)
1743                    except:
1744                        msg = "DataLoader Error: Encounted Non-ASCII character"
1745                        msg += "\n(%s)" % sys.exc_value
1746                        wx.PostEvent(self, StatusEvent(status=msg,
1747                                                       info="error", type="stop"))
1748                        return
1749
1750        style = self.__gui_style & GUIFRAME.MANAGER_ON
1751        if style == GUIFRAME.MANAGER_ON:
1752            if self._data_panel is not None:
1753                self._data_panel.frame.Show(True)
1754
1755    def load_from_cmd(self, path):
1756        """
1757        load data from cmd or application
1758        """
1759        if path is None:
1760            return
1761        else:
1762            path = os.path.abspath(path)
1763            if not os.path.isfile(path) and not os.path.isdir(path):
1764                return
1765
1766            if os.path.isdir(path):
1767                self.load_folder(path)
1768                return
1769
1770        basename = os.path.basename(path)
1771        _, extension = os.path.splitext(basename)
1772        if extension.lower() not in EXTENSIONS:
1773            self.load_data(path)
1774        else:
1775            self.load_state(path)
1776
1777        self._default_save_location = os.path.dirname(path)
1778
1779    def load_state(self, path, is_project=False):
1780        """
1781        load data from command line or application
1782        """
1783        if path and (path is not None) and os.path.isfile(path):
1784            basename = os.path.basename(path)
1785            if APPLICATION_STATE_EXTENSION is not None \
1786                and basename.endswith(APPLICATION_STATE_EXTENSION):
1787                if is_project:
1788                    for ID in self.plot_panels.keys():
1789                        panel = self.plot_panels[ID]
1790                        panel.on_close(None)
1791            self.get_data(path)
1792            wx.PostEvent(self, StatusEvent(status="Completed loading."))
1793        else:
1794            wx.PostEvent(self, StatusEvent(status=" "))
1795
1796    def load_data(self, path):
1797        """
1798        load data from command line
1799        """
1800        if not os.path.isfile(path):
1801            return
1802        basename = os.path.basename(path)
1803        _, extension = os.path.splitext(basename)
1804        if extension.lower() in EXTENSIONS:
1805            log_msg = "Data Loader cannot "
1806            log_msg += "load: %s\n" % str(path)
1807            log_msg += "Try File opening ...."
1808            logging.error(log_msg)
1809            return
1810        log_msg = ''
1811        output = {}
1812        error_message = ""
1813        try:
1814            logging.info("Loading Data...:\n" + str(path) + "\n")
1815            temp = self.loader.load(path)
1816            if temp.__class__.__name__ == "list":
1817                for item in temp:
1818                    data = self.create_gui_data(item, path)
1819                    output[data.id] = data
1820            else:
1821                data = self.create_gui_data(temp, path)
1822                output[data.id] = data
1823
1824            self.add_data(data_list=output)
1825        except:
1826            error_message = "Error while loading"
1827            error_message += " Data from cmd:\n %s\n" % str(path)
1828            error_message += str(sys.exc_value) + "\n"
1829            logging.error(error_message)
1830
1831    def load_folder(self, path):
1832        """
1833        Load entire folder
1834        """
1835        if not os.path.isdir(path):
1836            return
1837        if self._data_plugin is None:
1838            return
1839        try:
1840            if path is not None:
1841                self._default_save_location = os.path.dirname(path)
1842                file_list = self._data_plugin.get_file_path(path)
1843                self._data_plugin.get_data(file_list)
1844            else:
1845                return
1846        except:
1847            error_message = "Error while loading"
1848            error_message += " Data folder from cmd:\n %s\n" % str(path)
1849            error_message += str(sys.exc_value) + "\n"
1850            logging.error(error_message)
1851
1852    def _on_open_state_application(self, event):
1853        """
1854        """
1855        path = None
1856        if self._default_save_location == None:
1857            self._default_save_location = os.getcwd()
1858        wx.PostEvent(self, StatusEvent(status="Loading Analysis file..."))
1859        plug_wlist = self._on_open_state_app_helper()
1860        dlg = wx.FileDialog(self,
1861                            "Choose a file",
1862                            self._default_save_location, "",
1863                            plug_wlist)
1864        if dlg.ShowModal() == wx.ID_OK:
1865            path = dlg.GetPath()
1866            if path is not None:
1867                self._default_save_location = os.path.dirname(path)
1868        dlg.Destroy()
1869        self.load_state(path=path)
1870
1871    def _on_open_state_app_helper(self):
1872        """
1873        Helps '_on_open_state_application()' to find the extension of
1874        the current perspective/application
1875        """
1876        # No current perspective or no extension attr
1877        if self._current_perspective is None:
1878            return PLUGINS_WLIST
1879        try:
1880            # Find the extension of the perspective
1881            # and get that as 1st item in list
1882            ind = None
1883            app_ext = self._current_perspective._extensions
1884            plug_wlist = config.PLUGINS_WLIST
1885            for ext in set(plug_wlist):
1886                if ext.count(app_ext) > 0:
1887                    ind = ext
1888                    break
1889            # Found the extension
1890            if ind != None:
1891                plug_wlist.remove(ind)
1892                plug_wlist.insert(0, ind)
1893                try:
1894                    plug_wlist = '|'.join(plug_wlist)
1895                except:
1896                    plug_wlist = ''
1897
1898        except:
1899            plug_wlist = PLUGINS_WLIST
1900
1901        return plug_wlist
1902
1903    def _on_open_state_project(self, event):
1904        """
1905        """
1906        path = None
1907        if self._default_save_location == None:
1908            self._default_save_location = os.getcwd()
1909        wx.PostEvent(self, StatusEvent(status="Loading Project file..."))
1910        dlg = wx.FileDialog(self,
1911                            "Choose a file",
1912                            self._default_save_location, "",
1913                            APPLICATION_WLIST)
1914        if dlg.ShowModal() == wx.ID_OK:
1915            path = dlg.GetPath()
1916            if path is not None:
1917                self._default_save_location = os.path.dirname(path)
1918        dlg.Destroy()
1919
1920        self.load_state(path=path, is_project=True)
1921
1922    def _on_save_application(self, event):
1923        """
1924        save the state of the current active application
1925        """
1926        if self.cpanel_on_focus is not None:
1927            try:
1928                wx.PostEvent(self,
1929                             StatusEvent(status="Saving Analysis file..."))
1930                self.cpanel_on_focus.on_save(event)
1931                wx.PostEvent(self,
1932                             StatusEvent(status="Completed saving."))
1933            except:
1934                msg = "Error occurred while saving: "
1935                msg += "To save, the application panel should have a data set.."
1936                wx.PostEvent(self, StatusEvent(status=msg))
1937
1938    def _on_save_project(self, event):
1939        """
1940        save the state of the SasView as *.svs
1941        """
1942        if self._current_perspective is  None:
1943            return
1944        wx.PostEvent(self, StatusEvent(status="Saving Project file..."))
1945        path = None
1946        extension = '*' + APPLICATION_STATE_EXTENSION
1947        dlg = wx.FileDialog(self, "Save Project file",
1948                            self._default_save_location, "sasview_proj",
1949                            extension,
1950                            wx.SAVE)
1951        if dlg.ShowModal() == wx.ID_OK:
1952            path = dlg.GetPath()
1953            self._default_save_location = os.path.dirname(path)
1954        else:
1955            return None
1956        dlg.Destroy()
1957        try:
1958            if path is None:
1959                return
1960            # default cansas xml doc
1961            doc = None
1962            for panel in self.panels.values():
1963                temp = panel.save_project(doc)
1964                if temp is not None:
1965                    doc = temp
1966
1967            # Write the XML document
1968            extens = APPLICATION_STATE_EXTENSION
1969            fName = os.path.splitext(path)[0] + extens
1970            if doc != None:
1971                fd = open(fName, 'w')
1972                fd.write(doc.toprettyxml())
1973                fd.close()
1974                wx.PostEvent(self, StatusEvent(status="Completed Saving."))
1975            else:
1976                msg = "Error occurred while saving the project: "
1977                msg += "To save, at least one application panel "
1978                msg += "should have a data set "
1979                msg += "and model selected. "
1980                msg += "No project was saved to %s" % (str(path))
1981                logging.warning(msg)
1982                wx.PostEvent(self, StatusEvent(status=msg, info="error"))
1983        except:
1984            msg = "Error occurred while saving: "
1985            msg += "To save, at least one application panel "
1986            msg += "should have a data set.."
1987            wx.PostEvent(self, StatusEvent(status=msg, info="error"))
1988
1989    def on_save_helper(self, doc, reader, panel, path):
1990        """
1991        Save state into a file
1992        """
1993        if reader is not None:
1994            # case of a panel with multi-pages
1995            if hasattr(panel, "opened_pages"):
1996                for _, page in panel.opened_pages.iteritems():
1997                    data = page.get_data()
1998                    # state must be cloned
1999                    state = page.get_state().clone()
2000                    if data is not None:
2001                        new_doc = reader.write_toXML(data, state)
2002                        if doc != None and hasattr(doc, "firstChild"):
2003                            child = new_doc.firstChild.firstChild
2004                            doc.firstChild.appendChild(child)
2005                        else:
2006                            doc = new_doc
2007            # case of only a panel
2008            else:
2009                data = panel.get_data()
2010                state = panel.get_state()
2011                if data is not None:
2012                    new_doc = reader.write_toXML(data, state)
2013                    if doc != None and hasattr(doc, "firstChild"):
2014                        child = new_doc.firstChild.firstChild
2015                        doc.firstChild.appendChild(child)
2016                    else:
2017                        doc = new_doc
2018        return doc
2019
2020    def quit_guiframe(self):
2021        """
2022        Pop up message to make sure the user wants to quit the application
2023        """
2024        message = "\nDo you really want to exit this application?        \n\n"
2025        dial = wx.MessageDialog(self, message, 'Confirm Exit',
2026                                wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
2027        if dial.ShowModal() == wx.ID_YES:
2028            return True
2029        else:
2030            return False
2031
2032    def WindowClose(self, event=None):
2033        """
2034        Quit the application from x icon
2035        """
2036        flag = self.quit_guiframe()
2037        if flag:
2038            _pylab_helpers.Gcf.figs = {}
2039            self.Close()
2040
2041    def Close(self, event=None):
2042        """
2043        Quit the application
2044        """
2045        logging.info(" --- SasView session was closed --- \n")
2046        wx.Exit()
2047        sys.exit()
2048
2049    def _check_update(self, event=None):
2050        """
2051        Check with the deployment server whether a new version
2052        of the application is available.
2053        A thread is started for the connecting with the server. The thread calls
2054        a call-back method when the current version number has been obtained.
2055        """
2056        version_info = {"version": "0.0.0"}
2057        c = Connection(config.__update_URL__, config.UPDATE_TIMEOUT)
2058        response = c.connect()
2059        if response is not None:
2060            try:
2061                #
2062                content = response.read().strip()
2063                logging.info("Connected to www.sasview.org. Latest version: %s"
2064                             % (content))
2065                version_info = json.loads(content)
2066            except:
2067                logging.info("Failed to connect to www.sasview.org")
2068        self._process_version(version_info, standalone=event == None)   
2069
2070       
2071       
2072#         
2073#         try:
2074#             req = urllib2.Request(config.__update_URL__)
2075#             res = urllib2.urlopen(req)
2076#             content = res.read().strip()
2077#             logging.info("Connected to www.sasview.org. Latest version: %s"
2078#                          % (content))
2079#             version_info = json.loads(content)
2080#         except:
2081#             logging.info("Failed to connect to www.sasview.org")
2082#             version_info = {"version": "0.0.0"}
2083#         self._process_version(version_info, standalone=event == None)
2084
2085    def _process_version(self, version_info, standalone=True):
2086        """
2087        Call-back method for the process of checking for updates.
2088        This methods is called by a VersionThread object once the current
2089        version number has been obtained. If the check is being done in the
2090        background, the user will not be notified unless there's an update.
2091
2092        :param version: version string
2093        :param standalone: True of the update is being checked in
2094           the background, False otherwise.
2095
2096        """
2097        try:
2098            version = version_info["version"]
2099            if version == "0.0.0":
2100                msg = "Could not connect to the application server."
2101                msg += " Please try again later."
2102                self.SetStatusText(msg)
2103            elif cmp(version, config.__version__) > 0:
2104                msg = "Version %s is available! " % str(version)
2105                if not standalone:
2106                    import webbrowser
2107                    if "download_url" in version_info:
2108                        webbrowser.open(version_info["download_url"])
2109                    else:
2110                        webbrowser.open(config.__download_page__)
2111                else:
2112                    msg += "See the help menu to download it."
2113                self.SetStatusText(msg)
2114            else:
2115                if not standalone:
2116                    msg = "You have the latest version"
2117                    msg += " of %s" % str(config.__appname__)
2118                    self.SetStatusText(msg)
2119        except:
2120            msg = "guiframe: could not get latest application"
2121            msg += " version number\n  %s" % sys.exc_value
2122            logging.error(msg)
2123            if not standalone:
2124                msg = "Could not connect to the application server."
2125                msg += " Please try again later."
2126                self.SetStatusText(msg)
2127
2128    def _onAcknowledge(self, evt):
2129        """
2130        Pop up the acknowledge dialog
2131
2132        :param evt: menu event
2133
2134        """
2135        if config._do_acknowledge:
2136            import sas.sasgui.guiframe.acknowledgebox as AcknowledgeBox
2137            dialog = AcknowledgeBox.DialogAcknowledge(None, -1, "")
2138            dialog.ShowModal()
2139
2140    def _onAbout(self, evt):
2141        """
2142        Pop up the about dialog
2143
2144        :param evt: menu event
2145
2146        """
2147        if config._do_aboutbox:
2148            import sas.sasgui.guiframe.aboutbox as AboutBox
2149            dialog = AboutBox.DialogAbout(None, -1, "")
2150            dialog.ShowModal()
2151
2152    def _onTutorial(self, evt):
2153        """
2154        Pop up the tutorial dialog
2155
2156        :param evt: menu event
2157
2158        """
2159        if config._do_tutorial:
2160            path = config.TUTORIAL_PATH
2161            if IS_WIN:
2162                try:
2163                    from sas.sasgui.guiframe.pdfview import PDFFrame
2164                    dialog = PDFFrame(None, -1, "Tutorial", path)
2165                    # put icon
2166                    self.put_icon(dialog)
2167                    dialog.Show(True)
2168                except:
2169                    logging.error("Error in _onTutorial: %s" % sys.exc_value)
2170                    try:
2171                        # Try an alternate method
2172                        logging.error("Could not open the tutorial pdf, trying xhtml2pdf")
2173                        from xhtml2pdf import pisa
2174                        pisa.startViewer(path)
2175                    except:
2176                        logging.error("Could not open the tutorial pdf with xhtml2pdf")
2177                        msg = "This feature requires 'PDF Viewer'\n"
2178                        wx.MessageBox(msg, 'Error')
2179            else:
2180                try:
2181                    command = "open '%s'" % path
2182                    os.system(command)
2183                except:
2184                    try:
2185                        # Try an alternate method
2186                        logging.error("Could not open the tutorial pdf, trying xhtml2pdf")
2187                        from xhtml2pdf import pisa
2188                        pisa.startViewer(path)
2189                    except:
2190                        logging.error("Could not open the tutorial pdf with xhtml2pdf")
2191                        msg = "This feature requires the 'Preview' application\n"
2192                        wx.MessageBox(msg, 'Error')
2193
2194    def _onSphinxDocs(self, evt):
2195        """
2196        Bring up Sphinx Documentation at top level whenever the menu item
2197        'documentation' is clicked. Calls DocumentationWindow with the top
2198        level path of "index.html"
2199
2200        :param evt: menu event
2201        """
2202        # Running SasView "in-place" using run.py means the docs will be in a
2203        # different place than they would otherwise.
2204        from documentation_window import DocumentationWindow
2205        _TreeLocation = "user/user.html"
2206        DocumentationWindow(self, -1, _TreeLocation, "", "SasView Documentation")
2207
2208    def set_manager(self, manager):
2209        """
2210        Sets the application manager for this frame
2211
2212        :param manager: frame manager
2213        """
2214        self.app_manager = manager
2215
2216    def post_init(self):
2217        """
2218        This initialization method is called after the GUI
2219        has been created and all plug-ins loaded. It calls
2220        the post_init() method of each plug-in (if it exists)
2221        so that final initialization can be done.
2222        """
2223        for item in self.plugins:
2224            if hasattr(item, "post_init"):
2225                item.post_init()
2226
2227    def set_perspective(self, panels):
2228        """
2229        Sets the perspective of the GUI.
2230        Opens all the panels in the list, and closes
2231        all the others.
2232
2233        :param panels: list of panels
2234        """
2235        for item in self.panels.keys():
2236            # Check whether this is a sticky panel
2237            if hasattr(self.panels[item], "ALWAYS_ON"):
2238                if self.panels[item].ALWAYS_ON:
2239                    continue
2240            if self.panels[item] == None:
2241                continue
2242            if self.panels[item].window_name in panels:
2243                frame = self.panels[item].get_frame()
2244                if not frame.IsShown():
2245                    frame.Show(True)
2246            else:
2247                # always show the data panel if enable
2248                style = self.__gui_style & GUIFRAME.MANAGER_ON
2249                if (style == GUIFRAME.MANAGER_ON) and self.panels[item] == self._data_panel:
2250                    if 'data_panel' in self.panels.keys():
2251                        frame = self.panels['data_panel'].get_frame()
2252                        if frame == None:
2253                            continue
2254                        flag = frame.IsShown()
2255                        frame.Show(flag)
2256                else:
2257                    frame = self.panels[item].get_frame()
2258                    if frame == None:
2259                        continue
2260
2261                    if frame.IsShown():
2262                        frame.Show(False)
2263
2264    def show_data_panel(self, event=None, action=True):
2265        """
2266        show the data panel
2267        """
2268        if self._data_panel_menu == None:
2269            return
2270        label = self._data_panel_menu.GetText()
2271        pane = self.panels["data_panel"]
2272        frame = pane.get_frame()
2273        if label == 'Show Data Explorer':
2274            if action:
2275                frame.Show(True)
2276            self.__gui_style = self.__gui_style | GUIFRAME.MANAGER_ON
2277            self._data_panel_menu.SetText('Hide Data Explorer')
2278        else:
2279            if action:
2280                frame.Show(False)
2281            self.__gui_style = self.__gui_style & (~GUIFRAME.MANAGER_ON)
2282            self._data_panel_menu.SetText('Show Data Explorer')
2283
2284    def add_data_helper(self, data_list):
2285        """
2286        """
2287        if self._data_manager is not None:
2288            self._data_manager.add_data(data_list)
2289
2290    def add_data(self, data_list):
2291        """
2292        receive a dictionary of data from loader
2293        store them its data manager if possible
2294        send to data the current active perspective if the data panel
2295        is not active.
2296        :param data_list: dictionary of data's ID and value Data
2297        """
2298        #Store data into manager
2299        self.add_data_helper(data_list)
2300        # set data in the data panel
2301        if self._data_panel is not None:
2302            data_state = self._data_manager.get_data_state(data_list.keys())
2303            self._data_panel.load_data_list(data_state)
2304        #if the data panel is shown wait for the user to press a button
2305        #to send data to the current perspective. if the panel is not
2306        #show  automatically send the data to the current perspective
2307        style = self.__gui_style & GUIFRAME.MANAGER_ON
2308        if style == GUIFRAME.MANAGER_ON:
2309            #wait for button press from the data panel to set_data
2310            if self._data_panel is not None:
2311                self._data_panel.frame.Show(True)
2312        else:
2313            #automatically send that to the current perspective
2314            self.set_data(data_id=data_list.keys())
2315
2316    def set_data(self, data_id, theory_id=None):
2317        """
2318        set data to current perspective
2319        """
2320        list_data, _ = self._data_manager.get_by_id(data_id)
2321        if self._current_perspective is not None:
2322            self._current_perspective.set_data(list_data.values())
2323
2324        else:
2325            msg = "Guiframe does not have a current perspective"
2326            logging.info(msg)
2327
2328    def set_theory(self, state_id, theory_id=None):
2329        """
2330        """
2331        _, list_theory = self._data_manager.get_by_id(theory_id)
2332        if self._current_perspective is not None:
2333            try:
2334                self._current_perspective.set_theory(list_theory.values())
2335            except:
2336                msg = "Guiframe set_theory: \n" + str(sys.exc_value)
2337                logging.info(msg)
2338                wx.PostEvent(self, StatusEvent(status=msg, info="error"))
2339        else:
2340            msg = "Guiframe does not have a current perspective"
2341            logging.info(msg)
2342
2343    def plot_data(self, state_id, data_id=None,
2344                  theory_id=None, append=False):
2345        """
2346        send a list of data to plot
2347        """
2348        data_list, _ = self._data_manager.get_by_id(data_id)
2349        _, temp_list_theory = self._data_manager.get_by_id(theory_id)
2350        total_plot_list = data_list.values()
2351        for item in temp_list_theory.values():
2352            theory_data, theory_state = item
2353            total_plot_list.append(theory_data)
2354        GROUP_ID = wx.NewId()
2355        for new_plot in total_plot_list:
2356            if append:
2357                if self.panel_on_focus is None:
2358                    message = "cannot append plot. No plot panel on focus!"
2359                    message += "please click on any available plot to set focus"
2360                    wx.PostEvent(self, StatusEvent(status=message,
2361                                                   info='warning'))
2362                    return
2363                else:
2364                    if self.enable_add_data(new_plot):
2365                        new_plot.group_id = self.panel_on_focus.group_id
2366                    else:
2367                        message = "Only 1D Data can be append to"
2368                        message += " plot panel containing 1D data.\n"
2369                        message += "%s not be appended.\n" % str(new_plot.name)
2370                        message += "try new plot option.\n"
2371                        wx.PostEvent(self, StatusEvent(status=message,
2372                                                       info='warning'))
2373            else:
2374                #if not append then new plot
2375                from sas.sasgui.guiframe.dataFitting import Data2D
2376                if issubclass(Data2D, new_plot.__class__):
2377                    #for 2 D always plot in a separated new plot
2378                    new_plot.group_id = wx.NewId()
2379                else:
2380                    # plot all 1D in a new plot
2381                    new_plot.group_id = GROUP_ID
2382            title = "PLOT " + str(new_plot.title)
2383            wx.PostEvent(self, NewPlotEvent(plot=new_plot,
2384                                            title=title,
2385                                            group_id=new_plot.group_id))
2386
2387    def remove_data(self, data_id, theory_id=None):
2388        """
2389        Delete data state if data_id is provide
2390        delete theory created with data of id data_id if theory_id is provide
2391        if delete all true: delete the all state
2392        else delete theory
2393        """
2394        temp = data_id + theory_id
2395        for plug in self.plugins:
2396            plug.delete_data(temp)
2397        data_list, _ = self._data_manager.get_by_id(data_id)
2398        _, temp_list_theory = self._data_manager.get_by_id(theory_id)
2399        total_plot_list = data_list.values()
2400        for item in temp_list_theory.values():
2401            theory_data, theory_state = item
2402            total_plot_list.append(theory_data)
2403        for new_plot in total_plot_list:
2404            for group_id in new_plot.list_group_id:
2405                wx.PostEvent(self, NewPlotEvent(id=new_plot.id,
2406                                                group_id=group_id,
2407                                                action='remove'))
2408                #remove res plot: Todo: improve
2409                wx.CallAfter(self._remove_res_plot, new_plot.id)
2410        self._data_manager.delete_data(data_id=data_id,
2411                                       theory_id=theory_id)
2412
2413    def _remove_res_plot(self, id):
2414        """
2415        Try to remove corresponding res plot
2416
2417        : param id: id of the data
2418        """
2419        try:
2420            wx.PostEvent(self, NewPlotEvent(id=("res" + str(id)),
2421                                            group_id=("res" + str(id)),
2422                                            action='remove'))
2423        except:
2424            logging.error(sys.exc_value)
2425
2426    def save_data1d(self, data, fname):
2427        """
2428        Save data dialog
2429        """
2430        default_name = fname
2431        wildcard = "Text files (*.txt)|*.txt|"\
2432                    "CanSAS 1D files(*.xml)|*.xml"
2433        path = None
2434        dlg = wx.FileDialog(self, "Choose a file",
2435                            self._default_save_location,
2436                            default_name, wildcard, wx.SAVE)
2437
2438        if dlg.ShowModal() == wx.ID_OK:
2439            path = dlg.GetPath()
2440            # ext_num = 0 for .txt, ext_num = 1 for .xml
2441            # This is MAC Fix
2442            ext_num = dlg.GetFilterIndex()
2443            if ext_num == 0:
2444                ext_format = '.txt'
2445            else:
2446                ext_format = '.xml'
2447            path = os.path.splitext(path)[0] + ext_format
2448            mypath = os.path.basename(path)
2449
2450            #Instantiate a loader
2451            loader = Loader()
2452            ext_format = ".txt"
2453            if os.path.splitext(mypath)[1].lower() == ext_format:
2454                # Make sure the ext included in the file name
2455                # especially on MAC
2456                fName = os.path.splitext(path)[0] + ext_format
2457                self._onsaveTXT(data, fName)
2458            ext_format = ".xml"
2459            if os.path.splitext(mypath)[1].lower() == ext_format:
2460                # Make sure the ext included in the file name
2461                # especially on MAC
2462                fName = os.path.splitext(path)[0] + ext_format
2463                loader.save(fName, data, ext_format)
2464            try:
2465                self._default_save_location = os.path.dirname(path)
2466            except:
2467                pass
2468        dlg.Destroy()
2469
2470
2471    def _onsaveTXT(self, data, path):
2472        """
2473        Save file as txt
2474
2475        .. todo:: Refactor and remove this method. See 'TODO' in _onSave.
2476        """
2477        if not path == None:
2478            out = open(path, 'w')
2479            has_errors = True
2480            if data.dy == None or data.dy == []:
2481                has_errors = False
2482            # Sanity check
2483            if has_errors:
2484                try:
2485                    if len(data.y) != len(data.dy):
2486                        has_errors = False
2487                except:
2488                    has_errors = False
2489            if has_errors:
2490                if data.dx != None and data.dx != []:
2491                    out.write("<X>   <Y>   <dY>   <dX>\n")
2492                else:
2493                    out.write("<X>   <Y>   <dY>\n")
2494            else:
2495                out.write("<X>   <Y>\n")
2496
2497            for i in range(len(data.x)):
2498                if has_errors:
2499                    if data.dx != None and data.dx != []:
2500                        if  data.dx[i] != None:
2501                            out.write("%g  %g  %g  %g\n" % (data.x[i],
2502                                                            data.y[i],
2503                                                            data.dy[i],
2504                                                            data.dx[i]))
2505                        else:
2506                            out.write("%g  %g  %g\n" % (data.x[i],
2507                                                        data.y[i],
2508                                                        data.dy[i]))
2509                    else:
2510                        out.write("%g  %g  %g\n" % (data.x[i],
2511                                                    data.y[i],
2512                                                    data.dy[i]))
2513                else:
2514                    out.write("%g  %g\n" % (data.x[i],
2515                                            data.y[i]))
2516            out.close()
2517
2518    def show_data1d(self, data, name):
2519        """
2520        Show data dialog
2521        """
2522        try:
2523            xmin = min(data.x)
2524            ymin = min(data.y)
2525        except:
2526            msg = "Unable to find min/max of \n data named %s" % \
2527                        data.filename
2528            wx.PostEvent(self, StatusEvent(status=msg,
2529                                           info="error"))
2530            raise ValueError, msg
2531        ## text = str(data)
2532        text = data.__str__()
2533        text += 'Data Min Max:\n'
2534        text += 'X_min = %s:  X_max = %s\n' % (xmin, max(data.x))
2535        text += 'Y_min = %s:  Y_max = %s\n' % (ymin, max(data.y))
2536        if data.dy != None:
2537            text += 'dY_min = %s:  dY_max = %s\n' % (min(data.dy), max(data.dy))
2538        text += '\nData Points:\n'
2539        x_st = "X"
2540        for index in range(len(data.x)):
2541            if data.dy != None and len(data.dy) > index:
2542                dy_val = data.dy[index]
2543            else:
2544                dy_val = 0.0
2545            if data.dx != None and len(data.dx) > index:
2546                dx_val = data.dx[index]
2547            else:
2548                dx_val = 0.0
2549            if data.dxl != None and len(data.dxl) > index:
2550                if index == 0:
2551                    x_st = "Xl"
2552                dx_val = data.dxl[index]
2553            elif data.dxw != None and len(data.dxw) > index:
2554                if index == 0:
2555                    x_st = "Xw"
2556                dx_val = data.dxw[index]
2557
2558            if index == 0:
2559                text += "<index> \t<X> \t<Y> \t<dY> \t<d%s>\n" % x_st
2560            text += "%s \t%s \t%s \t%s \t%s\n" % (index,
2561                                                  data.x[index],
2562                                                  data.y[index],
2563                                                  dy_val,
2564                                                  dx_val)
2565        from pdfview import TextFrame
2566        frame = TextFrame(None, -1, "Data Info: %s" % data.name, text)
2567        # put icon
2568        self.put_icon(frame)
2569        frame.Show(True)
2570
2571    def save_data2d(self, data, fname):
2572        """
2573        Save data2d dialog
2574        """
2575        default_name = fname
2576        wildcard = "IGOR/DAT 2D file in Q_map (*.dat)|*.DAT"
2577        dlg = wx.FileDialog(self, "Choose a file",
2578                            self._default_save_location,
2579                            default_name, wildcard, wx.SAVE)
2580
2581        if dlg.ShowModal() == wx.ID_OK:
2582            path = dlg.GetPath()
2583            # ext_num = 0 for .txt, ext_num = 1 for .xml
2584            # This is MAC Fix
2585            ext_num = dlg.GetFilterIndex()
2586            if ext_num == 0:
2587                ext_format = '.dat'
2588            else:
2589                ext_format = ''
2590            path = os.path.splitext(path)[0] + ext_format
2591            mypath = os.path.basename(path)
2592
2593            #Instantiate a loader
2594            loader = Loader()
2595
2596            ext_format = ".dat"
2597            if os.path.splitext(mypath)[1].lower() == ext_format:
2598                # Make sure the ext included in the file name
2599                # especially on MAC
2600                fileName = os.path.splitext(path)[0] + ext_format
2601                loader.save(fileName, data, ext_format)
2602            try:
2603                self._default_save_location = os.path.dirname(path)
2604            except:
2605                pass
2606        dlg.Destroy()
2607
2608    def show_data2d(self, data, name):
2609        """
2610        Show data dialog
2611        """
2612        wx.PostEvent(self, StatusEvent(status="Gathering Data2D Info.",
2613                                       type='start'))
2614        text = data.__str__()
2615        text += 'Data Min Max:\n'
2616        text += 'I_min = %s\n' % min(data.data)
2617        text += 'I_max = %s\n\n' % max(data.data)
2618        text += 'Data (First 2501) Points:\n'
2619        text += 'Data columns include err(I).\n'
2620        text += 'ASCII data starts here.\n'
2621        text += "<index> \t<Qx> \t<Qy> \t<I> \t<dI> \t<dQparal> \t<dQperp>\n"
2622        di_val = 0.0
2623        dx_val = 0.0
2624        dy_val = 0.0
2625        len_data = len(data.qx_data)
2626        for index in xrange(0, len_data):
2627            x_val = data.qx_data[index]
2628            y_val = data.qy_data[index]
2629            i_val = data.data[index]
2630            if data.err_data != None:
2631                di_val = data.err_data[index]
2632            if data.dqx_data != None:
2633                dx_val = data.dqx_data[index]
2634            if data.dqy_data != None:
2635                dy_val = data.dqy_data[index]
2636
2637            text += "%s \t%s \t%s \t%s \t%s \t%s \t%s\n" % (index,
2638                                                            x_val,
2639                                                            y_val,
2640                                                            i_val,
2641                                                            di_val,
2642                                                            dx_val,
2643                                                            dy_val)
2644            # Takes too long time for typical data2d: Break here
2645            if index >= 2500:
2646                text += ".............\n"
2647                break
2648
2649        from pdfview import TextFrame
2650        frame = TextFrame(None, -1, "Data Info: %s" % data.name, text)
2651        # put icon
2652        self.put_icon(frame)
2653        frame.Show(True)
2654        wx.PostEvent(self, StatusEvent(status="Data2D Info Displayed",
2655                                       type='stop'))
2656
2657    def set_current_perspective(self, perspective):
2658        """
2659        set the current active perspective
2660        """
2661        self._current_perspective = perspective
2662        name = "No current analysis selected"
2663        if self._current_perspective is not None:
2664            self._add_current_plugin_menu()
2665            for panel in self.panels.values():
2666                if hasattr(panel, 'CENTER_PANE') and panel.CENTER_PANE:
2667                    for name in self._current_perspective.get_perspective():
2668                        frame = panel.get_frame()
2669                        if frame != None:
2670                            if name == panel.window_name:
2671                                panel.on_set_focus(event=None)
2672                                frame.Show(True)
2673                            else:
2674                                frame.Show(False)
2675            name = self._current_perspective.sub_menu
2676            if self._data_panel is not None:
2677                self._data_panel.set_active_perspective(name)
2678                self._check_applications_menu()
2679            #Set the SasView title
2680            self._set_title_name(name)
2681
2682    def _set_title_name(self, name):
2683        """
2684        Set the SasView title w/ the current application name
2685
2686        : param name: application name [string]
2687        """
2688        # Set SanView Window title w/ application anme
2689        title = self.title + "  - " + name + " -"
2690        self.SetTitle(title)
2691
2692    def _check_applications_menu(self):
2693        """
2694        check the menu of the current application
2695        """
2696        if self._applications_menu is not None:
2697            for menu in self._applications_menu.GetMenuItems():
2698                if self._current_perspective is not None:
2699                    name = self._current_perspective.sub_menu
2700                    if menu.IsCheckable():
2701                        if menu.GetLabel() == name:
2702                            menu.Check(True)
2703                        else:
2704                            menu.Check(False)
2705
2706    def enable_add_data(self, new_plot):
2707        """
2708        Enable append data on a plot panel
2709        """
2710
2711        if self.panel_on_focus not in self._plotting_plugin.plot_panels.values():
2712            return
2713        is_theory = len(self.panel_on_focus.plots) <= 1 and \
2714            self.panel_on_focus.plots.values()[0].__class__.__name__ == "Theory1D"
2715
2716        is_data2d = hasattr(new_plot, 'data')
2717
2718        is_data1d = self.panel_on_focus.__class__.__name__ == "ModelPanel1D"\
2719            and self.panel_on_focus.group_id is not None
2720        has_meta_data = hasattr(new_plot, 'meta_data')
2721
2722        #disable_add_data if the data is being recovered from  a saved state file.
2723        is_state_data = False
2724        if has_meta_data:
2725            if 'invstate' in new_plot.meta_data:
2726                is_state_data = True
2727            if  'prstate' in new_plot.meta_data:
2728                is_state_data = True
2729            if  'fitstate' in new_plot.meta_data:
2730                is_state_data = True
2731
2732        return is_data1d and not is_data2d and not is_theory and not is_state_data
2733
2734    def check_multimode(self, perspective=None):
2735        """
2736        Check the perspective have batch mode capablitity
2737        """
2738        if perspective == None or self._data_panel == None:
2739            return
2740        flag = perspective.get_batch_capable()
2741        flag_on = perspective.batch_on
2742        if flag:
2743            self._data_panel.rb_single_mode.SetValue(not flag_on)
2744            self._data_panel.rb_batch_mode.SetValue(flag_on)
2745        else:
2746            self._data_panel.rb_single_mode.SetValue(True)
2747            self._data_panel.rb_batch_mode.SetValue(False)
2748        self._data_panel.rb_single_mode.Enable(flag)
2749        self._data_panel.rb_batch_mode.Enable(flag)
2750
2751    def enable_edit_menu(self):
2752        """
2753        enable menu item under edit menu depending on the panel on focus
2754        """
2755        if self.cpanel_on_focus is not None and self._edit_menu is not None:
2756            flag = self.cpanel_on_focus.get_undo_flag()
2757            self._edit_menu.Enable(GUIFRAME_ID.UNDO_ID, flag)
2758            flag = self.cpanel_on_focus.get_redo_flag()
2759            self._edit_menu.Enable(GUIFRAME_ID.REDO_ID, flag)
2760            flag = self.cpanel_on_focus.get_copy_flag()
2761            self._edit_menu.Enable(GUIFRAME_ID.COPY_ID, flag)
2762            flag = self.cpanel_on_focus.get_paste_flag()
2763            self._edit_menu.Enable(GUIFRAME_ID.PASTE_ID, flag)
2764
2765            #Copy menu
2766            flag = self.cpanel_on_focus.get_copy_flag()
2767            self._edit_menu_copyas.Enable(GUIFRAME_ID.COPYEX_ID, flag)
2768            self._edit_menu_copyas.Enable(GUIFRAME_ID.COPYLAT_ID, flag)
2769
2770            flag = self.cpanel_on_focus.get_preview_flag()
2771            self._edit_menu.Enable(GUIFRAME_ID.PREVIEW_ID, flag)
2772            flag = self.cpanel_on_focus.get_reset_flag()
2773            self._edit_menu.Enable(GUIFRAME_ID.RESET_ID, flag)
2774        else:
2775            flag = False
2776            self._edit_menu.Enable(GUIFRAME_ID.UNDO_ID, flag)
2777            self._edit_menu.Enable(GUIFRAME_ID.REDO_ID, flag)
2778            self._edit_menu.Enable(GUIFRAME_ID.COPY_ID, flag)
2779            self._edit_menu.Enable(GUIFRAME_ID.PASTE_ID, flag)
2780            self._edit_menu.Enable(GUIFRAME_ID.PREVIEW_ID, flag)
2781            self._edit_menu.Enable(GUIFRAME_ID.RESET_ID, flag)
2782
2783    def on_undo_panel(self, event=None):
2784        """
2785        undo previous action of the last panel on focus if possible
2786        """
2787        if self.cpanel_on_focus is not None:
2788            self.cpanel_on_focus.on_undo(event)
2789
2790    def on_redo_panel(self, event=None):
2791        """
2792        redo the last cancel action done on the last panel on focus
2793        """
2794        if self.cpanel_on_focus is not None:
2795            self.cpanel_on_focus.on_redo(event)
2796
2797    def on_copy_panel(self, event=None):
2798        """
2799        copy the last panel on focus if possible
2800        """
2801        if self.cpanel_on_focus is not None:
2802            self.cpanel_on_focus.on_copy(event)
2803
2804    def on_paste_panel(self, event=None):
2805        """
2806        paste clipboard to the last panel on focus
2807        """
2808        if self.cpanel_on_focus is not None:
2809            self.cpanel_on_focus.on_paste(event)
2810
2811    def on_bookmark_panel(self, event=None):
2812        """
2813        bookmark panel
2814        """
2815        if self.cpanel_on_focus is not None:
2816            self.cpanel_on_focus.on_bookmark(event)
2817
2818    def append_bookmark(self, event=None):
2819        """
2820        Bookmark available information of the panel on focus
2821        """
2822        self._toolbar.append_bookmark(event)
2823
2824    def on_save_panel(self, event=None):
2825        """
2826        save possible information on the current panel
2827        """
2828        if self.cpanel_on_focus is not None:
2829            self.cpanel_on_focus.on_save(event)
2830
2831    def on_preview_panel(self, event=None):
2832        """
2833        preview information on the panel on focus
2834        """
2835        if self.cpanel_on_focus is not None:
2836            self.cpanel_on_focus.on_preview(event)
2837
2838    def on_print_panel(self, event=None):
2839        """
2840        print available information on the last panel on focus
2841        """
2842        if self.cpanel_on_focus is not None:
2843            self.cpanel_on_focus.on_print(event)
2844
2845    def on_zoom_panel(self, event=None):
2846        """
2847        zoom on the current panel if possible
2848        """
2849        if self.cpanel_on_focus is not None:
2850            self.cpanel_on_focus.on_zoom(event)
2851
2852    def on_zoom_in_panel(self, event=None):
2853        """
2854        zoom in of the panel on focus
2855        """
2856        if self.cpanel_on_focus is not None:
2857            self.cpanel_on_focus.on_zoom_in(event)
2858
2859    def on_zoom_out_panel(self, event=None):
2860        """
2861        zoom out on the panel on focus
2862        """
2863        if self.cpanel_on_focus is not None:
2864            self.cpanel_on_focus.on_zoom_out(event)
2865
2866    def on_drag_panel(self, event=None):
2867        """
2868        drag apply to the panel on focus
2869        """
2870        if self.cpanel_on_focus is not None:
2871            self.cpanel_on_focus.on_drag(event)
2872
2873    def on_reset_panel(self, event=None):
2874        """
2875        reset the current panel
2876        """
2877        if self.cpanel_on_focus is not None:
2878            self.cpanel_on_focus.on_reset(event)
2879
2880    def on_change_caption(self, name, old_caption, new_caption):
2881        """
2882        Change the panel caption
2883
2884        :param name: window_name of the pane
2885        :param old_caption: current caption [string]
2886        :param new_caption: new caption [string]
2887        """
2888        # wx.aui.AuiPaneInfo
2889        pane_info = self.get_paneinfo(old_caption)
2890        # update the data_panel.cb_plotpanel
2891        if 'data_panel' in self.panels.keys():
2892            # remove from data_panel combobox
2893            data_panel = self.panels["data_panel"]
2894            if data_panel.cb_plotpanel is not None:
2895                # Check if any panel has the same caption
2896                has_newstring = data_panel.cb_plotpanel.FindString\
2897                                                            (str(new_caption))
2898                caption = new_caption
2899                if has_newstring != wx.NOT_FOUND:
2900                    captions = self._get_plotpanel_captions()
2901                    # Append nummber
2902                    inc = 1
2903                    # FIXME: fix this terrible loop
2904                    while (1):
2905                        caption = new_caption + '_%s' % str(inc)
2906                        if caption not in captions:
2907                            break
2908                        inc += 1
2909                    # notify to users
2910                    msg = "Found Same Title: Added '_%s'" % str(inc)
2911                    wx.PostEvent(self, StatusEvent(status=msg))
2912                # update data_panel cb
2913                pos = data_panel.cb_plotpanel.FindString(str(old_caption))
2914                if pos != wx.NOT_FOUND:
2915                    data_panel.cb_plotpanel.SetString(pos, caption)
2916                    data_panel.cb_plotpanel.SetStringSelection(caption)
2917        # New Caption
2918        pane_info.SetTitle(caption)
2919        return caption
2920
2921    def get_paneinfo(self, name):
2922        """
2923        Get pane Caption from window_name
2924
2925        :param name: window_name in AuiPaneInfo
2926        :returns: AuiPaneInfo of the name
2927        """
2928        for panel in self.plot_panels.values():
2929            if panel.frame.GetTitle() == name:
2930                return panel.frame
2931        return None
2932
2933    def enable_undo(self):
2934        """
2935        enable undo related control
2936        """
2937        if self.cpanel_on_focus is not None:
2938            self._toolbar.enable_undo(self.cpanel_on_focus)
2939
2940    def enable_redo(self):
2941        """
2942        enable redo
2943        """
2944        if self.cpanel_on_focus is not None:
2945            self._toolbar.enable_redo(self.cpanel_on_focus)
2946
2947    def enable_copy(self):
2948        """
2949        enable copy related control
2950        """
2951        if self.cpanel_on_focus is not None:
2952            self._toolbar.enable_copy(self.cpanel_on_focus)
2953
2954    def enable_paste(self):
2955        """
2956        enable paste
2957        """
2958        if self.cpanel_on_focus is not None:
2959            self._toolbar.enable_paste(self.cpanel_on_focus)
2960
2961    def enable_bookmark(self):
2962        """
2963        Bookmark
2964        """
2965        if self.cpanel_on_focus is not None:
2966            self._toolbar.enable_bookmark(self.cpanel_on_focus)
2967
2968    def enable_save(self):
2969        """
2970        save
2971        """
2972        if self.cpanel_on_focus is not None:
2973            self._toolbar.enable_save(self.cpanel_on_focus)
2974
2975    def enable_preview(self):
2976        """
2977        preview
2978        """
2979        if self.cpanel_on_focus is not None:
2980            self._toolbar.enable_preview(self.cpanel_on_focus)
2981
2982    def enable_print(self):
2983        """
2984        print
2985        """
2986        if self.cpanel_on_focus is not None:
2987            self._toolbar.enable_print(self.cpanel_on_focus)
2988
2989    def enable_zoom(self):
2990        """
2991        zoom
2992        """
2993        if self.cpanel_on_focus is not None:
2994            self._toolbar.enable_zoom(self.panel_on_focus)
2995
2996    def enable_zoom_in(self):
2997        """
2998        zoom in
2999        """
3000        if self.cpanel_on_focus is not None:
3001            self._toolbar.enable_zoom_in(self.panel_on_focus)
3002
3003    def enable_zoom_out(self):
3004        """
3005        zoom out
3006        """
3007        if self.cpanel_on_focus is not None:
3008            self._toolbar.enable_zoom_out(self.panel_on_focus)
3009
3010    def enable_drag(self, event=None):
3011        """
3012        drag
3013        """
3014        #Not implemeted
3015
3016    def enable_reset(self):
3017        """
3018        reset the current panel
3019        """
3020        if self.cpanel_on_focus is not None:
3021            self._toolbar.enable_reset(self.panel_on_focus)
3022
3023    def get_toolbar_height(self):
3024        """
3025        """
3026        size_y = 0
3027        if self.GetToolBar() != None and self.GetToolBar().IsShown():
3028            if not IS_LINUX:
3029                _, size_y = self.GetToolBar().GetSizeTuple()
3030        return size_y
3031
3032    def set_schedule_full_draw(self, panel=None, func='del'):
3033        """
3034        Add/subtract the schedule full draw list with the panel given
3035
3036        :param panel: plot panel
3037        :param func: append or del [string]
3038        """
3039
3040        # append this panel in the schedule list if not in yet
3041        if func == 'append':
3042            if not panel in self.schedule_full_draw_list:
3043                self.schedule_full_draw_list.append(panel)
3044        # remove this panel from schedule list
3045        elif func == 'del':
3046            if len(self.schedule_full_draw_list) > 0:
3047                if panel in self.schedule_full_draw_list:
3048                    self.schedule_full_draw_list.remove(panel)
3049
3050        # reset the schdule
3051        if len(self.schedule_full_draw_list) == 0:
3052            self.schedule = False
3053        else:
3054            self.schedule = True
3055
3056    def full_draw(self):
3057        """
3058        Draw the panels with axes in the schedule to full dwar list
3059        """
3060
3061        count = len(self.schedule_full_draw_list)
3062        #if not self.schedule:
3063        if count < 1:
3064            self.set_schedule(False)
3065            return
3066
3067        else:
3068            ind = 0
3069            # if any of the panel is shown do full_draw
3070            for panel in self.schedule_full_draw_list:
3071                ind += 1
3072                if panel.frame.IsShown():
3073                    break
3074                # otherwise, return
3075                if ind == count:
3076                    return
3077        #Simple redraw only for a panel shown
3078        def f_draw(panel):
3079            """
3080            Draw A panel in the full draw list
3081            """
3082            try:
3083                # This checking of GetCapture is to stop redrawing
3084                # while any panel is capture.
3085                frame = panel.frame
3086
3087                if not frame.GetCapture():
3088                    # draw if possible
3089                    panel.set_resizing(False)
3090                    #panel.Show(True)
3091                    panel.draw_plot()
3092                # Check if the panel is not shown
3093                flag = frame.IsShown()
3094                frame.Show(flag)
3095            except:
3096                pass
3097
3098        # Draw all panels
3099        if count == 1:
3100            f_draw(self.schedule_full_draw_list[0])
3101        else:
3102            map(f_draw, self.schedule_full_draw_list)
3103        # Reset the attr
3104        if len(self.schedule_full_draw_list) == 0:
3105            self.set_schedule(False)
3106        else:
3107            self.set_schedule(True)
3108
3109    def set_schedule(self, schedule=False):
3110        """
3111        Set schedule
3112        """
3113        self.schedule = schedule
3114
3115    def get_schedule(self):
3116        """
3117        Get schedule
3118        """
3119        return self.schedule
3120
3121    def on_set_plot_focus(self, panel):
3122        """
3123        Set focus on a plot panel
3124        """
3125        if panel == None:
3126            return
3127        #self.set_plot_unfocus()
3128        panel.on_set_focus(None)
3129        # set focusing panel
3130        self.panel_on_focus = panel
3131        self.set_panel_on_focus(None)
3132
3133    def set_plot_unfocus(self):
3134        """
3135        Un focus all plot panels
3136        """
3137        for plot in self.plot_panels.values():
3138            plot.on_kill_focus(None)
3139
3140    def get_window_size(self):
3141        """
3142        Get window size
3143
3144        :returns: size
3145        :rtype: tuple
3146        """
3147        width, height = self.GetSizeTuple()
3148        if not IS_WIN:
3149            # Subtract toolbar height to get real window side
3150            if self._toolbar.IsShown():
3151                height -= 45
3152        return (width, height)
3153
3154    def _onDrawIdle(self, *args, **kwargs):
3155        """
3156        ReDraw with axes
3157        """
3158        try:
3159            # check if it is time to redraw
3160            if self.GetCapture() == None:
3161                # Draw plot, changes resizing too
3162                self.full_draw()
3163        except:
3164            pass
3165
3166        # restart idle
3167        self._redraw_idle(*args, **kwargs)
3168
3169
3170    def _redraw_idle(self, *args, **kwargs):
3171        """
3172        Restart Idle
3173        """
3174        # restart idle
3175        self.idletimer.Restart(100 * TIME_FACTOR, *args, **kwargs)
3176
3177
3178class DefaultPanel(wx.Panel, PanelBase):
3179    """
3180    Defines the API for a panels to work with
3181    the GUI manager
3182    """
3183    ## Internal nickname for the window, used by the AUI manager
3184    window_name = "default"
3185    ## Name to appear on the window title bar
3186    window_caption = "Welcome panel"
3187    ## Flag to tell the AUI manager to put this panel in the center pane
3188    CENTER_PANE = True
3189    def __init__(self, parent, *args, **kwds):
3190        wx.Panel.__init__(self, parent, *args, **kwds)
3191        PanelBase.__init__(self, parent)
3192
3193
3194
3195class SasViewApp(wx.App):
3196    """
3197    SasView application
3198    """
3199    def OnInit(self):
3200        """
3201        When initialised
3202        """
3203        pos, size, self.is_max = self.window_placement((GUIFRAME_WIDTH,
3204                                                        GUIFRAME_HEIGHT))
3205        self.frame = ViewerFrame(parent=None,
3206                                 title=APPLICATION_NAME,
3207                                 pos=pos,
3208                                 gui_style=DEFAULT_STYLE,
3209                                 size=size)
3210        self.frame.Hide()
3211        if not IS_WIN:
3212            self.frame.EnableCloseButton(False)
3213        self.s_screen = None
3214
3215        try:
3216            self.open_file()
3217        except:
3218            msg = "%s Could not load " % str(APPLICATION_NAME)
3219            msg += "input file from command line.\n"
3220            logging.error(msg)
3221        # Display a splash screen on top of the frame.
3222        try:
3223            if os.path.isfile(SPLASH_SCREEN_PATH):
3224                self.s_screen = self.display_splash_screen(parent=self.frame,
3225                                                           path=SPLASH_SCREEN_PATH)
3226            else:
3227                self.frame.Show()
3228        except:
3229            if self.s_screen is not None:
3230                self.s_screen.Close()
3231            msg = "Cannot display splash screen\n"
3232            msg += str(sys.exc_value)
3233            logging.error(msg)
3234            self.frame.Show()
3235
3236        self.SetTopWindow(self.frame)
3237
3238        return True
3239
3240    def maximize_win(self):
3241        """
3242        Maximize the window after the frame shown
3243        """
3244        if self.is_max:
3245            if self.frame.IsShown():
3246                # Max window size
3247                self.frame.Maximize(self.is_max)
3248
3249    def open_file(self):
3250        """
3251        open a state file at the start of the application
3252        """
3253        input_file = None
3254        if len(sys.argv) >= 2:
3255            cmd = sys.argv[0].lower()
3256            basename = os.path.basename(cmd)
3257            app_base = str(APPLICATION_NAME).lower()
3258            if os.path.isfile(cmd) or basename.lower() == app_base:
3259                app_py = app_base + '.py'
3260                app_exe = app_base + '.exe'
3261                app_app = app_base + '.app'
3262                if basename.lower() in [app_py, app_exe, app_app, app_base]:
3263                    data_base = sys.argv[1]
3264                    input_file = os.path.normpath(os.path.join(DATAPATH,
3265                                                               data_base))
3266        if input_file is None:
3267            return
3268        if self.frame is not None:
3269            self.frame.set_input_file(input_file=input_file)
3270
3271    def clean_plugin_models(self, path):
3272        """
3273        Delete plugin models  in app folder
3274
3275        :param path: path of the plugin_models folder in app
3276        """
3277        # do it only the first time app loaded
3278        # delete unused model folder
3279        model_folder = os.path.join(PATH_APP, path)
3280        if os.path.exists(model_folder) and os.path.isdir(model_folder):
3281            if len(os.listdir(model_folder)) > 0:
3282                try:
3283                    for filename in os.listdir(model_folder):
3284                        file_path = os.path.join(model_folder, filename)
3285                        if os.path.isfile(file_path):
3286                            os.remove(file_path)
3287                except:
3288                    logging.error("gui_manager.clean_plugin_models:\n  %s" \
3289                                  % sys.exc_value)
3290
3291    def set_manager(self, manager):
3292        """
3293        Sets a reference to the application manager
3294        of the GUI manager (Frame)
3295        """
3296        self.frame.set_manager(manager)
3297
3298    def build_gui(self):
3299        """
3300        Build the GUI
3301        """
3302        #try to load file at the start
3303        self.open_file()
3304        self.frame.build_gui()
3305
3306    def set_welcome_panel(self, panel_class):
3307        """
3308        Set the welcome panel
3309
3310        :param panel_class: class of the welcome panel to be instantiated
3311
3312        """
3313        self.frame.welcome_panel_class = panel_class
3314
3315    def add_perspective(self, perspective):
3316        """
3317        Manually add a perspective to the application GUI
3318        """
3319        self.frame.add_perspective(perspective)
3320
3321    def window_placement(self, size):
3322        """
3323        Determines the position and size of the application frame such that it
3324        fits on the user's screen without obstructing (or being obstructed by)
3325        the Windows task bar.  The maximum initial size in pixels is bounded by
3326        WIDTH x HEIGHT.  For most monitors, the application
3327        will be centered on the screen; for very large monitors it will be
3328        placed on the left side of the screen.
3329        """
3330        is_maximized = False
3331        # Get size of screen without
3332        for screenCount in range(wx.Display().GetCount()):
3333            screen = wx.Display(screenCount)
3334            if screen.IsPrimary():
3335                displayRect = screen.GetClientArea()
3336                break
3337
3338        posX, posY, displayWidth, displayHeight = displayRect
3339        customWidth, customHeight = size
3340
3341        # If the custom size is default, set 90% of the screen size
3342        if customWidth <= 0 and customHeight <= 0:
3343            if customWidth == 0 and customHeight == 0:
3344                is_maximized = True
3345            customWidth = displayWidth * 0.9
3346            customHeight = displayHeight * 0.9
3347        else:
3348            # If the custom screen is bigger than the
3349            # window screen than make maximum size
3350            if customWidth > displayWidth:
3351                customWidth = displayWidth
3352            if customHeight > displayHeight:
3353                customHeight = displayHeight
3354
3355        # Note that when running Linux and using an Xming (X11) server on a PC
3356        # with a dual  monitor configuration, the reported display size may be
3357        # that of both monitors combined with an incorrect display count of 1.
3358        # To avoid displaying this app across both monitors, we check for
3359        # screen 'too big'.  If so, we assume a smaller width which means the
3360        # application will be placed towards the left hand side of the screen.
3361
3362        # If dual screen registered as 1 screen. Make width half.
3363        # MAC just follows the default behavior of pos
3364        if IS_WIN:
3365            if displayWidth > (displayHeight * 2):
3366                if customWidth == displayWidth:
3367                    customWidth = displayWidth / 2
3368                # and set the position to be the corner of the screen.
3369                posX = 0
3370                posY = 0
3371
3372            # Make the position the middle of the screen. (Not 0,0)
3373            else:
3374                posX = (displayWidth - customWidth) / 2
3375                posY = (displayHeight - customHeight) / 2
3376        # Return the suggested position and size for the application frame.
3377        return (posX, posY), (customWidth, customHeight), is_maximized
3378
3379
3380    def display_splash_screen(self, parent,
3381                              path=SPLASH_SCREEN_PATH):
3382        """Displays the splash screen.  It will exactly cover the main frame."""
3383
3384        # Prepare the picture.  On a 2GHz intel cpu, this takes about a second.
3385        image = wx.Image(path, wx.BITMAP_TYPE_PNG)
3386        image.Rescale(SPLASH_SCREEN_WIDTH,
3387                      SPLASH_SCREEN_HEIGHT, wx.IMAGE_QUALITY_HIGH)
3388        bm = image.ConvertToBitmap()
3389
3390        # Create and show the splash screen.  It will disappear only when the
3391        # program has entered the event loop AND either the timeout has expired
3392        # or the user has left clicked on the screen.  Thus any processing
3393        # performed in this routine (including sleeping) or processing in the
3394        # calling routine (including doing imports) will prevent the splash
3395        # screen from disappearing.
3396        #
3397        # Note that on Linux, the timeout appears to occur immediately in which
3398        # case the splash screen disappears upon entering the event loop.
3399        s_screen = wx.SplashScreen(bitmap=bm,
3400                                   splashStyle=(wx.SPLASH_TIMEOUT |
3401                                                wx.SPLASH_CENTRE_ON_SCREEN),
3402                                   style=(wx.SIMPLE_BORDER |
3403                                          wx.FRAME_NO_TASKBAR |
3404                                          wx.FRAME_FLOAT_ON_PARENT),
3405                                   milliseconds=SS_MAX_DISPLAY_TIME,
3406                                   parent=parent,
3407                                   id=wx.ID_ANY)
3408        from sas.sasgui.guiframe.gui_statusbar import SPageStatusbar
3409        statusBar = SPageStatusbar(s_screen)
3410        s_screen.SetStatusBar(statusBar)
3411        s_screen.Bind(wx.EVT_CLOSE, self.on_close_splash_screen)
3412        s_screen.Show()
3413        return s_screen
3414
3415
3416    def on_close_splash_screen(self, event):
3417        """
3418        When the splash screen is closed.
3419        """
3420        self.frame.Show(True)
3421        event.Skip()
3422        self.maximize_win()
3423
3424
3425class MDIFrame(CHILD_FRAME):
3426    """
3427    Frame for panels
3428    """
3429    def __init__(self, parent, panel, title="Untitled", size=(300, 200)):
3430        """
3431        comment
3432        :param parent: parent panel/container
3433        """
3434        # Initialize the Frame object
3435        CHILD_FRAME.__init__(self, parent=parent, id=wx.ID_ANY, title=title, size=size)
3436        self.parent = parent
3437        self.name = "Untitled"
3438        self.batch_on = self.parent.batch_on
3439        self.panel = panel
3440        if panel != None:
3441            self.set_panel(panel)
3442        self.Show(False)
3443
3444    def show_data_panel(self, action):
3445        """
3446        Turns on the data panel
3447
3448        The the data panel is optional.  Most of its functions can be
3449        performed from the menu bar and from the plots.
3450        """
3451        self.parent.show_data_panel(action)
3452
3453    def set_panel(self, panel):
3454        """
3455        """
3456        self.panel = panel
3457        self.name = panel.window_name
3458        self.SetTitle(panel.window_caption)
3459        self.SetHelpText(panel.help_string)
3460        width, height = self.parent._get_panels_size(panel)
3461        if hasattr(panel, "CENTER_PANE") and panel.CENTER_PANE:
3462            width *= 0.6
3463        self.SetSize((width, height))
3464        self.parent.put_icon(self)
3465        self.Bind(wx.EVT_SET_FOCUS, self.set_panel_focus)
3466        self.Bind(wx.EVT_CLOSE, self.OnClose)
3467        self.Show(False)
3468
3469    def set_panel_focus(self, event):
3470        """
3471        """
3472        if self.parent.panel_on_focus != self.panel:
3473            self.panel.SetFocus()
3474            self.parent.panel_on_focus = self.panel
3475
3476    def OnClose(self, event):
3477        """
3478        On Close event
3479        """
3480        self.panel.on_close(event)
3481
3482if __name__ == "__main__":
3483    app = SasViewApp(0)
3484    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.