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

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 16cd735 was 5ea34ec, checked in by ajj, 11 years ago

Fix to gui_manager to account for lack of CONTROL_HEIGHT and CONTROL_WIDTH in old sasview config files that prevented startup.

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