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

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 95eef00 was 95eef00, checked in by krzywon, 7 years ago

Merge branch 'master' into wx_save_simfit

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