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

magnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249unittest-saveload
Last change on this file since f4e2f22 was feec1cb, checked in by krzywon, 6 years ago

Differentiate between scalars and arrays.

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