source: sasview/src/sas/guiframe/gui_manager.py @ 957af0d

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 957af0d was 957af0d, checked in by Ricardo M. Ferraz Leal <ricleal@…>, 9 years ago

Proxy class in debug mode to test

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