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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalcmagnetic_scattrelease-4.1.1release-4.1.2release-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since dab94f6 was 73cbeec, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 8 years ago

Opencl dialog (#29)

Merged OpenCL dialog prototype branch

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