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

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.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since e6de6b8 was e6de6b8, checked in by krzywon, 8 years ago

Delinting and small structural changes.

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