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

Last change on this file since fd44af0 was c8e1996, checked in by krzywon, 8 years ago

Fixes #738: No errors are thrown on loading projects with fits, plus linting.

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