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

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

Setting proper CL env even when initiated from None

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