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

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

Fix installation and make sure SasView? starts

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