source: sasview/src/sas/guiframe/gui_manager.py @ 960b2de

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 960b2de was 9bbb627, checked in by Peter Parker, 10 years ago

Remove indent from button label.

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