source: sasview/src/sas/sasgui/guiframe/gui_manager.py @ 67b0a99

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 67b0a99 was 67b0a99, checked in by krzywon, 7 years ago

#761: Constrained fit pages are now deleted if they no longer have any data in them when other data sets are deleted. #826: Fixed a bug where dQ smearing option was unavailable for 1D data sets.

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