source: sasview/src/sans/guiframe/gui_manager.py @ 2498b9c

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 2498b9c was 2498b9c, checked in by Mathieu Doucet <doucetm@…>, 11 years ago

Fix issue with wx3.0

  • Property mode set to 100644
File size: 128.4 KB
Line 
1"""
2    Gui manager: manages the widgets making up an application
3"""
4################################################################################
5#This software was developed by the University of Tennessee as part of the
6#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
7#project funded by the US National Science Foundation.
8#
9#See the license text in license.txtz
10#
11#copyright 2008, University of Tennessee
12################################################################################
13
14
15import wx
16import wx.aui
17import os
18import sys
19import time
20import imp
21import warnings
22import re
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, style=wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX)
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 = 85
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 + 10
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        self._toolbar = GUIToolBar(self)
1242        # The legacy code doesn't work well for wx 3.0
1243        # but the old code produces better results with wx 2.8
1244        if wx.VERSION_STRING >= '3.0.0.0':
1245            sizer = wx.BoxSizer(wx.VERTICAL)
1246            sizer.Add(self._toolbar, 0, wx.EXPAND)
1247            self.SetSizer(sizer)
1248        else:
1249            self.SetToolBar(self._toolbar)
1250        self._update_toolbar_helper()
1251        self._on_toggle_toolbar(event=None)
1252   
1253    def _update_toolbar_helper(self):
1254        """
1255        Helping to update the toolbar
1256        """
1257        application_name = 'No Selected Analysis'
1258        panel_name = 'No Panel on Focus'
1259        c_panel = self.cpanel_on_focus       
1260        if self._toolbar is  None:
1261            return
1262        if c_panel is not None:
1263            self.reset_bookmark_menu(self.cpanel_on_focus)
1264        if self._current_perspective is not None:
1265            application_name = self._current_perspective.sub_menu
1266        c_panel_state = c_panel
1267        if c_panel is not None:
1268            panel_name = c_panel.window_caption
1269            if not c_panel.IsShownOnScreen():
1270                c_panel_state = None
1271        self._toolbar.update_toolbar(c_panel_state)
1272        self._toolbar.update_button(application_name=application_name, 
1273                                        panel_name=panel_name)
1274        self._toolbar.Realize()
1275       
1276    def _add_menu_tool(self):
1277        """
1278        Tools menu
1279        Go through plug-ins and find tools to populate the tools menu
1280        """
1281        style = self.__gui_style & GUIFRAME.CALCULATOR_ON
1282        if style == GUIFRAME.CALCULATOR_ON:
1283            self._tool_menu = None
1284            for item in self.plugins:
1285                if hasattr(item, "get_tools"):
1286                    for tool in item.get_tools():
1287                        # Only create a menu if we have at least one tool
1288                        if self._tool_menu is None:
1289                            self._tool_menu = wx.Menu()
1290                        if tool[0].lower().count('python') > 0:
1291                            self._tool_menu.AppendSeparator()
1292                        id = wx.NewId()
1293                        self._tool_menu.Append(id, tool[0], tool[1])
1294                        wx.EVT_MENU(self, id, tool[2])
1295            if self._tool_menu is not None:
1296                self._menubar.Append(self._tool_menu, '&Tool')
1297               
1298    def _add_current_plugin_menu(self):
1299        """
1300        add current plugin menu
1301        Look for plug-in menus
1302        Add available plug-in sub-menus.
1303        """
1304        if (self._menubar is None) or (self._current_perspective is None):
1305            return
1306        #replace or add a new menu for the current plugin
1307       
1308        pos = self._menubar.FindMenu(str(self._applications_menu_name))
1309        if pos != -1:
1310            menu_list = self._current_perspective.populate_menu(self)
1311            if menu_list:
1312                for (menu, name) in menu_list:
1313                    hidden_menu = self._menubar.Replace(pos, menu, name) 
1314                    self._applications_menu_name = name
1315                #self._applications_menu_pos = pos
1316            else:
1317                hidden_menu = self._menubar.Remove(pos)
1318                self._applications_menu_name = None
1319            #get the position of the menu when it first added
1320            self._applications_menu_pos = pos
1321           
1322        else:
1323            menu_list = self._current_perspective.populate_menu(self)
1324            if menu_list:
1325                for (menu, name) in menu_list:
1326                    if self._applications_menu_pos == -1:
1327                        self._menubar.Append(menu, name)
1328                    else:
1329                        self._menubar.Insert(self._applications_menu_pos, 
1330                                             menu, name)
1331                    self._applications_menu_name = name
1332                 
1333    def _add_help_menu(self):
1334        """
1335        add help menu
1336        """
1337        # Help menu
1338        self._help_menu = wx.Menu()
1339        style = self.__gui_style & GUIFRAME.WELCOME_PANEL_ON
1340        if style == GUIFRAME.WELCOME_PANEL_ON or custom_config != None:
1341            # add the welcome panel menu item
1342            if config.WELCOME_PANEL_ON and self.defaultPanel is not None:
1343                id = wx.NewId()
1344                self._help_menu.Append(id, '&Welcome', '')
1345                self._help_menu.AppendSeparator()
1346                wx.EVT_MENU(self, id, self.show_welcome_panel)
1347        # Look for help item in plug-ins
1348        for item in self.plugins:
1349            if hasattr(item, "help"):
1350                id = wx.NewId()
1351                self._help_menu.Append(id,'&%s Help' % item.sub_menu, '')
1352                wx.EVT_MENU(self, id, item.help)
1353        if config._do_tutorial and (IS_WIN or sys.platform =='darwin'):
1354            self._help_menu.AppendSeparator()
1355            id = wx.NewId()
1356            self._help_menu.Append(id, '&Tutorial', 'Software tutorial')
1357            wx.EVT_MENU(self, id, self._onTutorial)
1358           
1359        if config._do_aboutbox:
1360            self._help_menu.AppendSeparator()
1361            id = wx.NewId()
1362            self._help_menu.Append(id, '&About', 'Software information')
1363            wx.EVT_MENU(self, id, self._onAbout)
1364       
1365        # Checking for updates
1366        id = wx.NewId()
1367        self._help_menu.Append(id,'&Check for update', 
1368         'Check for the latest version of %s' % config.__appname__)
1369        wx.EVT_MENU(self, id, self._check_update)
1370        self._menubar.Append(self._help_menu, '&Help')
1371           
1372    def _add_menu_view(self):
1373        """
1374        add menu items under view menu
1375        """
1376        if not VIEW_MENU:
1377            return
1378        self._view_menu = wx.Menu()
1379       
1380        id = wx.NewId()
1381        hint = "Display the Grid Window for batch results etc."
1382        self._view_menu.Append(id, '&Show Grid Window', hint) 
1383        wx.EVT_MENU(self, id, self.show_batch_frame)
1384       
1385        self._view_menu.AppendSeparator()
1386        style = self.__gui_style & GUIFRAME.MANAGER_ON
1387        id = wx.NewId()
1388        self._data_panel_menu = self._view_menu.Append(id,
1389                                                '&Show Data Explorer', '')
1390        wx.EVT_MENU(self, id, self.show_data_panel)
1391        if style == GUIFRAME.MANAGER_ON:
1392            self._data_panel_menu.SetText('Hide Data Explorer')
1393        else:
1394            self._data_panel_menu.SetText('Show Data Explorer')
1395 
1396        self._view_menu.AppendSeparator()
1397        id = wx.NewId()
1398        style1 = self.__gui_style & GUIFRAME.TOOLBAR_ON
1399        if style1 == GUIFRAME.TOOLBAR_ON:
1400            self._toolbar_menu = self._view_menu.Append(id, '&Hide Toolbar', '')
1401        else:
1402            self._toolbar_menu = self._view_menu.Append(id, '&Show Toolbar', '')
1403        wx.EVT_MENU(self, id, self._on_toggle_toolbar)
1404
1405        if custom_config != None:
1406            self._view_menu.AppendSeparator()
1407            id = wx.NewId()
1408            hint_ss = "Select the current/default configuration "
1409            hint_ss += "as a startup setting"
1410            preference_menu = self._view_menu.Append(id, 'Startup Setting', 
1411                                                     hint_ss)
1412            wx.EVT_MENU(self, id, self._on_preference_menu)
1413           
1414        id = wx.NewId()
1415        self._view_menu.AppendSeparator()
1416        self._view_menu.Append(id, 'Category Manager', 'Edit model categories')
1417        wx.EVT_MENU(self, id, self._on_category_manager)
1418
1419        self._menubar.Append(self._view_menu, '&View')   
1420         
1421    def show_batch_frame(self, event=None):
1422        """
1423        show the grid of result
1424        """
1425        # Show(False) before Show(True) in order to bring it to the front
1426        self.batch_frame.Show(False)
1427        self.batch_frame.Show(True)
1428   
1429    def  on_category_panel(self, event): 
1430        """
1431        On cat panel
1432        """
1433        self._on_category_manager(event)
1434         
1435    def _on_category_manager(self, event):
1436        """
1437        Category manager frame
1438        """
1439        frame = CategoryManager(self, -1, 'Model Category Manager')
1440        icon = self.GetIcon()
1441        frame.SetIcon(icon)
1442
1443    def _on_preference_menu(self, event):     
1444        """
1445        Build a panel to allow to edit Mask
1446        """
1447       
1448        from sans.guiframe.startup_configuration \
1449        import StartupConfiguration as ConfDialog
1450       
1451        self.panel = ConfDialog(parent=self, gui=self.__gui_style)
1452        self.panel.ShowModal()
1453
1454               
1455    def _add_menu_application(self):
1456        """
1457        # Attach a menu item for each defined perspective or application.
1458        # Only add the perspective menu if there are more than one perspectives
1459        add menu application
1460        """
1461        if self._num_perspectives  > 1:
1462            plug_data_count = False
1463            plug_no_data_count = False
1464            self._applications_menu = wx.Menu()
1465            pos = 0
1466            separator = self._applications_menu.AppendSeparator()
1467            for plug in self.plugins:
1468                if len(plug.get_perspective()) > 0:
1469                    id = wx.NewId()
1470                    if plug.use_data():
1471                       
1472                        self._applications_menu.InsertCheckItem(pos, id, plug.sub_menu,
1473                                      "Switch to analysis: %s" % plug.sub_menu)
1474                        plug_data_count = True
1475                        pos += 1
1476                    else:
1477                        plug_no_data_count = True
1478                        self._applications_menu.AppendCheckItem(id, plug.sub_menu,
1479                                      "Switch to analysis: %s" % plug.sub_menu)
1480                    wx.EVT_MENU(self, id, plug.on_perspective)
1481
1482            if (not plug_data_count or not plug_no_data_count):
1483                self._applications_menu.RemoveItem(separator)
1484            self._menubar.Append(self._applications_menu, '&Analysis')
1485            self._check_applications_menu()
1486           
1487    def _populate_file_menu(self):
1488        """
1489        Insert menu item under file menu
1490        """
1491        for plugin in self.plugins:
1492            if len(plugin.populate_file_menu()) > 0:
1493                for item in plugin.populate_file_menu():
1494                    m_name, m_hint, m_handler = item
1495                    id = wx.NewId()
1496                    self._file_menu.Append(id, m_name, m_hint)
1497                    wx.EVT_MENU(self, id, m_handler)
1498                self._file_menu.AppendSeparator()
1499               
1500    def _add_menu_file(self):
1501        """
1502        add menu file
1503        """
1504       
1505        # File menu
1506        self._file_menu = wx.Menu()
1507        #append item from plugin under menu file if necessary
1508        self._populate_file_menu()
1509        style1 = self.__gui_style & GUIFRAME.MULTIPLE_APPLICATIONS
1510        if OPEN_SAVE_MENU:
1511            id = wx.NewId()
1512            hint_load_file = "read all analysis states saved previously"
1513            self._save_appl_menu = self._file_menu.Append(id, 
1514                                    '&Open Project', hint_load_file)
1515            wx.EVT_MENU(self, id, self._on_open_state_project)
1516           
1517        if style1 == GUIFRAME.MULTIPLE_APPLICATIONS:
1518            # some menu of plugin to be seen under file menu
1519            hint_load_file = "Read a status files and load"
1520            hint_load_file += " them into the analysis"
1521            id = wx.NewId()
1522            self._save_appl_menu = self._file_menu.Append(id, 
1523                                    '&Open Analysis', hint_load_file)
1524            wx.EVT_MENU(self, id, self._on_open_state_application)
1525        if OPEN_SAVE_MENU:       
1526            self._file_menu.AppendSeparator()
1527            id = wx.NewId()
1528            self._file_menu.Append(id, '&Save Project',
1529                                 'Save the state of the whole analysis')
1530            wx.EVT_MENU(self, id, self._on_save_project)
1531        if style1 == GUIFRAME.MULTIPLE_APPLICATIONS:
1532            id = wx.NewId()
1533            self._save_appl_menu = self._file_menu.Append(id, 
1534                                                      '&Save Analysis',
1535                        'Save state of the current active analysis panel')
1536            wx.EVT_MENU(self, id, self._on_save_application)
1537            self._file_menu.AppendSeparator()
1538       
1539        id = wx.NewId()
1540        self._file_menu.Append(id, '&Quit', 'Exit') 
1541        wx.EVT_MENU(self, id, self.Close)
1542        # Add sub menus
1543        self._menubar.Append(self._file_menu, '&File')
1544       
1545    def _add_menu_edit(self):
1546        """
1547        add menu edit
1548        """
1549        if not EDIT_MENU:
1550            return
1551        # Edit Menu
1552        self._edit_menu = wx.Menu()
1553        self._edit_menu.Append(GUIFRAME_ID.UNDO_ID, '&Undo', 
1554                               'Undo the previous action')
1555        wx.EVT_MENU(self, GUIFRAME_ID.UNDO_ID, self.on_undo_panel)
1556        self._edit_menu.Append(GUIFRAME_ID.REDO_ID, '&Redo', 
1557                               'Redo the previous action')
1558        wx.EVT_MENU(self, GUIFRAME_ID.REDO_ID, self.on_redo_panel)
1559        self._edit_menu.AppendSeparator()
1560        self._edit_menu.Append(GUIFRAME_ID.COPY_ID, '&Copy Params', 
1561                               'Copy parameter values')
1562        wx.EVT_MENU(self, GUIFRAME_ID.COPY_ID, self.on_copy_panel)
1563        self._edit_menu.Append(GUIFRAME_ID.PASTE_ID, '&Paste Params', 
1564                               'Paste parameter values')
1565        wx.EVT_MENU(self, GUIFRAME_ID.PASTE_ID, self.on_paste_panel)
1566        self._edit_menu.AppendSeparator()
1567       
1568        self._edit_menu.Append(GUIFRAME_ID.PREVIEW_ID, '&Report Results',
1569                               'Preview current panel')
1570        wx.EVT_MENU(self, GUIFRAME_ID.PREVIEW_ID, self.on_preview_panel)
1571
1572        self._edit_menu.Append(GUIFRAME_ID.RESET_ID, '&Reset Page', 
1573                               'Reset current panel')
1574        wx.EVT_MENU(self, GUIFRAME_ID.RESET_ID, self.on_reset_panel)
1575   
1576        self._menubar.Append(self._edit_menu,  '&Edit')
1577        self.enable_edit_menu()
1578       
1579    def get_style(self):
1580        """
1581        Return the gui style
1582        """
1583        return  self.__gui_style
1584   
1585    def _add_menu_data(self):
1586        """
1587        Add menu item item data to menu bar
1588        """
1589        if self._data_plugin is not None:
1590            menu_list = self._data_plugin.populate_menu(self)
1591            if menu_list:
1592                for (menu, name) in menu_list:
1593                    self._menubar.Append(menu, name)
1594       
1595                       
1596    def _on_toggle_toolbar(self, event=None):
1597        """
1598        hide or show toolbar
1599        """
1600        if self._toolbar is None:
1601            return
1602        if self._toolbar.IsShown():
1603            if self._toolbar_menu is not None:
1604                self._toolbar_menu.SetItemLabel('Show Toolbar')
1605            self._toolbar.Hide()
1606        else:
1607            if self._toolbar_menu is not None:
1608                self._toolbar_menu.SetItemLabel('Hide Toolbar')
1609            self._toolbar.Show()
1610        self._toolbar.Realize()
1611       
1612    def _on_status_event(self, evt):
1613        """
1614        Display status message
1615        """
1616        # This CallAfter fixes many crashes on MAC.
1617        wx.CallAfter(self.sb.set_status, evt)
1618       
1619    def on_view(self, evt):
1620        """
1621        A panel was selected to be shown. If it's not already
1622        shown, display it.
1623       
1624        :param evt: menu event
1625       
1626        """
1627        panel_id = str(evt.GetId())
1628        self.on_set_plot_focus(self.panels[panel_id])
1629        self.show_panel(evt.GetId(), 'on')     
1630        wx.CallLater(5*TIME_FACTOR, self.set_schedule(True))
1631        self.set_plot_unfocus()
1632 
1633    def show_welcome_panel(self, event):
1634        """   
1635        Display the welcome panel
1636        """
1637        if self.defaultPanel is None:
1638            return 
1639        frame = self.panels['default'].get_frame()
1640        if frame == None:
1641            return
1642        # Show default panel
1643        if not frame.IsShown():
1644            frame.Show(True)
1645           
1646    def on_close_welcome_panel(self):
1647        """
1648        Close the welcome panel
1649        """
1650        if self.defaultPanel is None:
1651            return 
1652        default_panel = self.panels["default"].frame
1653        if default_panel.IsShown():
1654            default_panel.Show(False) 
1655               
1656    def delete_panel(self, uid):
1657        """
1658        delete panel given uid
1659        """
1660        ID = str(uid)
1661        config.printEVT("delete_panel: %s" % ID)
1662        try:
1663            caption = self.panels[ID].window_caption
1664        except:
1665            print "delete_panel: No such plot id as %s" % ID
1666            return
1667        if ID in self.panels.keys():
1668            self.panel_on_focus = None
1669            panel = self.panels[ID]
1670
1671            if hasattr(panel, "connect"):
1672                panel.connect.disconnect()
1673            self._plotting_plugin.delete_panel(panel.group_id)
1674
1675            if panel in self.schedule_full_draw_list:
1676                self.schedule_full_draw_list.remove(panel) 
1677           
1678            #delete uid number not str(uid)
1679            if ID in self.plot_panels.keys():
1680                del self.plot_panels[ID]
1681            if ID in self.panels.keys():
1682                del self.panels[ID]
1683            return 
1684           
1685    def create_gui_data(self, data, path=None):
1686        """
1687        """
1688        return self._data_manager.create_gui_data(data, path)
1689   
1690    def get_data(self, path):
1691        """
1692        """
1693        message = ""
1694        log_msg = ''
1695        output = []
1696        error_message = ""
1697        basename  = os.path.basename(path)
1698        root, extension = os.path.splitext(basename)
1699        if extension.lower() not in EXTENSIONS:
1700            log_msg = "File Loader cannot "
1701            log_msg += "load: %s\n" % str(basename)
1702            log_msg += "Try Data opening...."
1703            logging.info(log_msg)
1704            print log_msg
1705            #self.load_complete(output=output, error_message=error_message,
1706            #       message=log_msg, path=path)   
1707            return
1708       
1709        #reading a state file
1710        for plug in self.plugins:
1711            reader, ext = plug.get_extensions()
1712            if reader is not None:
1713                #read the state of the single plugin
1714                if extension == ext:
1715                    reader.read(path)
1716                    return
1717                elif extension == APPLICATION_STATE_EXTENSION:
1718                    try:
1719                        reader.read(path)
1720                    except:
1721                        msg = "DataLoader Error: Encounted Non-ASCII character"
1722                        msg += "\n(%s)"% sys.exc_value
1723                        wx.PostEvent(self, StatusEvent(status=msg, 
1724                                                info="error", type="stop"))
1725                        return
1726       
1727        style = self.__gui_style & GUIFRAME.MANAGER_ON
1728        if style == GUIFRAME.MANAGER_ON:
1729            if self._data_panel is not None:
1730                self._data_panel.frame.Show(True)
1731     
1732    def load_from_cmd(self,  path):   
1733        """
1734        load data from cmd or application
1735        """ 
1736        if path is None:
1737            return
1738        else:
1739            path = os.path.abspath(path)
1740            if not os.path.isfile(path) and not os.path.isdir(path):
1741                return
1742           
1743            if os.path.isdir(path):
1744                self.load_folder(path)
1745                return
1746
1747        basename  = os.path.basename(path)
1748        root, extension = os.path.splitext(basename)
1749        if extension.lower() not in EXTENSIONS:
1750            self.load_data(path)
1751        else:
1752            self.load_state(path)
1753
1754        self._default_save_location = os.path.dirname(path)
1755       
1756    def show_panel(self, uid, show=None):
1757        """
1758        Shows the panel with the given id
1759       
1760        :param uid: unique ID number of the panel to show
1761       
1762        """
1763        #Not implemeted
1764        return
1765       
1766    def load_state(self, path, is_project=False):   
1767        """
1768        load data from command line or application
1769        """
1770        if path and (path is not None) and os.path.isfile(path):
1771            basename  = os.path.basename(path)
1772            if APPLICATION_STATE_EXTENSION is not None \
1773                and basename.endswith(APPLICATION_STATE_EXTENSION):
1774                if is_project:
1775                    for ID in self.plot_panels.keys():
1776                        panel = self.plot_panels[ID]
1777                        panel.on_close(None)
1778            self.get_data(path)
1779            wx.PostEvent(self, StatusEvent(status="Completed loading."))
1780        else:
1781            wx.PostEvent(self, StatusEvent(status=" "))
1782           
1783    def load_data(self, path):
1784        """
1785        load data from command line
1786        """
1787        if not os.path.isfile(path):
1788            return
1789        basename  = os.path.basename(path)
1790        root, extension = os.path.splitext(basename)
1791        if extension.lower() in EXTENSIONS:
1792            log_msg = "Data Loader cannot "
1793            log_msg += "load: %s\n" % str(path)
1794            log_msg += "Try File opening ...."
1795            print log_msg
1796            return
1797        log_msg = ''
1798        output = {}
1799        error_message = ""
1800        try:
1801            print "Loading Data...:\n" + str(path) + "\n"
1802            temp =  self.loader.load(path)
1803            if temp.__class__.__name__ == "list":
1804                for item in temp:
1805                    data = self.create_gui_data(item, path)
1806                    output[data.id] = data
1807            else:
1808                data = self.create_gui_data(temp, path)
1809                output[data.id] = data
1810           
1811            self.add_data(data_list=output)
1812        except:
1813            error_message = "Error while loading"
1814            error_message += " Data from cmd:\n %s\n" % str(path)
1815            error_message += str(sys.exc_value) + "\n"
1816            print error_message
1817 
1818    def load_folder(self, path):
1819        """
1820        Load entire folder
1821        """   
1822        if not os.path.isdir(path):
1823            return
1824        if self._data_plugin is None:
1825            return
1826        try:
1827            if path is not None:
1828                self._default_save_location = os.path.dirname(path)
1829                file_list = self._data_plugin.get_file_path(path)
1830                self._data_plugin.get_data(file_list)
1831            else:
1832                return 
1833        except:
1834            error_message = "Error while loading"
1835            error_message += " Data folder from cmd:\n %s\n" % str(path)
1836            error_message += str(sys.exc_value) + "\n"
1837            print error_message
1838           
1839    def _on_open_state_application(self, event):
1840        """
1841        """
1842        path = None
1843        if self._default_save_location == None:
1844            self._default_save_location = os.getcwd()
1845        wx.PostEvent(self, StatusEvent(status="Loading Analysis file..."))
1846        plug_wlist = self._on_open_state_app_helper()
1847        dlg = wx.FileDialog(self, 
1848                            "Choose a file", 
1849                            self._default_save_location, "",
1850                            plug_wlist)
1851        if dlg.ShowModal() == wx.ID_OK:
1852            path = dlg.GetPath()
1853            if path is not None:
1854                self._default_save_location = os.path.dirname(path)
1855        dlg.Destroy()
1856        self.load_state(path=path) 
1857   
1858    def _on_open_state_app_helper(self):
1859        """
1860        Helps '_on_open_state_application()' to find the extension of
1861        the current perspective/application
1862        """
1863        # No current perspective or no extension attr
1864        if self._current_perspective is None:
1865            return PLUGINS_WLIST
1866        try:
1867            # Find the extension of the perspective
1868            # and get that as 1st item in list
1869            ind = None
1870            app_ext = self._current_perspective._extensions
1871            plug_wlist = config.PLUGINS_WLIST
1872            for ext in set(plug_wlist):
1873                if ext.count(app_ext) > 0:
1874                    ind = ext
1875                    break
1876            # Found the extension
1877            if ind != None:
1878                plug_wlist.remove(ind)
1879                plug_wlist.insert(0, ind)
1880                try:
1881                    plug_wlist = '|'.join(plug_wlist)
1882                except:
1883                    plug_wlist = ''
1884
1885        except:
1886            plug_wlist = PLUGINS_WLIST
1887           
1888        return plug_wlist
1889           
1890    def _on_open_state_project(self, event):
1891        """
1892        """
1893        path = None
1894        if self._default_save_location == None:
1895            self._default_save_location = os.getcwd()
1896        wx.PostEvent(self, StatusEvent(status="Loading Project file..."))
1897        dlg = wx.FileDialog(self, 
1898                            "Choose a file", 
1899                            self._default_save_location, "",
1900                             APPLICATION_WLIST)
1901        if dlg.ShowModal() == wx.ID_OK:
1902            path = dlg.GetPath()
1903            if path is not None:
1904                self._default_save_location = os.path.dirname(path)
1905        dlg.Destroy()
1906       
1907        self.load_state(path=path, is_project=True)
1908       
1909    def _on_save_application(self, event):
1910        """
1911        save the state of the current active application
1912        """
1913        if self.cpanel_on_focus is not None:
1914            try:
1915                wx.PostEvent(self, 
1916                             StatusEvent(status="Saving Analysis file..."))
1917                self.cpanel_on_focus.on_save(event)
1918                wx.PostEvent(self, 
1919                             StatusEvent(status="Completed saving."))
1920            except:
1921                msg = "Error occurred while saving: "
1922                msg += "To save, the application panel should have a data set.."
1923                wx.PostEvent(self, StatusEvent(status=msg)) 
1924           
1925    def _on_save_project(self, event):
1926        """
1927        save the state of the SasView as *.svs
1928        """
1929        if self._current_perspective is  None:
1930            return
1931        wx.PostEvent(self, StatusEvent(status="Saving Project file..."))
1932        reader, ext = self._current_perspective.get_extensions()
1933        path = None
1934        extension = '*' + APPLICATION_STATE_EXTENSION
1935        dlg = wx.FileDialog(self, "Save Project file",
1936                            self._default_save_location, "sasview_proj",
1937                             extension, 
1938                             wx.SAVE)
1939        if dlg.ShowModal() == wx.ID_OK:
1940            path = dlg.GetPath()
1941            self._default_save_location = os.path.dirname(path)
1942        else:
1943            return None
1944        dlg.Destroy()
1945        try:
1946            if path is None:
1947                return
1948            # default cansas xml doc
1949            doc = None
1950            for panel in self.panels.values():
1951                temp = panel.save_project(doc)
1952                if temp is not None:
1953                    doc = temp
1954             
1955            # Write the XML document
1956            extens = APPLICATION_STATE_EXTENSION
1957            fName = os.path.splitext(path)[0] + extens
1958            if doc != None:
1959                fd = open(fName, 'w')
1960                fd.write(doc.toprettyxml())
1961                fd.close()
1962                wx.PostEvent(self, StatusEvent(status="Completed Saving."))
1963            else:
1964                msg = "%s cannot read %s\n" % (str(APPLICATION_NAME), str(path))
1965                logging.error(msg)
1966        except:
1967            msg = "Error occurred while saving: "
1968            msg += "To save, at leat one application panel "
1969            msg += "should have a data set.."
1970            wx.PostEvent(self, StatusEvent(status=msg))   
1971                   
1972    def on_save_helper(self, doc, reader, panel, path):
1973        """
1974        Save state into a file
1975        """
1976        try:
1977            if reader is not None:
1978                # case of a panel with multi-pages
1979                if hasattr(panel, "opened_pages"):
1980                    for uid, page in panel.opened_pages.iteritems():
1981                        data = page.get_data()
1982                        # state must be cloned
1983                        state = page.get_state().clone()
1984                        if data is not None:
1985                            new_doc = reader.write_toXML(data, state)
1986                            if doc != None and hasattr(doc, "firstChild"):
1987                                child = new_doc.firstChild.firstChild
1988                                doc.firstChild.appendChild(child) 
1989                            else:
1990                                doc = new_doc
1991                # case of only a panel
1992                else:
1993                    data = panel.get_data()
1994                    state = panel.get_state()
1995                    if data is not None:
1996                        new_doc = reader.write_toXML(data, state)
1997                        if doc != None and hasattr(doc, "firstChild"):
1998                            child = new_doc.firstChild.firstChild
1999                            doc.firstChild.appendChild(child) 
2000                        else:
2001                            doc = new_doc
2002        except: 
2003            raise
2004            #pass
2005
2006        return doc
2007
2008    def quit_guiframe(self):
2009        """
2010        Pop up message to make sure the user wants to quit the application
2011        """
2012        message = "\nDo you really want to exit this application?        \n\n"
2013        dial = wx.MessageDialog(self, message, 'Confirm Exit',
2014                           wx.YES_NO|wx.YES_DEFAULT|wx.ICON_QUESTION)
2015        if dial.ShowModal() == wx.ID_YES:
2016            return True
2017        else:
2018            return False   
2019       
2020    def WindowClose(self, event=None):
2021        """
2022        Quit the application from x icon
2023        """
2024        flag = self.quit_guiframe()
2025        if flag:
2026            _pylab_helpers.Gcf.figs = {}
2027            self.Close()
2028           
2029    def Close(self, event=None):
2030        """
2031        Quit the application
2032        """
2033        wx.Exit()
2034        sys.exit()
2035           
2036    def _check_update(self, event=None): 
2037        """
2038        Check with the deployment server whether a new version
2039        of the application is available.
2040        A thread is started for the connecting with the server. The thread calls
2041        a call-back method when the current version number has been obtained.
2042        """
2043        try:
2044            conn = httplib.HTTPConnection(config.__update_URL__[0], 
2045                              timeout=3.0)
2046            conn.request("GET", config.__update_URL__[1])
2047            res = conn.getresponse()
2048            content = res.read()
2049            conn.close()
2050        except:
2051            content = "0.0.0"
2052       
2053        version = content.strip()
2054        if len(re.findall('\d+\.\d+\.\d+$', version)) < 0:
2055            content = "0.0.0"
2056        self._process_version(content, standalone=event==None)
2057   
2058    def _process_version(self, version, standalone=True):
2059        """
2060        Call-back method for the process of checking for updates.
2061        This methods is called by a VersionThread object once the current
2062        version number has been obtained. If the check is being done in the
2063        background, the user will not be notified unless there's an update.
2064       
2065        :param version: version string
2066        :param standalone: True of the update is being checked in
2067           the background, False otherwise.
2068           
2069        """
2070        try:
2071            if version == "0.0.0":
2072                msg = "Could not connect to the application server."
2073                msg += " Please try again later."
2074                self.SetStatusText(msg)
2075            elif cmp(version, config.__version__) > 0:
2076                msg = "Version %s is available! " % str(version)
2077                if not standalone:
2078                    import webbrowser
2079                    webbrowser.open(config.__download_page__)
2080                else:
2081                    msg +=  "See the help menu to download it." 
2082                self.SetStatusText(msg)
2083            else:
2084                if not standalone:
2085                    msg = "You have the latest version"
2086                    msg += " of %s" % str(config.__appname__)
2087                    self.SetStatusText(msg)
2088        except:
2089            msg = "guiframe: could not get latest application"
2090            msg += " version number\n  %s" % sys.exc_value
2091            logging.error(msg)
2092            if not standalone:
2093                msg = "Could not connect to the application server."
2094                msg += " Please try again later."
2095                self.SetStatusText(msg)
2096                   
2097    def _onAbout(self, evt):
2098        """
2099        Pop up the about dialog
2100       
2101        :param evt: menu event
2102       
2103        """
2104        if config._do_aboutbox:
2105            import sans.guiframe.aboutbox as AboutBox 
2106            dialog = AboutBox.DialogAbout(None, -1, "")
2107            dialog.ShowModal()   
2108                     
2109    def _onTutorial(self, evt):
2110        """
2111        Pop up the tutorial dialog
2112       
2113        :param evt: menu event
2114       
2115        """
2116        if config._do_tutorial:   
2117            path = config.TUTORIAL_PATH
2118            if IS_WIN:
2119                try:
2120                    from sans.guiframe.pdfview import PDFFrame
2121                    dialog = PDFFrame(None, -1, "Tutorial", path)
2122                    # put icon
2123                    self.put_icon(dialog) 
2124                    dialog.Show(True) 
2125                except:
2126                    print "Error in _onTutorial: %s" % sys.exc_value
2127                    try:
2128                        #in case when the pdf default set other than acrobat
2129                        import ho.pisa as pisa
2130                        pisa.startViewer(path)
2131                    except:
2132                        msg = "This feature requires 'PDF Viewer'\n"
2133                        msg += "Please install it first (Free)..."
2134                        wx.MessageBox(msg, 'Error')
2135            else:
2136                try:
2137                    command = "open '%s'" % path
2138                    os.system(command)
2139                except:
2140                    try:
2141                        #in case when the pdf default set other than preview
2142                        import ho.pisa as pisa
2143                        pisa.startViewer(path)
2144                    except:
2145                        msg = "This feature requires 'Preview' Application\n"
2146                        msg += "Please install it first..."
2147                        wx.MessageBox(msg, 'Error')
2148
2149                     
2150    def set_manager(self, manager):
2151        """
2152        Sets the application manager for this frame
2153       
2154        :param manager: frame manager
2155        """
2156        self.app_manager = manager
2157       
2158    def post_init(self):
2159        """
2160        This initialization method is called after the GUI
2161        has been created and all plug-ins loaded. It calls
2162        the post_init() method of each plug-in (if it exists)
2163        so that final initialization can be done.
2164        """
2165        for item in self.plugins:
2166            if hasattr(item, "post_init"):
2167                item.post_init()
2168       
2169    def set_default_perspective(self):
2170        """
2171        Choose among the plugin the first plug-in that has
2172        "set_default_perspective" method and its return value is True will be
2173        as a default perspective when the welcome page is closed
2174        """
2175        for item in self.plugins:
2176            if hasattr(item, "set_default_perspective"):
2177                if item.set_default_perspective():
2178                    item.on_perspective(event=None)
2179                    return 
2180       
2181    def set_perspective(self, panels):
2182        """
2183        Sets the perspective of the GUI.
2184        Opens all the panels in the list, and closes
2185        all the others.
2186       
2187        :param panels: list of panels
2188        """
2189        for item in self.panels.keys():
2190            # Check whether this is a sticky panel
2191            if hasattr(self.panels[item], "ALWAYS_ON"):
2192                if self.panels[item].ALWAYS_ON:
2193                    continue 
2194            if self.panels[item] == None:
2195                continue
2196            if self.panels[item].window_name in panels:
2197                frame = self.panels[item].get_frame()
2198                if not frame.IsShown():
2199                    frame.Show(True)
2200            else:
2201                # always show the data panel if enable
2202                style = self.__gui_style & GUIFRAME.MANAGER_ON
2203                if (style == GUIFRAME.MANAGER_ON) and self.panels[item] == self._data_panel:
2204                    if 'data_panel' in self.panels.keys():
2205                        frame = self.panels['data_panel'].get_frame()
2206                        if frame == None:
2207                            continue
2208                        flag = frame.IsShown()
2209                        frame.Show(flag)
2210                else:
2211                    frame = self.panels[item].get_frame()
2212                    if frame == None:
2213                        continue
2214
2215                    if frame.IsShown():
2216                        frame.Show(False)
2217       
2218    def show_data_panel(self, event=None, action=True):
2219        """
2220        show the data panel
2221        """
2222        if self._data_panel_menu == None:
2223            return
2224        label = self._data_panel_menu.GetText()
2225        pane = self.panels["data_panel"]
2226        frame = pane.get_frame()
2227        if label == 'Show Data Explorer':
2228            if action: 
2229                frame.Show(True)
2230            self.__gui_style = self.__gui_style | GUIFRAME.MANAGER_ON
2231            self._data_panel_menu.SetText('Hide Data Explorer')
2232        else:
2233            if action:
2234                frame.Show(False)
2235            self.__gui_style = self.__gui_style & (~GUIFRAME.MANAGER_ON)
2236            self._data_panel_menu.SetText('Show Data Explorer')
2237
2238    def add_data_helper(self, data_list):
2239        """
2240        """
2241        if self._data_manager is not None:
2242            self._data_manager.add_data(data_list)
2243       
2244    def add_data(self, data_list):
2245        """
2246        receive a dictionary of data from loader
2247        store them its data manager if possible
2248        send to data the current active perspective if the data panel
2249        is not active.
2250        :param data_list: dictionary of data's ID and value Data
2251        """
2252        #Store data into manager
2253        self.add_data_helper(data_list)
2254        # set data in the data panel
2255        if self._data_panel is not None:
2256            data_state = self._data_manager.get_data_state(data_list.keys())
2257            self._data_panel.load_data_list(data_state)
2258        #if the data panel is shown wait for the user to press a button
2259        #to send data to the current perspective. if the panel is not
2260        #show  automatically send the data to the current perspective
2261        style = self.__gui_style & GUIFRAME.MANAGER_ON
2262        if style == GUIFRAME.MANAGER_ON:
2263            #wait for button press from the data panel to set_data
2264            if self._data_panel is not None:
2265                self._data_panel.frame.Show(True)
2266        else:
2267            #automatically send that to the current perspective
2268            self.set_data(data_id=data_list.keys())
2269       
2270    def set_data(self, data_id, theory_id=None): 
2271        """
2272        set data to current perspective
2273        """
2274        list_data, _ = self._data_manager.get_by_id(data_id)
2275        if self._current_perspective is not None:
2276            self._current_perspective.set_data(list_data.values())
2277
2278        else:
2279            msg = "Guiframe does not have a current perspective"
2280            logging.info(msg)
2281           
2282    def set_theory(self, state_id, theory_id=None):
2283        """
2284        """
2285        _, list_theory = self._data_manager.get_by_id(theory_id)
2286        if self._current_perspective is not None:
2287            try:
2288                self._current_perspective.set_theory(list_theory.values())
2289            except:
2290                msg = "Guiframe set_theory: \n" + str(sys.exc_value)
2291                logging.info(msg)
2292                wx.PostEvent(self, StatusEvent(status=msg, info="error"))
2293        else:
2294            msg = "Guiframe does not have a current perspective"
2295            logging.info(msg)
2296           
2297    def plot_data(self,  state_id, data_id=None,
2298                  theory_id=None, append=False):
2299        """
2300        send a list of data to plot
2301        """
2302        total_plot_list = []
2303        data_list, _ = self._data_manager.get_by_id(data_id)
2304        _, temp_list_theory = self._data_manager.get_by_id(theory_id)
2305        total_plot_list = data_list.values()
2306        for item in temp_list_theory.values():
2307            theory_data, theory_state = item
2308            total_plot_list.append(theory_data)
2309        GROUP_ID = wx.NewId()
2310        for new_plot in total_plot_list:
2311            if append:
2312                if self.panel_on_focus is None:
2313                    message = "cannot append plot. No plot panel on focus!"
2314                    message += "please click on any available plot to set focus"
2315                    wx.PostEvent(self, StatusEvent(status=message, 
2316                                                   info='warning'))
2317                    return 
2318                else:
2319                    if self.enable_add_data(new_plot):
2320                        new_plot.group_id = self.panel_on_focus.group_id
2321                    else:
2322                        message = "Only 1D Data can be append to"
2323                        message += " plot panel containing 1D data.\n"
2324                        message += "%s not be appended.\n" %str(new_plot.name)
2325                        message += "try new plot option.\n"
2326                        wx.PostEvent(self, StatusEvent(status=message, 
2327                                                   info='warning'))
2328            else:
2329                #if not append then new plot
2330                from sans.guiframe.dataFitting import Data2D
2331                if issubclass(Data2D, new_plot.__class__):
2332                    #for 2 D always plot in a separated new plot
2333                    new_plot.group_id = wx.NewId()
2334                else:
2335                    # plot all 1D in a new plot
2336                    new_plot.group_id = GROUP_ID
2337            title = "PLOT " + str(new_plot.title)
2338            wx.PostEvent(self, NewPlotEvent(plot=new_plot,
2339                                                  title=title,
2340                                                  group_id = new_plot.group_id))
2341           
2342    def remove_data(self, data_id, theory_id=None):
2343        """
2344        Delete data state if data_id is provide
2345        delete theory created with data of id data_id if theory_id is provide
2346        if delete all true: delete the all state
2347        else delete theory
2348        """
2349        temp = data_id + theory_id
2350        for plug in self.plugins:
2351            plug.delete_data(temp)
2352        total_plot_list = []
2353        data_list, _ = self._data_manager.get_by_id(data_id)
2354        _, temp_list_theory = self._data_manager.get_by_id(theory_id)
2355        total_plot_list = data_list.values()
2356        for item in temp_list_theory.values():
2357            theory_data, theory_state = item
2358            total_plot_list.append(theory_data)
2359        for new_plot in total_plot_list:
2360            id = new_plot.id
2361            for group_id in new_plot.list_group_id:
2362                wx.PostEvent(self, NewPlotEvent(id=id,
2363                                                   group_id=group_id,
2364                                                   action='remove'))
2365                #remove res plot: Todo: improve
2366                wx.CallAfter(self._remove_res_plot, id)
2367        self._data_manager.delete_data(data_id=data_id, 
2368                                       theory_id=theory_id)
2369       
2370    def _remove_res_plot(self, id):
2371        """
2372        Try to remove corresponding res plot
2373       
2374        : param id: id of the data
2375        """
2376        try:
2377            wx.PostEvent(self, NewPlotEvent(id=("res"+str(id)),
2378                                           group_id=("res"+str(id)),
2379                                           action='remove'))
2380        except:
2381            pass
2382   
2383    def save_data1d(self, data, fname):
2384        """
2385        Save data dialog
2386        """
2387        default_name = fname
2388        wildcard = "Text files (*.txt)|*.txt|"\
2389                    "CanSAS 1D files(*.xml)|*.xml" 
2390        path = None
2391        dlg = wx.FileDialog(self, "Choose a file",
2392                            self._default_save_location,
2393                            default_name, wildcard , wx.SAVE)
2394       
2395        if dlg.ShowModal() == wx.ID_OK:
2396            path = dlg.GetPath()
2397            # ext_num = 0 for .txt, ext_num = 1 for .xml
2398            # This is MAC Fix
2399            ext_num = dlg.GetFilterIndex()
2400            if ext_num == 0:
2401                format = '.txt'
2402            else:
2403                format = '.xml'
2404            path = os.path.splitext(path)[0] + format
2405            mypath = os.path.basename(path)
2406           
2407            #TODO: This is bad design. The DataLoader is designed
2408            #to recognize extensions.
2409            # It should be a simple matter of calling the .
2410            #save(file, data, '.xml') method
2411            # of the sans.dataloader.loader.Loader class.
2412            from sans.dataloader.loader import  Loader
2413            #Instantiate a loader
2414            loader = Loader() 
2415            format = ".txt"
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                self._onsaveTXT(data, fName)
2421            format = ".xml"
2422            if os.path.splitext(mypath)[1].lower() == format:
2423                # Make sure the ext included in the file name
2424                # especially on MAC
2425                fName = os.path.splitext(path)[0] + format
2426                loader.save(fName, data, format)
2427            try:
2428                self._default_save_location = os.path.dirname(path)
2429            except:
2430                pass   
2431        dlg.Destroy()
2432       
2433       
2434    def _onsaveTXT(self, data, path):
2435        """
2436        Save file as txt 
2437        :TODO: Refactor and remove this method. See TODO in _onSave.
2438        """
2439        if not path == None:
2440            out = open(path, 'w')
2441            has_errors = True
2442            if data.dy == None or data.dy == []:
2443                has_errors = False
2444            # Sanity check
2445            if has_errors:
2446                try:
2447                    if len(data.y) != len(data.dy):
2448                        has_errors = False
2449                except:
2450                    has_errors = False
2451            if has_errors:
2452                if data.dx != None and data.dx != []:
2453                    out.write("<X>   <Y>   <dY>   <dX>\n")
2454                else:
2455                    out.write("<X>   <Y>   <dY>\n")
2456            else:
2457                out.write("<X>   <Y>\n")
2458               
2459            for i in range(len(data.x)):
2460                if has_errors:
2461                    if data.dx != None and data.dx != []:
2462                        if  data.dx[i] != None:
2463                            out.write("%g  %g  %g  %g\n" % (data.x[i], 
2464                                                        data.y[i],
2465                                                        data.dy[i],
2466                                                        data.dx[i]))
2467                        else:
2468                            out.write("%g  %g  %g\n" % (data.x[i], 
2469                                                        data.y[i],
2470                                                        data.dy[i]))
2471                    else:
2472                        out.write("%g  %g  %g\n" % (data.x[i], 
2473                                                    data.y[i],
2474                                                    data.dy[i]))
2475                else:
2476                    out.write("%g  %g\n" % (data.x[i], 
2477                                            data.y[i]))
2478            out.close() 
2479                             
2480    def show_data1d(self, data, name):
2481        """
2482        Show data dialog
2483        """   
2484        try:
2485            xmin = min(data.x)
2486            ymin = min(data.y)
2487        except:
2488            msg = "Unable to find min/max of \n data named %s"% \
2489                        data.filename 
2490            wx.PostEvent(self, StatusEvent(status=msg,
2491                                       info="error"))
2492            raise ValueError, msg
2493        ## text = str(data)
2494        text = data.__str__()
2495        text += 'Data Min Max:\n'
2496        text += 'X_min = %s:  X_max = %s\n'% (xmin, max(data.x))
2497        text += 'Y_min = %s:  Y_max = %s\n'% (ymin, max(data.y))
2498        if data.dy != None:
2499            text += 'dY_min = %s:  dY_max = %s\n'% (min(data.dy), max(data.dy))
2500        text += '\nData Points:\n'
2501        x_st = "X"
2502        for index in range(len(data.x)):
2503            if data.dy != None and len(data.dy) > index:
2504                dy_val = data.dy[index]
2505            else:
2506                dy_val = 0.0
2507            if data.dx != None and len(data.dx) > index:
2508                dx_val = data.dx[index]
2509            else:
2510                dx_val = 0.0
2511            if data.dxl != None and len(data.dxl) > index:
2512                if index == 0: 
2513                    x_st = "Xl"
2514                dx_val = data.dxl[index]
2515            elif data.dxw != None and len(data.dxw) > index:
2516                if index == 0: 
2517                    x_st = "Xw"
2518                dx_val = data.dxw[index]
2519           
2520            if index == 0:
2521                text += "<index> \t<X> \t<Y> \t<dY> \t<d%s>\n"% x_st
2522            text += "%s \t%s \t%s \t%s \t%s\n" % (index,
2523                                            data.x[index], 
2524                                            data.y[index],
2525                                            dy_val,
2526                                            dx_val)
2527        from pdfview import TextFrame
2528        frame = TextFrame(None, -1, "Data Info: %s"% data.name, text) 
2529        # put icon
2530        self.put_icon(frame) 
2531        frame.Show(True) 
2532           
2533    def save_data2d(self, data, fname):   
2534        """
2535        Save data2d dialog
2536        """
2537        default_name = fname
2538        wildcard = "IGOR/DAT 2D file in Q_map (*.dat)|*.DAT"
2539        dlg = wx.FileDialog(self, "Choose a file",
2540                            self._default_save_location,
2541                            default_name, wildcard , wx.SAVE)
2542       
2543        if dlg.ShowModal() == wx.ID_OK:
2544            path = dlg.GetPath()
2545            # ext_num = 0 for .txt, ext_num = 1 for .xml
2546            # This is MAC Fix
2547            ext_num = dlg.GetFilterIndex()
2548            if ext_num == 0:
2549                format = '.dat'
2550            else:
2551                format = ''
2552            path = os.path.splitext(path)[0] + format
2553            mypath = os.path.basename(path)
2554           
2555            #TODO: This is bad design. The DataLoader is designed
2556            #to recognize extensions.
2557            # It should be a simple matter of calling the .
2558            #save(file, data, '.xml') method
2559            # of the DataLoader.loader.Loader class.
2560            from sans.dataloader.loader import  Loader
2561            #Instantiate a loader
2562            loader = Loader() 
2563
2564            format = ".dat"
2565            if os.path.splitext(mypath)[1].lower() == format:
2566                # Make sure the ext included in the file name
2567                # especially on MAC
2568                fileName = os.path.splitext(path)[0] + format
2569                loader.save(fileName, data, format)
2570            try:
2571                self._default_save_location = os.path.dirname(path)
2572            except:
2573                pass   
2574        dlg.Destroy() 
2575                             
2576    def show_data2d(self, data, name):
2577        """
2578        Show data dialog
2579        """   
2580
2581        wx.PostEvent(self, StatusEvent(status = "Gathering Data2D Info.", 
2582                                       type = 'start' ))
2583        text = data.__str__() 
2584        text += 'Data Min Max:\n'
2585        text += 'I_min = %s\n'% min(data.data)
2586        text += 'I_max = %s\n\n'% max(data.data)
2587        text += 'Data (First 2501) Points:\n'
2588        text += 'Data columns include err(I).\n'
2589        text += 'ASCII data starts here.\n'
2590        text += "<index> \t<Qx> \t<Qy> \t<I> \t<dI> \t<dQparal> \t<dQperp>\n"
2591        di_val = 0.0
2592        dx_val = 0.0
2593        dy_val = 0.0
2594        #mask_val = True
2595        len_data = len(data.qx_data)
2596        for index in xrange(0, len_data):
2597            x_val = data.qx_data[index]
2598            y_val = data.qy_data[index]
2599            i_val = data.data[index]
2600            if data.err_data != None: 
2601                di_val = data.err_data[index]
2602            if data.dqx_data != None: 
2603                dx_val = data.dqx_data[index]
2604            if data.dqy_data != None: 
2605                dy_val = data.dqy_data[index]
2606 
2607            text += "%s \t%s \t%s \t%s \t%s \t%s \t%s\n" % (index,
2608                                            x_val, 
2609                                            y_val,
2610                                            i_val,
2611                                            di_val,
2612                                            dx_val,
2613                                            dy_val)
2614            # Takes too long time for typical data2d: Break here
2615            if index >= 2500:
2616                text += ".............\n"
2617                break
2618
2619        from pdfview import TextFrame
2620        frame = TextFrame(None, -1, "Data Info: %s"% data.name, text) 
2621        # put icon
2622        self.put_icon(frame)
2623        frame.Show(True) 
2624        wx.PostEvent(self, StatusEvent(status = "Data2D Info Displayed", 
2625                                       type = 'stop' ))
2626                                 
2627    def set_current_perspective(self, perspective):
2628        """
2629        set the current active perspective
2630        """
2631        self._current_perspective = perspective
2632        name = "No current analysis selected"
2633        if self._current_perspective is not None:
2634            self._add_current_plugin_menu()
2635            for panel in self.panels.values():
2636                if hasattr(panel, 'CENTER_PANE') and panel.CENTER_PANE:
2637                    for name in self._current_perspective.get_perspective():
2638                        frame = panel.get_frame()
2639                        if frame != None:
2640                            if name == panel.window_name:
2641                                panel.on_set_focus(event=None)
2642                                frame.Show(True)
2643                            else:
2644                                frame.Show(False)
2645                            #break               
2646            name = self._current_perspective.sub_menu
2647            if self._data_panel is not None:
2648                self._data_panel.set_active_perspective(name)
2649                self._check_applications_menu()
2650            #Set the SasView title
2651            self._set_title_name(name)
2652           
2653    def _set_title_name(self, name):
2654        """
2655        Set the SasView title w/ the current application name
2656       
2657        : param name: application name [string]
2658        """
2659        # Set SanView Window title w/ application anme
2660        title = self.title + "  - " + name + " -"
2661        self.SetTitle(title)
2662           
2663    def _check_applications_menu(self):
2664        """
2665        check the menu of the current application
2666        """
2667        if self._applications_menu is not None:
2668            for menu in self._applications_menu.GetMenuItems():
2669                if self._current_perspective is not None:
2670                    name = self._current_perspective.sub_menu
2671                    if menu.IsCheckable():
2672                        if menu.GetLabel() == name:
2673                            menu.Check(True)
2674                        else:
2675                            menu.Check(False) 
2676           
2677    def enable_add_data(self, new_plot):
2678        """
2679        Enable append data on a plot panel
2680        """
2681
2682        if self.panel_on_focus not in self._plotting_plugin.plot_panels.values():
2683            return
2684        is_theory = len(self.panel_on_focus.plots) <= 1 and \
2685            self.panel_on_focus.plots.values()[0].__class__.__name__ == "Theory1D"
2686           
2687        is_data2d = hasattr(new_plot, 'data')
2688       
2689        is_data1d = self.panel_on_focus.__class__.__name__ == "ModelPanel1D"\
2690            and self.panel_on_focus.group_id is not None
2691        has_meta_data = hasattr(new_plot, 'meta_data')
2692       
2693        #disable_add_data if the data is being recovered from  a saved state file.
2694        is_state_data = False
2695        if has_meta_data:
2696            if 'invstate' in new_plot.meta_data: 
2697                is_state_data = True
2698            if  'prstate' in new_plot.meta_data: 
2699                is_state_data = True
2700            if  'fitstate' in new_plot.meta_data: 
2701                is_state_data = True
2702   
2703        return is_data1d and not is_data2d and not is_theory and not is_state_data
2704   
2705    def check_multimode(self, perspective=None):
2706        """
2707        Check the perspective have batch mode capablitity
2708        """
2709        if perspective == None or self._data_panel == None:
2710            return
2711        flag = perspective.get_batch_capable()
2712        flag_on = perspective.batch_on
2713        if flag:
2714            self._data_panel.rb_single_mode.SetValue(not flag_on)
2715            self._data_panel.rb_batch_mode.SetValue(flag_on)
2716        else:
2717            self._data_panel.rb_single_mode.SetValue(True)
2718            self._data_panel.rb_batch_mode.SetValue(False)
2719        self._data_panel.rb_single_mode.Enable(flag)
2720        self._data_panel.rb_batch_mode.Enable(flag)
2721               
2722
2723   
2724    def enable_edit_menu(self):
2725        """
2726        enable menu item under edit menu depending on the panel on focus
2727        """
2728        if self.cpanel_on_focus is not None and self._edit_menu is not None:
2729            flag = self.cpanel_on_focus.get_undo_flag()
2730            self._edit_menu.Enable(GUIFRAME_ID.UNDO_ID, flag)
2731            flag = self.cpanel_on_focus.get_redo_flag()
2732            self._edit_menu.Enable(GUIFRAME_ID.REDO_ID, flag)
2733            flag = self.cpanel_on_focus.get_copy_flag()
2734            self._edit_menu.Enable(GUIFRAME_ID.COPY_ID, flag)
2735            flag = self.cpanel_on_focus.get_paste_flag()
2736            self._edit_menu.Enable(GUIFRAME_ID.PASTE_ID, flag)
2737
2738            flag = self.cpanel_on_focus.get_preview_flag()
2739            self._edit_menu.Enable(GUIFRAME_ID.PREVIEW_ID, flag)
2740            flag = self.cpanel_on_focus.get_reset_flag()
2741            self._edit_menu.Enable(GUIFRAME_ID.RESET_ID, flag)
2742        else:
2743            flag = False
2744            self._edit_menu.Enable(GUIFRAME_ID.UNDO_ID, flag)
2745            self._edit_menu.Enable(GUIFRAME_ID.REDO_ID, flag)
2746            self._edit_menu.Enable(GUIFRAME_ID.COPY_ID, flag)
2747            self._edit_menu.Enable(GUIFRAME_ID.PASTE_ID, flag)
2748
2749            self._edit_menu.Enable(GUIFRAME_ID.PREVIEW_ID, flag)
2750            self._edit_menu.Enable(GUIFRAME_ID.RESET_ID, flag)
2751           
2752    def on_undo_panel(self, event=None):
2753        """
2754        undo previous action of the last panel on focus if possible
2755        """
2756        if self.cpanel_on_focus is not None:
2757            self.cpanel_on_focus.on_undo(event)
2758           
2759    def on_redo_panel(self, event=None):
2760        """
2761        redo the last cancel action done on the last panel on focus
2762        """
2763        if self.cpanel_on_focus is not None:
2764            self.cpanel_on_focus.on_redo(event)
2765           
2766    def on_copy_panel(self, event=None):
2767        """
2768        copy the last panel on focus if possible
2769        """
2770        if self.cpanel_on_focus is not None:
2771            self.cpanel_on_focus.on_copy(event)
2772           
2773    def on_paste_panel(self, event=None):
2774        """
2775        paste clipboard to the last panel on focus
2776        """
2777        if self.cpanel_on_focus is not None:
2778            self.cpanel_on_focus.on_paste(event)
2779                   
2780    def on_bookmark_panel(self, event=None):
2781        """
2782        bookmark panel
2783        """
2784        if self.cpanel_on_focus is not None:
2785            self.cpanel_on_focus.on_bookmark(event)
2786           
2787    def append_bookmark(self, event=None):
2788        """
2789        Bookmark available information of the panel on focus
2790        """
2791        self._toolbar.append_bookmark(event)
2792           
2793    def on_save_panel(self, event=None):
2794        """
2795        save possible information on the current panel
2796        """
2797        if self.cpanel_on_focus is not None:
2798            self.cpanel_on_focus.on_save(event)
2799           
2800    def on_preview_panel(self, event=None):
2801        """
2802        preview information on the panel on focus
2803        """
2804        if self.cpanel_on_focus is not None:
2805            self.cpanel_on_focus.on_preview(event)
2806           
2807    def on_print_panel(self, event=None):
2808        """
2809        print available information on the last panel on focus
2810        """
2811        if self.cpanel_on_focus is not None:
2812            self.cpanel_on_focus.on_print(event)
2813           
2814    def on_zoom_panel(self, event=None):
2815        """
2816        zoom on the current panel if possible
2817        """
2818        if self.cpanel_on_focus is not None:
2819            self.cpanel_on_focus.on_zoom(event)
2820           
2821    def on_zoom_in_panel(self, event=None):
2822        """
2823        zoom in of the panel on focus
2824        """
2825        if self.cpanel_on_focus is not None:
2826            self.cpanel_on_focus.on_zoom_in(event)
2827           
2828    def on_zoom_out_panel(self, event=None):
2829        """
2830        zoom out on the panel on focus
2831        """
2832        if self.cpanel_on_focus is not None:
2833            self.cpanel_on_focus.on_zoom_out(event)
2834           
2835    def on_drag_panel(self, event=None):
2836        """
2837        drag apply to the panel on focus
2838        """
2839        if self.cpanel_on_focus is not None:
2840            self.cpanel_on_focus.on_drag(event)
2841           
2842    def on_reset_panel(self, event=None):
2843        """
2844        reset the current panel
2845        """
2846        if self.cpanel_on_focus is not None:
2847            self.cpanel_on_focus.on_reset(event)
2848           
2849    def on_change_caption(self, name, old_caption, new_caption):     
2850        """
2851        Change the panel caption
2852       
2853        :param name: window_name of the pane
2854        :param old_caption: current caption [string]
2855        :param new_caption: new caption [string]
2856        """
2857        # wx.aui.AuiPaneInfo
2858        pane_info = self.get_paneinfo(old_caption) 
2859        # update the data_panel.cb_plotpanel
2860        if 'data_panel' in self.panels.keys():
2861            # remove from data_panel combobox
2862            data_panel = self.panels["data_panel"]
2863            if data_panel.cb_plotpanel is not None:
2864                # Check if any panel has the same caption
2865                has_newstring = data_panel.cb_plotpanel.FindString\
2866                                                            (str(new_caption)) 
2867                caption = new_caption
2868                if has_newstring != wx.NOT_FOUND:
2869                    captions = self._get_plotpanel_captions()
2870                    # Append nummber
2871                    inc = 1
2872                    while (1):
2873                        caption = new_caption + '_%s'% str(inc)
2874                        if caption not in captions:
2875                            break
2876                        inc += 1
2877                    # notify to users
2878                    msg = "Found Same Title: Added '_%s'"% str(inc)
2879                    wx.PostEvent(self, StatusEvent(status=msg))
2880                # update data_panel cb
2881                pos = data_panel.cb_plotpanel.FindString(str(old_caption)) 
2882                if pos != wx.NOT_FOUND:
2883                    data_panel.cb_plotpanel.SetString(pos, caption)
2884                    data_panel.cb_plotpanel.SetStringSelection(caption)
2885        # update window Show menu
2886        if self._window_menu != None:
2887            for item in self._window_menu.GetMenuItems():
2888                pos = self._window_menu.FindItem(old_caption)
2889                if self._window_menu.GetLabel(pos) == str(old_caption):
2890                    self._window_menu.SetLabel(pos, caption)
2891                break
2892        # New Caption
2893        pane_info.SetTitle(caption)
2894        return caption
2895       
2896    def get_paneinfo(self, name):
2897        """
2898        Get pane Caption from window_name
2899       
2900        :param name: window_name in AuiPaneInfo
2901        : return: AuiPaneInfo of the name
2902        """
2903        for panel in self.plot_panels.values():
2904            if panel.frame.GetTitle() == name:
2905                return panel.frame
2906        return None
2907   
2908    def enable_undo(self):
2909        """
2910        enable undo related control
2911        """
2912        if self.cpanel_on_focus is not None:
2913            self._toolbar.enable_undo(self.cpanel_on_focus)
2914           
2915    def enable_redo(self):
2916        """
2917        enable redo
2918        """
2919        if self.cpanel_on_focus is not None:
2920            self._toolbar.enable_redo(self.cpanel_on_focus)
2921           
2922    def enable_copy(self):
2923        """
2924        enable copy related control
2925        """
2926        if self.cpanel_on_focus is not None:
2927            self._toolbar.enable_copy(self.cpanel_on_focus)
2928           
2929    def enable_paste(self):
2930        """
2931        enable paste
2932        """
2933        if self.cpanel_on_focus is not None:
2934            self._toolbar.enable_paste(self.cpanel_on_focus)
2935                       
2936    def enable_bookmark(self):
2937        """
2938        Bookmark
2939        """
2940        if self.cpanel_on_focus is not None:
2941            self._toolbar.enable_bookmark(self.cpanel_on_focus)
2942           
2943    def enable_save(self):
2944        """
2945        save
2946        """
2947        if self.cpanel_on_focus is not None:
2948            self._toolbar.enable_save(self.cpanel_on_focus)
2949           
2950    def enable_preview(self):
2951        """
2952        preview
2953        """
2954        if self.cpanel_on_focus is not None:
2955            self._toolbar.enable_preview(self.cpanel_on_focus)
2956           
2957    def enable_print(self):
2958        """
2959        print
2960        """
2961        if self.cpanel_on_focus is not None:
2962            self._toolbar.enable_print(self.cpanel_on_focus)
2963           
2964    def enable_zoom(self):
2965        """
2966        zoom
2967        """
2968        if self.cpanel_on_focus is not None:
2969            self._toolbar.enable_zoom(self.panel_on_focus)
2970           
2971    def enable_zoom_in(self):
2972        """
2973        zoom in
2974        """
2975        if self.cpanel_on_focus is not None:
2976            self._toolbar.enable_zoom_in(self.panel_on_focus)
2977           
2978    def enable_zoom_out(self):
2979        """
2980        zoom out
2981        """
2982        if self.cpanel_on_focus is not None:
2983            self._toolbar.enable_zoom_out(self.panel_on_focus)
2984           
2985    def enable_drag(self, event=None):
2986        """
2987        drag
2988        """
2989        #Not implemeted
2990           
2991    def enable_reset(self):
2992        """
2993        reset the current panel
2994        """
2995        if self.cpanel_on_focus is not None:
2996            self._toolbar.enable_reset(self.panel_on_focus)
2997           
2998    def get_toolbar_height(self):
2999        """
3000        """
3001        size_y = 0
3002        if self.GetToolBar() != None and self.GetToolBar().IsShown():
3003            if not IS_LINUX:
3004                _, size_y = self.GetToolBar().GetSizeTuple()
3005        return size_y
3006   
3007    def set_schedule_full_draw(self, panel=None, func='del'):
3008        """
3009        Add/subtract the schedule full draw list with the panel given
3010       
3011        :param panel: plot panel
3012        :param func: append or del [string]
3013        """
3014
3015        # append this panel in the schedule list if not in yet
3016        if func == 'append':
3017            if not panel in self.schedule_full_draw_list:
3018                self.schedule_full_draw_list.append(panel) 
3019        # remove this panel from schedule list
3020        elif func == 'del':
3021            if len(self.schedule_full_draw_list) > 0:
3022                if panel in self.schedule_full_draw_list:
3023                    self.schedule_full_draw_list.remove(panel)
3024
3025        # reset the schdule
3026        if len(self.schedule_full_draw_list) == 0:
3027            self.schedule = False
3028        else:
3029            self.schedule = True   
3030       
3031    def full_draw(self):
3032        """
3033        Draw the panels with axes in the schedule to full dwar list
3034        """
3035       
3036        count = len(self.schedule_full_draw_list)
3037        #if not self.schedule:
3038        if count < 1:
3039            self.set_schedule(False)
3040            return
3041
3042        else:
3043            ind = 0
3044            # if any of the panel is shown do full_draw
3045            for panel in self.schedule_full_draw_list:
3046                ind += 1
3047                if panel.frame.IsShown():
3048                    break
3049                # otherwise, return
3050                if ind == count:
3051                    return
3052        #Simple redraw only for a panel shown
3053        def f_draw(panel):
3054            """
3055            Draw A panel in the full draw list
3056            """
3057            try:
3058                # This checking of GetCapture is to stop redrawing
3059                # while any panel is capture.
3060                frame = panel.frame
3061               
3062                if not frame.GetCapture():
3063                    # draw if possible
3064                    panel.set_resizing(False)
3065                    #panel.Show(True)
3066                    panel.draw_plot()
3067                # Check if the panel is not shown
3068                flag = frame.IsShown()
3069                frame.Show(flag) 
3070            except:
3071                pass
3072     
3073        # Draw all panels
3074        if count == 1:
3075            f_draw(self.schedule_full_draw_list[0]) 
3076        else:
3077            map(f_draw, self.schedule_full_draw_list)
3078        # Reset the attr 
3079        if len(self.schedule_full_draw_list) == 0:
3080            self.set_schedule(False)
3081        else:
3082            self.set_schedule(True)
3083       
3084    def set_schedule(self, schedule=False): 
3085        """
3086        Set schedule
3087        """
3088        self.schedule = schedule
3089               
3090    def get_schedule(self): 
3091        """
3092        Get schedule
3093        """
3094        return self.schedule
3095   
3096    def on_set_plot_focus(self, panel):
3097        """
3098        Set focus on a plot panel
3099        """
3100        if panel == None:
3101            return
3102        #self.set_plot_unfocus()
3103        panel.on_set_focus(None) 
3104        # set focusing panel
3105        self.panel_on_focus = panel 
3106        self.set_panel_on_focus(None)
3107
3108    def set_plot_unfocus(self): 
3109        """
3110        Un focus all plot panels
3111        """
3112        for plot in self.plot_panels.values():
3113            plot.on_kill_focus(None)
3114   
3115    def get_window_size(self):
3116        """
3117        Get window size
3118       
3119        :return size: tuple
3120        """
3121        width, height = self.GetSizeTuple()
3122        if not IS_WIN:
3123            # Subtract toolbar height to get real window side
3124            if self._toolbar.IsShown():
3125                height -= 45
3126        return (width, height)
3127           
3128    def _onDrawIdle(self, *args, **kwargs):
3129        """
3130        ReDraw with axes
3131        """
3132        try:
3133            # check if it is time to redraw
3134            if self.GetCapture() == None:
3135                # Draw plot, changes resizing too
3136                self.full_draw()
3137        except:
3138            pass
3139           
3140        # restart idle       
3141        self._redraw_idle(*args, **kwargs)
3142
3143           
3144    def _redraw_idle(self, *args, **kwargs):
3145        """
3146        Restart Idle
3147        """
3148        # restart idle   
3149        self.idletimer.Restart(100*TIME_FACTOR, *args, **kwargs)
3150
3151       
3152class DefaultPanel(wx.Panel, PanelBase):
3153    """
3154    Defines the API for a panels to work with
3155    the GUI manager
3156    """
3157    ## Internal nickname for the window, used by the AUI manager
3158    window_name = "default"
3159    ## Name to appear on the window title bar
3160    window_caption = "Welcome panel"
3161    ## Flag to tell the AUI manager to put this panel in the center pane
3162    CENTER_PANE = True
3163    def __init__(self, parent, *args, **kwds):
3164        wx.Panel.__init__(self, parent, *args, **kwds)
3165        PanelBase.__init__(self, parent)
3166   
3167
3168
3169class ViewApp(wx.App):
3170    """
3171    Toy application to test this Frame
3172    """
3173    def OnInit(self):
3174        """
3175        When initialised
3176        """
3177        pos, size, self.is_max = self.window_placement((GUIFRAME_WIDTH, 
3178                                           GUIFRAME_HEIGHT))     
3179        self.frame = ViewerFrame(parent=None, 
3180                             title=APPLICATION_NAME, 
3181                             pos=pos, 
3182                             gui_style = DEFAULT_STYLE,
3183                             size=size)
3184        self.frame.Hide()
3185        if not IS_WIN:
3186            self.frame.EnableCloseButton(False)
3187        self.s_screen = None
3188
3189        try:
3190            self.open_file()
3191        except:
3192            msg = "%s Could not load " % str(APPLICATION_NAME)
3193            msg += "input file from command line.\n"
3194            logging.error(msg)
3195        # Display a splash screen on top of the frame.
3196        try:
3197            if os.path.isfile(SPLASH_SCREEN_PATH):
3198                self.s_screen = self.display_splash_screen(parent=self.frame, 
3199                                        path=SPLASH_SCREEN_PATH)
3200            else:
3201                self.frame.Show()   
3202        except:
3203            if self.s_screen is not None:
3204                self.s_screen.Close()
3205            msg = "Cannot display splash screen\n"
3206            msg += str (sys.exc_value)
3207            logging.error(msg)
3208            self.frame.Show()
3209
3210        self.SetTopWindow(self.frame)
3211 
3212        return True
3213   
3214    def maximize_win(self):
3215        """
3216        Maximize the window after the frame shown
3217        """
3218        if self.is_max:
3219            if self.frame.IsShown():
3220                # Max window size
3221                self.frame.Maximize(self.is_max)
3222
3223    def open_file(self):
3224        """
3225        open a state file at the start of the application
3226        """
3227        input_file = None
3228        if len(sys.argv) >= 2:
3229            cmd = sys.argv[0].lower()
3230            basename  = os.path.basename(cmd)
3231            app_base = str(APPLICATION_NAME).lower()
3232            if os.path.isfile(cmd) or basename.lower() == app_base:
3233                app_py = app_base + '.py'
3234                app_exe = app_base + '.exe'
3235                app_app = app_base + '.app'
3236                if basename.lower() in [app_py, app_exe, app_app, app_base]:
3237                    data_base = sys.argv[1]
3238                    input_file = os.path.normpath(os.path.join(DATAPATH, 
3239                                                               data_base))
3240        if input_file is None:
3241            return
3242        if self.frame is not None:
3243            self.frame.set_input_file(input_file=input_file)
3244         
3245    def clean_plugin_models(self, path): 
3246        """
3247        Delete plugin models  in app folder
3248       
3249        :param path: path of the plugin_models folder in app
3250        """
3251        # do it only the first time app loaded
3252        # delete unused model folder   
3253        model_folder = os.path.join(PATH_APP, path)
3254        if os.path.exists(model_folder) and os.path.isdir(model_folder):
3255            if len(os.listdir(model_folder)) > 0:
3256                try:
3257                    for file in os.listdir(model_folder):
3258                        file_path = os.path.join(model_folder, file)
3259                        if os.path.isfile(file_path):
3260                            os.remove(file_path)
3261                except:
3262                    logging.error("gui_manager.clean_plugin_models:\n  %s" \
3263                                  % sys.exc_value)
3264             
3265    def set_manager(self, manager):
3266        """
3267        Sets a reference to the application manager
3268        of the GUI manager (Frame)
3269        """
3270        self.frame.set_manager(manager)
3271       
3272    def build_gui(self):
3273        """
3274        Build the GUI
3275        """
3276        #try to load file at the start
3277        try:
3278            self.open_file()
3279        except:
3280            raise
3281        self.frame.build_gui()
3282       
3283    def set_welcome_panel(self, panel_class):
3284        """
3285        Set the welcome panel
3286       
3287        :param panel_class: class of the welcome panel to be instantiated
3288       
3289        """
3290        self.frame.set_welcome_panel(panel_class)
3291       
3292    def add_perspective(self, perspective):
3293        """
3294        Manually add a perspective to the application GUI
3295        """
3296        self.frame.add_perspective(perspective)
3297   
3298    def window_placement(self, size):
3299        """
3300        Determines the position and size of the application frame such that it
3301        fits on the user's screen without obstructing (or being obstructed by)
3302        the Windows task bar.  The maximum initial size in pixels is bounded by
3303        WIDTH x HEIGHT.  For most monitors, the application
3304        will be centered on the screen; for very large monitors it will be
3305        placed on the left side of the screen.
3306        """
3307        is_maximized = False
3308        # Get size of screen without
3309        for screenCount in range(wx.Display().GetCount()):
3310            screen = wx.Display(screenCount)
3311            if screen.IsPrimary():
3312                displayRect = screen.GetClientArea()
3313                break
3314       
3315        posX, posY, displayWidth, displayHeight = displayRect       
3316        customWidth, customHeight = size
3317       
3318        # If the custom size is default, set 90% of the screen size
3319        if customWidth <= 0 and customHeight <= 0:
3320            if customWidth == 0 and customHeight == 0:
3321                is_maximized = True
3322            customWidth = displayWidth * 0.9
3323            customHeight = displayHeight * 0.9
3324        else:
3325            # If the custom screen is bigger than the
3326            # window screen than make maximum size
3327            if customWidth > displayWidth:
3328                customWidth = displayWidth
3329            if customHeight > displayHeight:
3330                customHeight = displayHeight
3331           
3332        # Note that when running Linux and using an Xming (X11) server on a PC
3333        # with a dual  monitor configuration, the reported display size may be
3334        # that of both monitors combined with an incorrect display count of 1.
3335        # To avoid displaying this app across both monitors, we check for
3336        # screen 'too big'.  If so, we assume a smaller width which means the
3337        # application will be placed towards the left hand side of the screen.
3338       
3339        # If dual screen registered as 1 screen. Make width half.
3340        # MAC just follows the default behavior of pos
3341        if IS_WIN:
3342            if displayWidth > (displayHeight*2):
3343                if (customWidth == displayWidth):
3344                    customWidth = displayWidth/2
3345                # and set the position to be the corner of the screen.
3346                posX = 0
3347                posY = 0
3348               
3349            # Make the position the middle of the screen. (Not 0,0)
3350            else:
3351                posX = (displayWidth - customWidth)/2
3352                posY = (displayHeight - customHeight)/2
3353        # Return the suggested position and size for the application frame.   
3354        return (posX, posY), (customWidth, customHeight), is_maximized
3355
3356   
3357    def display_splash_screen(self, parent, 
3358                              path=SPLASH_SCREEN_PATH):
3359        """Displays the splash screen.  It will exactly cover the main frame."""
3360       
3361        # Prepare the picture.  On a 2GHz intel cpu, this takes about a second.
3362        image = wx.Image(path, wx.BITMAP_TYPE_PNG)
3363        image.Rescale(SPLASH_SCREEN_WIDTH, 
3364                      SPLASH_SCREEN_HEIGHT, wx.IMAGE_QUALITY_HIGH)
3365        bm = image.ConvertToBitmap()
3366
3367        # Create and show the splash screen.  It will disappear only when the
3368        # program has entered the event loop AND either the timeout has expired
3369        # or the user has left clicked on the screen.  Thus any processing
3370        # performed in this routine (including sleeping) or processing in the
3371        # calling routine (including doing imports) will prevent the splash
3372        # screen from disappearing.
3373        #
3374        # Note that on Linux, the timeout appears to occur immediately in which
3375        # case the splash screen disappears upon entering the event loop.
3376        s_screen = wx.SplashScreen(bitmap=bm,
3377                         splashStyle=(wx.SPLASH_TIMEOUT|
3378                                              wx.SPLASH_CENTRE_ON_SCREEN),
3379                                 style=(wx.SIMPLE_BORDER|
3380                                        wx.FRAME_NO_TASKBAR|
3381                                        wx.FRAME_FLOAT_ON_PARENT),
3382                                       
3383                        milliseconds=SS_MAX_DISPLAY_TIME,
3384                        parent=parent,
3385                        id=wx.ID_ANY)
3386        from sans.guiframe.gui_statusbar import SPageStatusbar
3387        statusBar = SPageStatusbar(s_screen)
3388        s_screen.SetStatusBar(statusBar)
3389        s_screen.Bind(wx.EVT_CLOSE, self.on_close_splash_screen)
3390        s_screen.Show()
3391        return s_screen
3392       
3393       
3394    def on_close_splash_screen(self, event):
3395        """
3396        When the splash screen is closed.
3397        """
3398        self.frame.Show(True)
3399        event.Skip()
3400        self.maximize_win()
3401
3402
3403class MDIFrame(CHILD_FRAME):
3404    """
3405    Frame for panels
3406    """
3407    def __init__(self, parent, panel, title="Untitled",
3408                size=(300,200), *args, **kwds):
3409        """
3410        comment
3411        :param parent: parent panel/container
3412        """
3413        kwds['size']= size
3414        kwds['title']= title
3415        kwds['style'] = MDI_STYLE
3416        # Initialize the Frame object
3417        CHILD_FRAME.__init__(self, parent, *args, **kwds)
3418        self.parent = parent
3419        self.name = "Untitled"
3420        self.batch_on = self.parent.batch_on
3421        self.panel = panel
3422        if panel != None:
3423            self.set_panel(panel)
3424        self.Show(False)
3425   
3426    def show_data_panel(self, action):
3427        """
3428        """
3429        self.parent.show_data_panel(action)
3430       
3431    def set_panel(self, panel):
3432        """
3433        """
3434        self.panel = panel
3435        self.name = panel.window_name
3436        self.SetTitle(panel.window_caption)
3437        self.SetHelpText(panel.help_string)
3438        width, height = self.parent._get_panels_size(panel)
3439        if hasattr(panel, "CENTER_PANE") and panel.CENTER_PANE:
3440            width *= 0.6 
3441        self.SetSize((width, height))
3442        self.parent.put_icon(self)
3443        self.Bind(wx.EVT_SET_FOCUS, self.set_panel_focus)
3444        self.Bind(wx.EVT_CLOSE, self.OnClose)
3445        self.Show(False)
3446   
3447    def set_panel_focus(self, event):
3448        """
3449        """
3450        if self.parent.panel_on_focus != self.panel:
3451            self.panel.SetFocus()
3452            self.parent.panel_on_focus = self.panel
3453       
3454    def OnClose(self, event):
3455        """
3456        On Close event
3457        """
3458        self.panel.on_close(event)
3459       
3460if __name__ == "__main__": 
3461    app = ViewApp(0)
3462    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.