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

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

Writting to configuration file at the end of session

  • Property mode set to 100644
File size: 129.3 KB
Line 
1"""
2    Gui manager: manages the widgets making up an application
3"""
4################################################################################
5# This software was developed by the University of Tennessee as part of the
6# Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
7# project funded by the US National Science Foundation.
8#
9# See the license text in license.txtz
10#
11# copyright 2008, University of Tennessee
12################################################################################
13
14
15import wx
16import wx.aui
17import os
18import sys
19import time
20import imp
21import warnings
22import re
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        #IF SAS_OPENCL is set, settings are stored in the custom config file
2112        if "SAS_OPENCL" in os.environ:
2113            self._write_opencl_config_file()
2114        logging.info(" --- SasView session was closed --- \n")
2115        wx.Exit()
2116        sys.exit()
2117
2118    def _write_opencl_config_file(self):
2119        #from sas.sasgui.guiframe.customdir import SetupCustom
2120        #from sas.sasgui.guiframe.gui_manager import _find_local_config
2121
2122        sas_opencl = os.environ.get("SAS_OPENCL","")
2123        #How to store it in file
2124        new_config_lines = []
2125        config_file = open(custom_config.__file__)
2126        if custom_config is not None:
2127            config_lines = config_file.readlines()
2128            for line in config_lines:
2129                if "SAS_OPENCL" in line:
2130                    if sas_opencl:
2131                        new_config_lines.append("SAS_OPENCL = \""+sas_opencl+"\"")
2132                    else:
2133                        new_config_lines.append("SAS_OPENCL = None")
2134                else:
2135                    new_config_lines.append(line)
2136        config_file.close()
2137        new_config_file = open(custom_config.__file__,"w")
2138        new_config_file.writelines(new_config_lines)
2139        new_config_file.close()
2140
2141
2142    def _check_update(self, event=None):
2143        """
2144        Check with the deployment server whether a new version
2145        of the application is available.
2146        A thread is started for the connecting with the server. The thread calls
2147        a call-back method when the current version number has been obtained.
2148        """
2149        version_info = {"version": "0.0.0"}
2150        c = Connection(config.__update_URL__, config.UPDATE_TIMEOUT)
2151        response = c.connect()
2152        if response is not None:
2153            try:
2154                #
2155                content = response.read().strip()
2156                logging.info("Connected to www.sasview.org. Latest version: %s"
2157                             % (content))
2158                version_info = json.loads(content)
2159            except:
2160                logging.info("Failed to connect to www.sasview.org")
2161        self._process_version(version_info, standalone=event is None)
2162
2163    def _process_version(self, version_info, standalone=True):
2164        """
2165        Call-back method for the process of checking for updates.
2166        This methods is called by a VersionThread object once the current
2167        version number has been obtained. If the check is being done in the
2168        background, the user will not be notified unless there's an update.
2169
2170        :param version: version string
2171        :param standalone: True of the update is being checked in
2172           the background, False otherwise.
2173
2174        """
2175        try:
2176            version = version_info["version"]
2177            if version == "0.0.0":
2178                msg = "Could not connect to the application server."
2179                msg += " Please try again later."
2180                self.SetStatusText(msg)
2181            elif cmp(version, config.__version__) > 0:
2182                msg = "Version %s is available! " % str(version)
2183                if not standalone:
2184                    import webbrowser
2185                    if "download_url" in version_info:
2186                        webbrowser.open(version_info["download_url"])
2187                    else:
2188                        webbrowser.open(config.__download_page__)
2189                else:
2190                    msg += "See the help menu to download it."
2191                self.SetStatusText(msg)
2192            else:
2193                if not standalone:
2194                    msg = "You have the latest version"
2195                    msg += " of %s" % str(config.__appname__)
2196                    self.SetStatusText(msg)
2197        except:
2198            msg = "guiframe: could not get latest application"
2199            msg += " version number\n  %s" % sys.exc_value
2200            logging.error(msg)
2201            if not standalone:
2202                msg = "Could not connect to the application server."
2203                msg += " Please try again later."
2204                self.SetStatusText(msg)
2205
2206    def _onAcknowledge(self, evt):
2207        """
2208        Pop up the acknowledge dialog
2209
2210        :param evt: menu event
2211
2212        """
2213        if config._do_acknowledge:
2214            import sas.sasgui.guiframe.acknowledgebox as AcknowledgeBox
2215            dialog = AcknowledgeBox.DialogAcknowledge(None, -1, "")
2216            dialog.ShowModal()
2217
2218    def _onAbout(self, evt):
2219        """
2220        Pop up the about dialog
2221
2222        :param evt: menu event
2223
2224        """
2225        if config._do_aboutbox:
2226            import sas.sasgui.guiframe.aboutbox as AboutBox
2227            dialog = AboutBox.DialogAbout(None, -1, "")
2228            dialog.ShowModal()
2229
2230    def _onTutorial(self, evt):
2231        """
2232        Pop up the tutorial dialog
2233
2234        :param evt: menu event
2235
2236        """
2237        if config._do_tutorial:
2238            path = config.TUTORIAL_PATH
2239            if IS_WIN:
2240                try:
2241                    from sas.sasgui.guiframe.pdfview import PDFFrame
2242                    dialog = PDFFrame(None, -1, "Tutorial", path)
2243                    # put icon
2244                    self.put_icon(dialog)
2245                    dialog.Show(True)
2246                except:
2247                    logging.error("Error in _onTutorial: %s" % sys.exc_value)
2248                    try:
2249                        # Try an alternate method
2250                        logging.error(
2251                            "Could not open the tutorial pdf, trying xhtml2pdf")
2252                        from xhtml2pdf import pisa
2253                        pisa.startViewer(path)
2254                    except:
2255                        logging.error(
2256                            "Could not open the tutorial pdf with xhtml2pdf")
2257                        msg = "This feature requires 'PDF Viewer'\n"
2258                        wx.MessageBox(msg, 'Error')
2259            else:
2260                try:
2261                    command = "open '%s'" % path
2262                    os.system(command)
2263                except:
2264                    try:
2265                        # Try an alternate method
2266                        logging.error(
2267                            "Could not open the tutorial pdf, trying xhtml2pdf")
2268                        from xhtml2pdf import pisa
2269                        pisa.startViewer(path)
2270                    except:
2271                        logging.error(
2272                            "Could not open the tutorial pdf with xhtml2pdf")
2273                        msg = "This feature requires the Preview application\n"
2274                        wx.MessageBox(msg, 'Error')
2275
2276    def _onSphinxDocs(self, evt):
2277        """
2278        Bring up Sphinx Documentation at top level whenever the menu item
2279        'documentation' is clicked. Calls DocumentationWindow with the top
2280        level path of "index.html"
2281
2282        :param evt: menu event
2283        """
2284        # Running SasView "in-place" using run.py means the docs will be in a
2285        # different place than they would otherwise.
2286        from documentation_window import DocumentationWindow
2287        _TreeLocation = "user/user.html"
2288        DocumentationWindow(self, -1, _TreeLocation, "",
2289                            "SasView Documentation")
2290
2291    def set_manager(self, manager):
2292        """
2293        Sets the application manager for this frame
2294
2295        :param manager: frame manager
2296        """
2297        self.app_manager = manager
2298
2299    def post_init(self):
2300        """
2301        This initialization method is called after the GUI
2302        has been created and all plug-ins loaded. It calls
2303        the post_init() method of each plug-in (if it exists)
2304        so that final initialization can be done.
2305        """
2306        for item in self.plugins:
2307            if hasattr(item, "post_init"):
2308                item.post_init()
2309
2310    def set_perspective(self, panels):
2311        """
2312        Sets the perspective of the GUI.
2313        Opens all the panels in the list, and closes
2314        all the others.
2315
2316        :param panels: list of panels
2317        """
2318        for item in self.panels.keys():
2319            # Check whether this is a sticky panel
2320            if hasattr(self.panels[item], "ALWAYS_ON"):
2321                if self.panels[item].ALWAYS_ON:
2322                    continue
2323            if self.panels[item] is None:
2324                continue
2325            if self.panels[item].window_name in panels:
2326                frame = self.panels[item].get_frame()
2327                if not frame.IsShown():
2328                    frame.Show(True)
2329            else:
2330                # always show the data panel if enable
2331                style = self.__gui_style & GUIFRAME.MANAGER_ON
2332                if (style == GUIFRAME.MANAGER_ON) \
2333                        and self.panels[item] == self._data_panel:
2334                    if 'data_panel' in self.panels.keys():
2335                        frame = self.panels['data_panel'].get_frame()
2336                        if frame is None:
2337                            continue
2338                        flag = frame.IsShown()
2339                        frame.Show(flag)
2340                else:
2341                    frame = self.panels[item].get_frame()
2342                    if frame is None:
2343                        continue
2344
2345                    if frame.IsShown():
2346                        frame.Show(False)
2347
2348    def show_data_panel(self, event=None, action=True):
2349        """
2350        show the data panel
2351        """
2352        if self._data_panel_menu is None:
2353            return
2354        label = self._data_panel_menu.GetText()
2355        pane = self.panels["data_panel"]
2356        frame = pane.get_frame()
2357        if label == 'Show Data Explorer':
2358            if action:
2359                frame.Show(True)
2360            self.__gui_style = self.__gui_style | GUIFRAME.MANAGER_ON
2361            self._data_panel_menu.SetText('Hide Data Explorer')
2362        else:
2363            if action:
2364                frame.Show(False)
2365            self.__gui_style = self.__gui_style & (~GUIFRAME.MANAGER_ON)
2366            self._data_panel_menu.SetText('Show Data Explorer')
2367
2368    def add_data_helper(self, data_list):
2369        """
2370        """
2371        if self._data_manager is not None:
2372            self._data_manager.add_data(data_list)
2373
2374    def add_data(self, data_list):
2375        """
2376        receive a dictionary of data from loader
2377        store them its data manager if possible
2378        send to data the current active perspective if the data panel
2379        is not active.
2380        :param data_list: dictionary of data's ID and value Data
2381        """
2382        # Store data into manager
2383        self.add_data_helper(data_list)
2384        # set data in the data panel
2385        if self._data_panel is not None:
2386            data_state = self._data_manager.get_data_state(data_list.keys())
2387            self._data_panel.load_data_list(data_state)
2388        # if the data panel is shown wait for the user to press a button
2389        # to send data to the current perspective. if the panel is not
2390        # show  automatically send the data to the current perspective
2391        style = self.__gui_style & GUIFRAME.MANAGER_ON
2392        if style == GUIFRAME.MANAGER_ON:
2393            # wait for button press from the data panel to set_data
2394            if self._data_panel is not None:
2395                self._data_panel.frame.Show(True)
2396        else:
2397            # automatically send that to the current perspective
2398            self.set_data(data_id=data_list.keys())
2399
2400    def set_data(self, data_id, theory_id=None):
2401        """
2402        set data to current perspective
2403        """
2404        list_data, _ = self._data_manager.get_by_id(data_id)
2405        if self._current_perspective is not None:
2406            self._current_perspective.set_data(list_data.values())
2407
2408        else:
2409            msg = "Guiframe does not have a current perspective"
2410            logging.info(msg)
2411
2412    def set_theory(self, state_id, theory_id=None):
2413        """
2414        """
2415        _, list_theory = self._data_manager.get_by_id(theory_id)
2416        if self._current_perspective is not None:
2417            try:
2418                self._current_perspective.set_theory(list_theory.values())
2419            except:
2420                msg = "Guiframe set_theory: \n" + str(sys.exc_value)
2421                logging.info(msg)
2422                wx.PostEvent(self, StatusEvent(status=msg, info="error"))
2423        else:
2424            msg = "Guiframe does not have a current perspective"
2425            logging.info(msg)
2426
2427    def plot_data(self, state_id, data_id=None,
2428                  theory_id=None, append=False):
2429        """
2430        send a list of data to plot
2431        """
2432        data_list, _ = self._data_manager.get_by_id(data_id)
2433        _, temp_list_theory = self._data_manager.get_by_id(theory_id)
2434        total_plot_list = data_list.values()
2435        for item in temp_list_theory.values():
2436            theory_data, theory_state = item
2437            total_plot_list.append(theory_data)
2438        GROUP_ID = wx.NewId()
2439        for new_plot in total_plot_list:
2440            if append:
2441                if self.panel_on_focus is None:
2442                    message = "cannot append plot. No plot panel on focus!"
2443                    message += "please click on any available plot to set focus"
2444                    wx.PostEvent(self, StatusEvent(status=message,
2445                                                   info='warning'))
2446                    return
2447                else:
2448                    if self.enable_add_data(new_plot):
2449                        new_plot.group_id = self.panel_on_focus.group_id
2450                    else:
2451                        message = "Only 1D Data can be append to"
2452                        message += " plot panel containing 1D data.\n"
2453                        message += "%s not be appended.\n" % str(new_plot.name)
2454                        message += "try new plot option.\n"
2455                        wx.PostEvent(self, StatusEvent(status=message,
2456                                                       info='warning'))
2457            else:
2458                # if not append then new plot
2459                from sas.sasgui.guiframe.dataFitting import Data2D
2460                if issubclass(Data2D, new_plot.__class__):
2461                    # for 2 D always plot in a separated new plot
2462                    new_plot.group_id = wx.NewId()
2463                else:
2464                    # plot all 1D in a new plot
2465                    new_plot.group_id = GROUP_ID
2466            title = "PLOT " + str(new_plot.title)
2467            wx.PostEvent(self, NewPlotEvent(plot=new_plot,
2468                                            title=title,
2469                                            group_id=new_plot.group_id))
2470
2471    def remove_data(self, data_id, theory_id=None):
2472        """
2473        Delete data state if data_id is provide
2474        delete theory created with data of id data_id if theory_id is provide
2475        if delete all true: delete the all state
2476        else delete theory
2477        """
2478        temp = data_id + theory_id
2479        for plug in self.plugins:
2480            plug.delete_data(temp)
2481        data_list, _ = self._data_manager.get_by_id(data_id)
2482        _, temp_list_theory = self._data_manager.get_by_id(theory_id)
2483        total_plot_list = data_list.values()
2484        for item in temp_list_theory.values():
2485            theory_data, theory_state = item
2486            total_plot_list.append(theory_data)
2487        for new_plot in total_plot_list:
2488            for group_id in new_plot.list_group_id:
2489                wx.PostEvent(self, NewPlotEvent(id=new_plot.id,
2490                                                group_id=group_id,
2491                                                action='remove'))
2492                wx.CallAfter(self._remove_res_plot, new_plot.id)
2493        self._data_manager.delete_data(data_id=data_id,
2494                                       theory_id=theory_id)
2495
2496    def _remove_res_plot(self, id):
2497        """
2498        Try to remove corresponding res plot
2499
2500        : param id: id of the data
2501        """
2502        try:
2503            wx.PostEvent(self, NewPlotEvent(id=("res" + str(id)),
2504                                            group_id=("res" + str(id)),
2505                                            action='remove'))
2506        except:
2507            logging.error(sys.exc_value)
2508
2509    def save_data1d(self, data, fname):
2510        """
2511        Save data dialog
2512        """
2513        default_name = fname
2514        wildcard = "Text files (*.txt)|*.txt|"\
2515                    "CanSAS 1D files(*.xml)|*.xml"
2516        path = None
2517        dlg = wx.FileDialog(self, "Choose a file",
2518                            self._default_save_location,
2519                            default_name, wildcard, wx.SAVE)
2520
2521        if dlg.ShowModal() == wx.ID_OK:
2522            path = dlg.GetPath()
2523            # ext_num = 0 for .txt, ext_num = 1 for .xml
2524            # This is MAC Fix
2525            ext_num = dlg.GetFilterIndex()
2526            if ext_num == 0:
2527                ext_format = '.txt'
2528            else:
2529                ext_format = '.xml'
2530            path = os.path.splitext(path)[0] + ext_format
2531            mypath = os.path.basename(path)
2532
2533            # Instantiate a loader
2534            loader = Loader()
2535            ext_format = ".txt"
2536            if os.path.splitext(mypath)[1].lower() == ext_format:
2537                # Make sure the ext included in the file name
2538                # especially on MAC
2539                fName = os.path.splitext(path)[0] + ext_format
2540                self._onsaveTXT(data, fName)
2541            ext_format = ".xml"
2542            if os.path.splitext(mypath)[1].lower() == ext_format:
2543                # Make sure the ext included in the file name
2544                # especially on MAC
2545                fName = os.path.splitext(path)[0] + ext_format
2546                loader.save(fName, data, ext_format)
2547            try:
2548                self._default_save_location = os.path.dirname(path)
2549            except:
2550                pass
2551        dlg.Destroy()
2552
2553    def _onsaveTXT(self, data, path):
2554        """
2555        Save file as txt
2556
2557        .. todo:: Refactor and remove this method. See 'TODO' in _onSave.
2558        """
2559        if path is not None:
2560            out = open(path, 'w')
2561            has_errors = True
2562            if data.dy is None or data.dy == []:
2563                has_errors = False
2564            # Sanity check
2565            if has_errors:
2566                try:
2567                    if len(data.y) != len(data.dy):
2568                        has_errors = False
2569                except:
2570                    has_errors = False
2571            if has_errors:
2572                if data.dx is not None and data.dx != []:
2573                    out.write("<X>   <Y>   <dY>   <dX>\n")
2574                else:
2575                    out.write("<X>   <Y>   <dY>\n")
2576            else:
2577                out.write("<X>   <Y>\n")
2578
2579            for i in range(len(data.x)):
2580                if has_errors:
2581                    if data.dx is not None and data.dx != []:
2582                        if data.dx[i] is not None:
2583                            out.write("%g  %g  %g  %g\n" % (data.x[i],
2584                                                            data.y[i],
2585                                                            data.dy[i],
2586                                                            data.dx[i]))
2587                        else:
2588                            out.write("%g  %g  %g\n" % (data.x[i],
2589                                                        data.y[i],
2590                                                        data.dy[i]))
2591                    else:
2592                        out.write("%g  %g  %g\n" % (data.x[i],
2593                                                    data.y[i],
2594                                                    data.dy[i]))
2595                else:
2596                    out.write("%g  %g\n" % (data.x[i],
2597                                            data.y[i]))
2598            out.close()
2599
2600    def show_data1d(self, data, name):
2601        """
2602        Show data dialog
2603        """
2604        try:
2605            xmin = min(data.x)
2606            ymin = min(data.y)
2607        except:
2608            msg = "Unable to find min/max of \n data named %s" % \
2609                        data.filename
2610            wx.PostEvent(self, StatusEvent(status=msg,
2611                                           info="error"))
2612            raise ValueError, msg
2613        # text = str(data)
2614        text = data.__str__()
2615        text += 'Data Min Max:\n'
2616        text += 'X_min = %s:  X_max = %s\n' % (xmin, max(data.x))
2617        text += 'Y_min = %s:  Y_max = %s\n' % (ymin, max(data.y))
2618        if data.dy is not None:
2619            text += 'dY_min = %s:  dY_max = %s\n' % (min(data.dy), max(data.dy))
2620        text += '\nData Points:\n'
2621        x_st = "X"
2622        for index in range(len(data.x)):
2623            if data.dy is not None and len(data.dy) > index:
2624                dy_val = data.dy[index]
2625            else:
2626                dy_val = 0.0
2627            if data.dx is not None and len(data.dx) > index:
2628                dx_val = data.dx[index]
2629            else:
2630                dx_val = 0.0
2631            if data.dxl is not None and len(data.dxl) > index:
2632                if index == 0:
2633                    x_st = "Xl"
2634                dx_val = data.dxl[index]
2635            elif data.dxw is not None and len(data.dxw) > index:
2636                if index == 0:
2637                    x_st = "Xw"
2638                dx_val = data.dxw[index]
2639
2640            if index == 0:
2641                text += "<index> \t<X> \t<Y> \t<dY> \t<d%s>\n" % x_st
2642            text += "%s \t%s \t%s \t%s \t%s\n" % (index,
2643                                                  data.x[index],
2644                                                  data.y[index],
2645                                                  dy_val,
2646                                                  dx_val)
2647        from pdfview import TextFrame
2648        frame = TextFrame(None, -1, "Data Info: %s" % data.name, text)
2649        # put icon
2650        self.put_icon(frame)
2651        frame.Show(True)
2652
2653    def save_data2d(self, data, fname):
2654        """
2655        Save data2d dialog
2656        """
2657        default_name = fname
2658        wildcard = "IGOR/DAT 2D file in Q_map (*.dat)|*.DAT"
2659        dlg = wx.FileDialog(self, "Choose a file",
2660                            self._default_save_location,
2661                            default_name, wildcard, wx.SAVE)
2662
2663        if dlg.ShowModal() == wx.ID_OK:
2664            path = dlg.GetPath()
2665            # ext_num = 0 for .txt, ext_num = 1 for .xml
2666            # This is MAC Fix
2667            ext_num = dlg.GetFilterIndex()
2668            if ext_num == 0:
2669                ext_format = '.dat'
2670            else:
2671                ext_format = ''
2672            path = os.path.splitext(path)[0] + ext_format
2673            mypath = os.path.basename(path)
2674
2675            # Instantiate a loader
2676            loader = Loader()
2677
2678            ext_format = ".dat"
2679            if os.path.splitext(mypath)[1].lower() == ext_format:
2680                # Make sure the ext included in the file name
2681                # especially on MAC
2682                fileName = os.path.splitext(path)[0] + ext_format
2683                loader.save(fileName, data, ext_format)
2684            try:
2685                self._default_save_location = os.path.dirname(path)
2686            except:
2687                pass
2688        dlg.Destroy()
2689
2690    def show_data2d(self, data, name):
2691        """
2692        Show data dialog
2693        """
2694        wx.PostEvent(self, StatusEvent(status="Gathering Data2D Info.",
2695                                       type='start'))
2696        text = data.__str__()
2697        text += 'Data Min Max:\n'
2698        text += 'I_min = %s\n' % min(data.data)
2699        text += 'I_max = %s\n\n' % max(data.data)
2700        text += 'Data (First 2501) Points:\n'
2701        text += 'Data columns include err(I).\n'
2702        text += 'ASCII data starts here.\n'
2703        text += "<index> \t<Qx> \t<Qy> \t<I> \t<dI> \t<dQparal> \t<dQperp>\n"
2704        di_val = 0.0
2705        dx_val = 0.0
2706        dy_val = 0.0
2707        len_data = len(data.qx_data)
2708        for index in xrange(0, len_data):
2709            x_val = data.qx_data[index]
2710            y_val = data.qy_data[index]
2711            i_val = data.data[index]
2712            if data.err_data is not None:
2713                di_val = data.err_data[index]
2714            if data.dqx_data is not None:
2715                dx_val = data.dqx_data[index]
2716            if data.dqy_data is not None:
2717                dy_val = data.dqy_data[index]
2718
2719            text += "%s \t%s \t%s \t%s \t%s \t%s \t%s\n" % (index,
2720                                                            x_val,
2721                                                            y_val,
2722                                                            i_val,
2723                                                            di_val,
2724                                                            dx_val,
2725                                                            dy_val)
2726            # Takes too long time for typical data2d: Break here
2727            if index >= 2500:
2728                text += ".............\n"
2729                break
2730
2731        from pdfview import TextFrame
2732        frame = TextFrame(None, -1, "Data Info: %s" % data.name, text)
2733        # put icon
2734        self.put_icon(frame)
2735        frame.Show(True)
2736        wx.PostEvent(self, StatusEvent(status="Data2D Info Displayed",
2737                                       type='stop'))
2738
2739    def set_current_perspective(self, perspective):
2740        """
2741        set the current active perspective
2742        """
2743        self._current_perspective = perspective
2744        name = "No current analysis selected"
2745        if self._current_perspective is not None:
2746            self._add_current_plugin_menu()
2747            for panel in self.panels.values():
2748                if hasattr(panel, 'CENTER_PANE') and panel.CENTER_PANE:
2749                    for name in self._current_perspective.get_perspective():
2750                        frame = panel.get_frame()
2751                        if frame is not None:
2752                            if name == panel.window_name:
2753                                panel.on_set_focus(event=None)
2754                                frame.Show(True)
2755                            else:
2756                                frame.Show(False)
2757            name = self._current_perspective.sub_menu
2758            if self._data_panel is not None:
2759                self._data_panel.set_active_perspective(name)
2760                self._check_applications_menu()
2761            # Set the SasView title
2762            self._set_title_name(name)
2763
2764    def _set_title_name(self, name):
2765        """
2766        Set the SasView title w/ the current application name
2767
2768        : param name: application name [string]
2769        """
2770        # Set SanView Window title w/ application anme
2771        title = self.title + "  - " + name + " -"
2772        self.SetTitle(title)
2773
2774    def _check_applications_menu(self):
2775        """
2776        check the menu of the current application
2777        """
2778        if self._applications_menu is not None:
2779            for menu in self._applications_menu.GetMenuItems():
2780                if self._current_perspective is not None:
2781                    name = self._current_perspective.sub_menu
2782                    if menu.IsCheckable():
2783                        if menu.GetLabel() == name:
2784                            menu.Check(True)
2785                        else:
2786                            menu.Check(False)
2787
2788    def enable_add_data(self, new_plot):
2789        """
2790        Enable append data on a plot panel
2791        """
2792
2793        if self.panel_on_focus \
2794                not in self._plotting_plugin.plot_panels.values():
2795            return
2796        check = "Theory1D"
2797        is_theory = len(self.panel_on_focus.plots) <= 1 and \
2798            self.panel_on_focus.plots.values()[0].__class__.__name__ == check
2799
2800        is_data2d = hasattr(new_plot, 'data')
2801
2802        is_data1d = self.panel_on_focus.__class__.__name__ == "ModelPanel1D"\
2803            and self.panel_on_focus.group_id is not None
2804        has_meta_data = hasattr(new_plot, 'meta_data')
2805
2806        # disable_add_data if the data is being recovered from  a saved state
2807        is_state_data = False
2808        if has_meta_data:
2809            if 'invstate' in new_plot.meta_data:
2810                is_state_data = True
2811            if 'prstate' in new_plot.meta_data:
2812                is_state_data = True
2813            if 'fitstate' in new_plot.meta_data:
2814                is_state_data = True
2815
2816        return is_data1d and not is_data2d and not is_theory \
2817               and not is_state_data
2818
2819    def check_multimode(self, perspective=None):
2820        """
2821        Check the perspective have batch mode capablitity
2822        """
2823        if perspective is None or self._data_panel is None:
2824            return
2825        flag = perspective.get_batch_capable()
2826        flag_on = perspective.batch_on
2827        if flag:
2828            self._data_panel.rb_single_mode.SetValue(not flag_on)
2829            self._data_panel.rb_batch_mode.SetValue(flag_on)
2830        else:
2831            self._data_panel.rb_single_mode.SetValue(True)
2832            self._data_panel.rb_batch_mode.SetValue(False)
2833        self._data_panel.rb_single_mode.Enable(flag)
2834        self._data_panel.rb_batch_mode.Enable(flag)
2835
2836    def enable_edit_menu(self):
2837        """
2838        enable menu item under edit menu depending on the panel on focus
2839        """
2840        if self.cpanel_on_focus is not None and self._edit_menu is not None:
2841            flag = self.cpanel_on_focus.get_undo_flag()
2842            self._edit_menu.Enable(GUIFRAME_ID.UNDO_ID, flag)
2843            flag = self.cpanel_on_focus.get_redo_flag()
2844            self._edit_menu.Enable(GUIFRAME_ID.REDO_ID, flag)
2845            flag = self.cpanel_on_focus.get_copy_flag()
2846            self._edit_menu.Enable(GUIFRAME_ID.COPY_ID, flag)
2847            flag = self.cpanel_on_focus.get_paste_flag()
2848            self._edit_menu.Enable(GUIFRAME_ID.PASTE_ID, flag)
2849
2850            # Copy menu
2851            flag = self.cpanel_on_focus.get_copy_flag()
2852            self._edit_menu_copyas.Enable(GUIFRAME_ID.COPYEX_ID, flag)
2853            self._edit_menu_copyas.Enable(GUIFRAME_ID.COPYLAT_ID, flag)
2854
2855            flag = self.cpanel_on_focus.get_preview_flag()
2856            self._edit_menu.Enable(GUIFRAME_ID.PREVIEW_ID, flag)
2857            flag = self.cpanel_on_focus.get_reset_flag()
2858            self._edit_menu.Enable(GUIFRAME_ID.RESET_ID, flag)
2859        else:
2860            flag = False
2861            self._edit_menu.Enable(GUIFRAME_ID.UNDO_ID, flag)
2862            self._edit_menu.Enable(GUIFRAME_ID.REDO_ID, flag)
2863            self._edit_menu.Enable(GUIFRAME_ID.COPY_ID, flag)
2864            self._edit_menu.Enable(GUIFRAME_ID.PASTE_ID, flag)
2865            self._edit_menu.Enable(GUIFRAME_ID.PREVIEW_ID, flag)
2866            self._edit_menu.Enable(GUIFRAME_ID.RESET_ID, flag)
2867
2868    def on_undo_panel(self, event=None):
2869        """
2870        undo previous action of the last panel on focus if possible
2871        """
2872        if self.cpanel_on_focus is not None:
2873            self.cpanel_on_focus.on_undo(event)
2874
2875    def on_redo_panel(self, event=None):
2876        """
2877        redo the last cancel action done on the last panel on focus
2878        """
2879        if self.cpanel_on_focus is not None:
2880            self.cpanel_on_focus.on_redo(event)
2881
2882    def on_copy_panel(self, event=None):
2883        """
2884        copy the last panel on focus if possible
2885        """
2886        if self.cpanel_on_focus is not None:
2887            self.cpanel_on_focus.on_copy(event)
2888
2889    def on_paste_panel(self, event=None):
2890        """
2891        paste clipboard to the last panel on focus
2892        """
2893        if self.cpanel_on_focus is not None:
2894            self.cpanel_on_focus.on_paste(event)
2895
2896    def on_bookmark_panel(self, event=None):
2897        """
2898        bookmark panel
2899        """
2900        if self.cpanel_on_focus is not None:
2901            self.cpanel_on_focus.on_bookmark(event)
2902
2903    def append_bookmark(self, event=None):
2904        """
2905        Bookmark available information of the panel on focus
2906        """
2907        self._toolbar.append_bookmark(event)
2908
2909    def on_save_panel(self, event=None):
2910        """
2911        save possible information on the current panel
2912        """
2913        if self.cpanel_on_focus is not None:
2914            self.cpanel_on_focus.on_save(event)
2915
2916    def on_preview_panel(self, event=None):
2917        """
2918        preview information on the panel on focus
2919        """
2920        if self.cpanel_on_focus is not None:
2921            self.cpanel_on_focus.on_preview(event)
2922
2923    def on_print_panel(self, event=None):
2924        """
2925        print available information on the last panel on focus
2926        """
2927        if self.cpanel_on_focus is not None:
2928            self.cpanel_on_focus.on_print(event)
2929
2930    def on_zoom_panel(self, event=None):
2931        """
2932        zoom on the current panel if possible
2933        """
2934        if self.cpanel_on_focus is not None:
2935            self.cpanel_on_focus.on_zoom(event)
2936
2937    def on_zoom_in_panel(self, event=None):
2938        """
2939        zoom in of the panel on focus
2940        """
2941        if self.cpanel_on_focus is not None:
2942            self.cpanel_on_focus.on_zoom_in(event)
2943
2944    def on_zoom_out_panel(self, event=None):
2945        """
2946        zoom out on the panel on focus
2947        """
2948        if self.cpanel_on_focus is not None:
2949            self.cpanel_on_focus.on_zoom_out(event)
2950
2951    def on_drag_panel(self, event=None):
2952        """
2953        drag apply to the panel on focus
2954        """
2955        if self.cpanel_on_focus is not None:
2956            self.cpanel_on_focus.on_drag(event)
2957
2958    def on_reset_panel(self, event=None):
2959        """
2960        reset the current panel
2961        """
2962        if self.cpanel_on_focus is not None:
2963            self.cpanel_on_focus.on_reset(event)
2964
2965    def on_change_caption(self, name, old_caption, new_caption):
2966        """
2967        Change the panel caption
2968
2969        :param name: window_name of the pane
2970        :param old_caption: current caption [string]
2971        :param new_caption: new caption [string]
2972        """
2973        # wx.aui.AuiPaneInfo
2974        pane_info = self.get_paneinfo(old_caption)
2975        # update the data_panel.cb_plotpanel
2976        if 'data_panel' in self.panels.keys():
2977            # remove from data_panel combobox
2978            data_panel = self.panels["data_panel"]
2979            if data_panel.cb_plotpanel is not None:
2980                # Check if any panel has the same caption
2981                has_newstring = data_panel.cb_plotpanel.FindString(
2982                    str(new_caption))
2983                caption = new_caption
2984                if has_newstring != wx.NOT_FOUND:
2985                    captions = self._get_plotpanel_captions()
2986                    # Append nummber
2987                    inc = 1
2988                    # FIXME: fix this terrible loop
2989                    while (1):
2990                        caption = new_caption + '_%s' % str(inc)
2991                        if caption not in captions:
2992                            break
2993                        inc += 1
2994                    # notify to users
2995                    msg = "Found Same Title: Added '_%s'" % str(inc)
2996                    wx.PostEvent(self, StatusEvent(status=msg))
2997                # update data_panel cb
2998                pos = data_panel.cb_plotpanel.FindString(str(old_caption))
2999                if pos != wx.NOT_FOUND:
3000                    data_panel.cb_plotpanel.SetString(pos, caption)
3001                    data_panel.cb_plotpanel.SetStringSelection(caption)
3002        # New Caption
3003        pane_info.SetTitle(caption)
3004        return caption
3005
3006    def get_paneinfo(self, name):
3007        """
3008        Get pane Caption from window_name
3009
3010        :param name: window_name in AuiPaneInfo
3011        :returns: AuiPaneInfo of the name
3012        """
3013        for panel in self.plot_panels.values():
3014            if panel.frame.GetTitle() == name:
3015                return panel.frame
3016        return None
3017
3018    def enable_undo(self):
3019        """
3020        enable undo related control
3021        """
3022        if self.cpanel_on_focus is not None:
3023            self._toolbar.enable_undo(self.cpanel_on_focus)
3024
3025    def enable_redo(self):
3026        """
3027        enable redo
3028        """
3029        if self.cpanel_on_focus is not None:
3030            self._toolbar.enable_redo(self.cpanel_on_focus)
3031
3032    def enable_copy(self):
3033        """
3034        enable copy related control
3035        """
3036        if self.cpanel_on_focus is not None:
3037            self._toolbar.enable_copy(self.cpanel_on_focus)
3038
3039    def enable_paste(self):
3040        """
3041        enable paste
3042        """
3043        if self.cpanel_on_focus is not None:
3044            self._toolbar.enable_paste(self.cpanel_on_focus)
3045
3046    def enable_bookmark(self):
3047        """
3048        Bookmark
3049        """
3050        if self.cpanel_on_focus is not None:
3051            self._toolbar.enable_bookmark(self.cpanel_on_focus)
3052
3053    def enable_save(self):
3054        """
3055        save
3056        """
3057        if self.cpanel_on_focus is not None:
3058            self._toolbar.enable_save(self.cpanel_on_focus)
3059
3060    def enable_preview(self):
3061        """
3062        preview
3063        """
3064        if self.cpanel_on_focus is not None:
3065            self._toolbar.enable_preview(self.cpanel_on_focus)
3066
3067    def enable_print(self):
3068        """
3069        print
3070        """
3071        if self.cpanel_on_focus is not None:
3072            self._toolbar.enable_print(self.cpanel_on_focus)
3073
3074    def enable_zoom(self):
3075        """
3076        zoom
3077        """
3078        if self.cpanel_on_focus is not None:
3079            self._toolbar.enable_zoom(self.panel_on_focus)
3080
3081    def enable_zoom_in(self):
3082        """
3083        zoom in
3084        """
3085        if self.cpanel_on_focus is not None:
3086            self._toolbar.enable_zoom_in(self.panel_on_focus)
3087
3088    def enable_zoom_out(self):
3089        """
3090        zoom out
3091        """
3092        if self.cpanel_on_focus is not None:
3093            self._toolbar.enable_zoom_out(self.panel_on_focus)
3094
3095    def enable_drag(self, event=None):
3096        """
3097        drag
3098        """
3099        # Not implemeted
3100
3101    def enable_reset(self):
3102        """
3103        reset the current panel
3104        """
3105        if self.cpanel_on_focus is not None:
3106            self._toolbar.enable_reset(self.panel_on_focus)
3107
3108    def get_toolbar_height(self):
3109        """
3110        """
3111        size_y = 0
3112        if self.GetToolBar() is not None and self.GetToolBar().IsShown():
3113            if not IS_LINUX:
3114                _, size_y = self.GetToolBar().GetSizeTuple()
3115        return size_y
3116
3117    def set_schedule_full_draw(self, panel=None, func='del'):
3118        """
3119        Add/subtract the schedule full draw list with the panel given
3120
3121        :param panel: plot panel
3122        :param func: append or del [string]
3123        """
3124
3125        # append this panel in the schedule list if not in yet
3126        if func == 'append':
3127            if panel not in self.schedule_full_draw_list:
3128                self.schedule_full_draw_list.append(panel)
3129        # remove this panel from schedule list
3130        elif func == 'del':
3131            if len(self.schedule_full_draw_list) > 0:
3132                if panel in self.schedule_full_draw_list:
3133                    self.schedule_full_draw_list.remove(panel)
3134
3135        # reset the schdule
3136        if len(self.schedule_full_draw_list) == 0:
3137            self.schedule = False
3138        else:
3139            self.schedule = True
3140
3141    def full_draw(self):
3142        """
3143        Draw the panels with axes in the schedule to full dwar list
3144        """
3145
3146        count = len(self.schedule_full_draw_list)
3147        # if not self.schedule:
3148        if count < 1:
3149            self.set_schedule(False)
3150            return
3151
3152        else:
3153            ind = 0
3154            # if any of the panel is shown do full_draw
3155            for panel in self.schedule_full_draw_list:
3156                ind += 1
3157                if panel.frame.IsShown():
3158                    break
3159                # otherwise, return
3160                if ind == count:
3161                    return
3162        # Simple redraw only for a panel shown
3163
3164        def f_draw(panel):
3165            """
3166            Draw A panel in the full draw list
3167            """
3168            try:
3169                # This checking of GetCapture is to stop redrawing
3170                # while any panel is capture.
3171                frame = panel.frame
3172
3173                if not frame.GetCapture():
3174                    # draw if possible
3175                    panel.set_resizing(False)
3176                    # panel.Show(True)
3177                    panel.draw_plot()
3178                # Check if the panel is not shown
3179                flag = frame.IsShown()
3180                frame.Show(flag)
3181            except:
3182                pass
3183
3184        # Draw all panels
3185        if count == 1:
3186            f_draw(self.schedule_full_draw_list[0])
3187        else:
3188            map(f_draw, self.schedule_full_draw_list)
3189        # Reset the attr
3190        if len(self.schedule_full_draw_list) == 0:
3191            self.set_schedule(False)
3192        else:
3193            self.set_schedule(True)
3194
3195    def set_schedule(self, schedule=False):
3196        """
3197        Set schedule
3198        """
3199        self.schedule = schedule
3200
3201    def get_schedule(self):
3202        """
3203        Get schedule
3204        """
3205        return self.schedule
3206
3207    def on_set_plot_focus(self, panel):
3208        """
3209        Set focus on a plot panel
3210        """
3211        if panel is None:
3212            return
3213        # self.set_plot_unfocus()
3214        panel.on_set_focus(None)
3215        # set focusing panel
3216        self.panel_on_focus = panel
3217        self.set_panel_on_focus(None)
3218
3219    def set_plot_unfocus(self):
3220        """
3221        Un focus all plot panels
3222        """
3223        for plot in self.plot_panels.values():
3224            plot.on_kill_focus(None)
3225
3226    def get_window_size(self):
3227        """
3228        Get window size
3229
3230        :returns: size
3231        :rtype: tuple
3232        """
3233        width, height = self.GetSizeTuple()
3234        if not IS_WIN:
3235            # Subtract toolbar height to get real window side
3236            if self._toolbar.IsShown():
3237                height -= 45
3238        return (width, height)
3239
3240    def _onDrawIdle(self, *args, **kwargs):
3241        """
3242        ReDraw with axes
3243        """
3244        try:
3245            # check if it is time to redraw
3246            if self.GetCapture() is None:
3247                # Draw plot, changes resizing too
3248                self.full_draw()
3249        except:
3250            pass
3251
3252        # restart idle
3253        self._redraw_idle(*args, **kwargs)
3254
3255    def _redraw_idle(self, *args, **kwargs):
3256        """
3257        Restart Idle
3258        """
3259        # restart idle
3260        self.idletimer.Restart(100 * TIME_FACTOR, *args, **kwargs)
3261
3262
3263class DefaultPanel(wx.Panel, PanelBase):
3264    """
3265    Defines the API for a panels to work with
3266    the GUI manager
3267    """
3268    # Internal nickname for the window, used by the AUI manager
3269    window_name = "default"
3270    # Name to appear on the window title bar
3271    window_caption = "Welcome panel"
3272    # Flag to tell the AUI manager to put this panel in the center pane
3273    CENTER_PANE = True
3274
3275    def __init__(self, parent, *args, **kwds):
3276        wx.Panel.__init__(self, parent, *args, **kwds)
3277        PanelBase.__init__(self, parent)
3278
3279
3280class SasViewApp(wx.App):
3281    """
3282    SasView application
3283    """
3284    def OnInit(self):
3285        """
3286        When initialised
3287        """
3288        pos, size, self.is_max = self.window_placement((GUIFRAME_WIDTH,
3289                                                        GUIFRAME_HEIGHT))
3290        self.frame = ViewerFrame(parent=None,
3291                                 title=APPLICATION_NAME,
3292                                 pos=pos,
3293                                 gui_style=DEFAULT_STYLE,
3294                                 size=size)
3295        self.frame.Hide()
3296        if not IS_WIN:
3297            self.frame.EnableCloseButton(False)
3298        self.s_screen = None
3299
3300        try:
3301            self.open_file()
3302        except:
3303            msg = "%s Could not load " % str(APPLICATION_NAME)
3304            msg += "input file from command line.\n"
3305            logging.error(msg)
3306        # Display a splash screen on top of the frame.
3307        try:
3308            if os.path.isfile(SPLASH_SCREEN_PATH):
3309                self.s_screen = \
3310                    self.display_splash_screen(parent=self.frame,
3311                                               path=SPLASH_SCREEN_PATH)
3312            else:
3313                self.frame.Show()
3314        except:
3315            if self.s_screen is not None:
3316                self.s_screen.Close()
3317            msg = "Cannot display splash screen\n"
3318            msg += str(sys.exc_value)
3319            logging.error(msg)
3320            self.frame.Show()
3321
3322        self.SetTopWindow(self.frame)
3323
3324        return True
3325
3326    def maximize_win(self):
3327        """
3328        Maximize the window after the frame shown
3329        """
3330        if self.is_max:
3331            if self.frame.IsShown():
3332                # Max window size
3333                self.frame.Maximize(self.is_max)
3334
3335    def open_file(self):
3336        """
3337        open a state file at the start of the application
3338        """
3339        input_file = None
3340        if len(sys.argv) >= 2:
3341            cmd = sys.argv[0].lower()
3342            basename = os.path.basename(cmd)
3343            app_base = str(APPLICATION_NAME).lower()
3344            if os.path.isfile(cmd) or basename.lower() == app_base:
3345                app_py = app_base + '.py'
3346                app_exe = app_base + '.exe'
3347                app_app = app_base + '.app'
3348                if basename.lower() in [app_py, app_exe, app_app, app_base]:
3349                    data_base = sys.argv[1]
3350                    input_file = os.path.normpath(os.path.join(DATAPATH,
3351                                                               data_base))
3352        if input_file is None:
3353            return
3354        if self.frame is not None:
3355            self.frame.set_input_file(input_file=input_file)
3356
3357    def clean_plugin_models(self, path):
3358        """
3359        Delete plugin models  in app folder
3360
3361        :param path: path of the plugin_models folder in app
3362        """
3363        # do it only the first time app loaded
3364        # delete unused model folder
3365        model_folder = os.path.join(PATH_APP, path)
3366        if os.path.exists(model_folder) and os.path.isdir(model_folder):
3367            if len(os.listdir(model_folder)) > 0:
3368                try:
3369                    for filename in os.listdir(model_folder):
3370                        file_path = os.path.join(model_folder, filename)
3371                        if os.path.isfile(file_path):
3372                            os.remove(file_path)
3373                except:
3374                    logging.error("gui_manager.clean_plugin_models:\n  %s"
3375                                  % sys.exc_value)
3376
3377    def set_manager(self, manager):
3378        """
3379        Sets a reference to the application manager
3380        of the GUI manager (Frame)
3381        """
3382        self.frame.set_manager(manager)
3383
3384    def build_gui(self):
3385        """
3386        Build the GUI
3387        """
3388        # try to load file at the start
3389        self.open_file()
3390        self.frame.build_gui()
3391
3392    def set_welcome_panel(self, panel_class):
3393        """
3394        Set the welcome panel
3395
3396        :param panel_class: class of the welcome panel to be instantiated
3397
3398        """
3399        self.frame.welcome_panel_class = panel_class
3400
3401    def add_perspective(self, perspective):
3402        """
3403        Manually add a perspective to the application GUI
3404        """
3405        self.frame.add_perspective(perspective)
3406
3407    def window_placement(self, size):
3408        """
3409        Determines the position and size of the application frame such that it
3410        fits on the user's screen without obstructing (or being obstructed by)
3411        the Windows task bar.  The maximum initial size in pixels is bounded by
3412        WIDTH x HEIGHT.  For most monitors, the application
3413        will be centered on the screen; for very large monitors it will be
3414        placed on the left side of the screen.
3415        """
3416        is_maximized = False
3417        # Get size of screen without
3418        for screenCount in range(wx.Display().GetCount()):
3419            screen = wx.Display(screenCount)
3420            if screen.IsPrimary():
3421                displayRect = screen.GetClientArea()
3422                break
3423
3424        posX, posY, displayWidth, displayHeight = displayRect
3425        customWidth, customHeight = size
3426
3427        # If the custom size is default, set 90% of the screen size
3428        if customWidth <= 0 and customHeight <= 0:
3429            if customWidth == 0 and customHeight == 0:
3430                is_maximized = True
3431            customWidth = displayWidth * 0.9
3432            customHeight = displayHeight * 0.9
3433        else:
3434            # If the custom screen is bigger than the
3435            # window screen than make maximum size
3436            if customWidth > displayWidth:
3437                customWidth = displayWidth
3438            if customHeight > displayHeight:
3439                customHeight = displayHeight
3440
3441        # Note that when running Linux and using an Xming (X11) server on a PC
3442        # with a dual  monitor configuration, the reported display size may be
3443        # that of both monitors combined with an incorrect display count of 1.
3444        # To avoid displaying this app across both monitors, we check for
3445        # screen 'too big'.  If so, we assume a smaller width which means the
3446        # application will be placed towards the left hand side of the screen.
3447
3448        # If dual screen registered as 1 screen. Make width half.
3449        # MAC just follows the default behavior of pos
3450        if IS_WIN:
3451            if displayWidth > (displayHeight * 2):
3452                if customWidth == displayWidth:
3453                    customWidth = displayWidth / 2
3454                # and set the position to be the corner of the screen.
3455                posX = 0
3456                posY = 0
3457
3458            # Make the position the middle of the screen. (Not 0,0)
3459            else:
3460                posX = (displayWidth - customWidth) / 2
3461                posY = (displayHeight - customHeight) / 2
3462        # Return the suggested position and size for the application frame.
3463        return (posX, posY), (customWidth, customHeight), is_maximized
3464
3465    def display_splash_screen(self, parent,
3466                              path=SPLASH_SCREEN_PATH):
3467        """Displays the splash screen.  It will exactly cover the main frame."""
3468
3469        # Prepare the picture.  On a 2GHz intel cpu, this takes about a second.
3470        image = wx.Image(path, wx.BITMAP_TYPE_PNG)
3471        image.Rescale(SPLASH_SCREEN_WIDTH,
3472                      SPLASH_SCREEN_HEIGHT, wx.IMAGE_QUALITY_HIGH)
3473        bm = image.ConvertToBitmap()
3474
3475        # Create and show the splash screen.  It will disappear only when the
3476        # program has entered the event loop AND either the timeout has expired
3477        # or the user has left clicked on the screen.  Thus any processing
3478        # performed in this routine (including sleeping) or processing in the
3479        # calling routine (including doing imports) will prevent the splash
3480        # screen from disappearing.
3481        #
3482        # Note that on Linux, the timeout appears to occur immediately in which
3483        # case the splash screen disappears upon entering the event loop.
3484        s_screen = wx.SplashScreen(bitmap=bm,
3485                                   splashStyle=(wx.SPLASH_TIMEOUT |
3486                                                wx.SPLASH_CENTRE_ON_SCREEN),
3487                                   style=(wx.SIMPLE_BORDER |
3488                                          wx.FRAME_NO_TASKBAR |
3489                                          wx.FRAME_FLOAT_ON_PARENT),
3490                                   milliseconds=SS_MAX_DISPLAY_TIME,
3491                                   parent=parent,
3492                                   id=wx.ID_ANY)
3493        from sas.sasgui.guiframe.gui_statusbar import SPageStatusbar
3494        statusBar = SPageStatusbar(s_screen)
3495        s_screen.SetStatusBar(statusBar)
3496        s_screen.Bind(wx.EVT_CLOSE, self.on_close_splash_screen)
3497        s_screen.Show()
3498        return s_screen
3499
3500    def on_close_splash_screen(self, event):
3501        """
3502        When the splash screen is closed.
3503        """
3504        self.frame.Show(True)
3505        event.Skip()
3506        self.maximize_win()
3507
3508
3509class MDIFrame(CHILD_FRAME):
3510    """
3511    Frame for panels
3512    """
3513    def __init__(self, parent, panel, title="Untitled", size=(300, 200)):
3514        """
3515        comment
3516        :param parent: parent panel/container
3517        """
3518        # Initialize the Frame object
3519        CHILD_FRAME.__init__(self, parent=parent, id=wx.ID_ANY,
3520                             title=title, size=size)
3521        self.parent = parent
3522        self.name = "Untitled"
3523        self.batch_on = self.parent.batch_on
3524        self.panel = panel
3525        if panel is not None:
3526            self.set_panel(panel)
3527        self.Show(False)
3528
3529    def show_data_panel(self, action):
3530        """
3531        Turns on the data panel
3532
3533        The the data panel is optional.  Most of its functions can be
3534        performed from the menu bar and from the plots.
3535        """
3536        self.parent.show_data_panel(action)
3537
3538    def set_panel(self, panel):
3539        """
3540        """
3541        self.panel = panel
3542        self.name = panel.window_name
3543        self.SetTitle(panel.window_caption)
3544        self.SetHelpText(panel.help_string)
3545        width, height = self.parent._get_panels_size(panel)
3546        if hasattr(panel, "CENTER_PANE") and panel.CENTER_PANE:
3547            width *= 0.6
3548        self.SetSize((width, height))
3549        self.parent.put_icon(self)
3550        self.Bind(wx.EVT_SET_FOCUS, self.set_panel_focus)
3551        self.Bind(wx.EVT_CLOSE, self.OnClose)
3552        self.Show(False)
3553
3554    def set_panel_focus(self, event):
3555        """
3556        """
3557        if self.parent.panel_on_focus != self.panel:
3558            self.panel.SetFocus()
3559            self.parent.panel_on_focus = self.panel
3560
3561    def OnClose(self, event):
3562        """
3563        On Close event
3564        """
3565        self.panel.on_close(event)
3566
3567if __name__ == "__main__":
3568    app = SasViewApp(0)
3569    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.