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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalcmagnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since c8321cfc was 2f22db9, checked in by krzywon, 8 years ago

Add carraige return to SAS_OPENCL config line.

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