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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since c155a16 was c155a16, checked in by Ricardo Ferraz Leal <ricleal@…>, 7 years ago

Logging is now logger

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