source: sasview/src/sas/guiframe/gui_manager.py @ 70481a5

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 70481a5 was 70481a5, checked in by butler, 9 years ago

Add logger messages to check connection to GitHub?

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