source: sasview/src/sas/guiframe/gui_manager.py @ 3db44fb

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

1) Fixed second issue that was caused by the recent cleanup of
DocumentationWindow?: loading html at anchor point for context help
(broken). In order to preserve the cleanup, the class was refactored to
take another parameter: html instruction string. This keeps it general
to accept not only the # anchor but alos queries of all sorts in the
future. Thus all modules using this class were also edited to match.

2) in process of editing the dozen or so instances did a bit of code
cleanup and pylint cleanup.

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