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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalcmagnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 899e084 was efe730d, checked in by Paul Kienzle <pkienzle@…>, 8 years ago

move sasview to src/sas/sasview and refactor bundled apps for easier debugging

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