source: sasview/src/sas/guiframe/gui_manager.py @ 3ef5b2f

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 3ef5b2f was 4d5c42f, checked in by Mathieu Doucet <doucetm@…>, 9 years ago

Move tutorial display to xhtml2pdf

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