source: sasview/src/sas/sasgui/guiframe/gui_manager.py @ c9dc8c9

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 c9dc8c9 was c8a641e8, checked in by krzywon, 9 years ago

Resolve #471 - Remove the "Welcome" option from the help menu.

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