source: sasview/src/sas/guiframe/gui_manager.py @ 069aae2

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

Removes traceback logging from check_updates - single line of "cannot
connect to sasview.org" seems sufficient here given the number of times
this is likely to happen if behind a firewall.

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