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

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 beba407 was 38beeab, checked in by Ricardo Ferraz Leal <ricleal@…>, 8 years ago

Logger is now a separate file

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