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

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

minor fixes in focusing graph name in combobox:Data Panel

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