source: sasview/src/sas/sasgui/guiframe/gui_manager.py @ 643dcd93

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.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 643dcd93 was 49165488, checked in by andyfaff, 7 years ago

MAINT: None is a default argument to dict.get

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