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

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

Re #216 Fix toolbar issue.

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