source: sasview/src/sas/guiframe/gui_manager.py @ 22d92da

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

fix toctree to remove model help link from top level
fix guiframe so that help→documentation points directly to user docs

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