source: sasview/src/sas/guiframe/gui_manager.py @ aceae8c

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

cleanup of documentation call

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