source: sasview/src/sas/sasgui/guiframe/gui_manager.py @ 505706a

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 505706a was 505706a, checked in by Paul Kienzle <pkienzle@…>, 7 years ago

Merge branch 'master' into ticket-811

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