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

magnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249
Last change on this file since d96744de was 9220e89c, checked in by Jeff Krzywon <jkrzywon@…>, 5 years ago

Code cleanup and py3 compatibility fixes.

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