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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_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 fd62331 was a4c2445, checked in by krzywon, 8 years ago

Fixes #730: All data, theories, plots, and analyses in perspectives are now flushed when loading in a project file.

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