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

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 8833c77 was 8833c77, checked in by Jae Cho <jhjcho@…>, 11 years ago

fix error on closing gui

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