source: sasview/sansguiframe/src/sans/guiframe/gui_manager.py @ aa07420

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

put back sys.path[0]

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