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

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

Added settings reading from custom config file

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