source: sasview/src/sans/guiframe/gui_manager.py @ cafa75f

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 cafa75f was 76cd1ae, checked in by Jeff Krzywon <jeffery.krzywon@…>, 11 years ago

Fix for the datainfo window not loading and bug fixes for the cansas data reader.

Fixes/changes:
(1) datainfo window is now loading for every data file I can test
(2) transmission spectrum information (but not data) is listed in datainfo window
(3) more than one transmission spectra can be loaded for each Data1D object
(4) fixed a bug in the cansas reader that allowed any file to be loaded as data if and only if another data file was already loaded
(5) fixed the cansas writer to include transmission spectrum data and output data in strict canSAS format
(6) increased the pylint score of cansas_constants.py to above 7
(7) increased the pylint score for all files I have modified

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