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

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

Moving things around. Will definitely not build.

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