source: sasview/src/sas/guiframe/gui_manager.py @ 9989a6a

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 9989a6a was 9989a6a, checked in by Doucet, Mathieu <doucetm@…>, 9 years ago

Move latest version file to a json format

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