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

Last change on this file since b39debba was b39debba, checked in by wojciech, 7 years ago

Merged with master

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