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

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 914ba0a was 914ba0a, checked in by Paul Kienzle <pkienzle@…>, 7 years ago

merge with master

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