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

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

Add NXcanSAS writer as an option when saving data from GUI. refs #1129

  • 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', '')
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            self._help_menu.Append(wx_id, '&Tutorial', 'Software tutorial')
1285            wx.EVT_MENU(self, wx_id, self._onTutorial)
1286
1287        if config._do_acknowledge:
1288            wx_id = wx.NewId()
1289            self._help_menu.Append(wx_id, '&Acknowledge',
1290                                   'Acknowledging SasView')
1291            wx.EVT_MENU(self, wx_id, self._onAcknowledge)
1292
1293        if config._do_aboutbox:
1294            logger.info("Doing help menu")
1295            wx_id = wx.NewId()
1296            self._help_menu.Append(wx_id, '&About', 'Software information')
1297            wx.EVT_MENU(self, wx_id, self._onAbout)
1298
1299        if config.marketplace_url:
1300            wx_id = wx.NewId()
1301            self._help_menu.Append(wx_id, '&Model marketplace', '')
1302            wx.EVT_MENU(self, wx_id, self._on_marketplace_click)
1303
1304        # Checking for updates
1305        wx_id = wx.NewId()
1306        self._help_menu.Append(wx_id, '&Check for update',
1307                               'Check for the latest version of %s' %
1308                               config.__appname__)
1309        wx.EVT_MENU(self, wx_id, self._check_update)
1310        self._menubar.Append(self._help_menu, '&Help')
1311
1312    def _add_menu_view(self):
1313        """
1314        add menu items under view menu
1315        """
1316        if not VIEW_MENU:
1317            return
1318        self._view_menu = wx.Menu()
1319
1320        wx_id = wx.NewId()
1321        hint = "Display the Grid Window for batch results etc."
1322        self._view_menu.Append(wx_id, '&Show Grid Window', hint)
1323        wx.EVT_MENU(self, wx_id, self.show_batch_frame)
1324
1325        self._view_menu.AppendSeparator()
1326        style = self.__gui_style & GUIFRAME.MANAGER_ON
1327        wx_id = wx.NewId()
1328        self._data_panel_menu = self._view_menu.Append(wx_id,
1329                                                       '&Show Data Explorer',
1330                                                       '')
1331        wx.EVT_MENU(self, wx_id, self.show_data_panel)
1332        if style == GUIFRAME.MANAGER_ON:
1333            self._data_panel_menu.SetText('Hide Data Explorer')
1334        else:
1335            self._data_panel_menu.SetText('Show Data Explorer')
1336
1337        self._view_menu.AppendSeparator()
1338        wx_id = wx.NewId()
1339        style1 = self.__gui_style & GUIFRAME.TOOLBAR_ON
1340        if style1 == GUIFRAME.TOOLBAR_ON:
1341            self._toolbar_menu = self._view_menu.Append(wx_id, '&Hide Toolbar',
1342                                                        '')
1343        else:
1344            self._toolbar_menu = self._view_menu.Append(wx_id, '&Show Toolbar',
1345                                                        '')
1346        wx.EVT_MENU(self, wx_id, self._on_toggle_toolbar)
1347
1348        if custom_config is not None:
1349            self._view_menu.AppendSeparator()
1350            wx_id = wx.NewId()
1351            hint_ss = "Select the current/default configuration "
1352            hint_ss += "as a startup setting"
1353            preference_menu = self._view_menu.Append(wx_id, 'Startup Setting',
1354                                                     hint_ss)
1355            wx.EVT_MENU(self, wx_id, self._on_preference_menu)
1356
1357        wx_id = wx.NewId()
1358        self._view_menu.AppendSeparator()
1359        self._view_menu.Append(wx_id, 'Category Manager',
1360                               'Edit model categories')
1361        wx.EVT_MENU(self, wx_id, self._on_category_manager)
1362
1363        self._menubar.Append(self._view_menu, '&View')
1364
1365    def show_batch_frame(self, event=None):
1366        """
1367        show the grid of result
1368        """
1369        # Show(False) before Show(True) in order to bring it to the front
1370        self.batch_frame.Show(False)
1371        self.batch_frame.Show(True)
1372
1373    def on_category_panel(self, event):
1374        """
1375        On cat panel
1376        """
1377        self._on_category_manager(event)
1378
1379    def _on_category_manager(self, event):
1380        """
1381        Category manager frame
1382        """
1383        frame = CategoryManager(self, -1, 'Model Category Manager')
1384        icon = self.GetIcon()
1385        frame.SetIcon(icon)
1386
1387    def _on_preference_menu(self, event):
1388        """
1389        Build a panel to allow to edit Mask
1390        """
1391        from sas.sasgui.guiframe.startup_configuration \
1392            import StartupConfiguration as ConfDialog
1393
1394        dialog = ConfDialog(parent=self, gui=self.__gui_style)
1395        result = dialog.ShowModal()
1396        if result == wx.ID_OK:
1397            dialog.write_custom_config()
1398            # post event for info
1399            wx.PostEvent(self, StatusEvent(status="Wrote custom configuration",
1400                                           info='info'))
1401        dialog.Destroy()
1402
1403    def _add_menu_application(self):
1404        """
1405        # Attach a menu item for each defined perspective or application.
1406        # Only add the perspective menu if there are more than one perspectives
1407        add menu application
1408        """
1409        if self._num_perspectives > 1:
1410            plug_data_count = False
1411            plug_no_data_count = False
1412            self._applications_menu = wx.Menu()
1413            pos = 0
1414            separator = self._applications_menu.AppendSeparator()
1415            for plug in self.plugins:
1416                if len(plug.get_perspective()) > 0:
1417                    id = wx.NewId()
1418                    if plug.use_data():
1419                        self._applications_menu.InsertCheckItem(pos, id,
1420                                                                plug.sub_menu,
1421                                    "Switch to analysis: %s" % plug.sub_menu)
1422                        plug_data_count = True
1423                        pos += 1
1424                    else:
1425                        plug_no_data_count = True
1426                        self._applications_menu.AppendCheckItem(id,
1427                                                                plug.sub_menu,
1428                            "Switch to analysis: %s" % plug.sub_menu)
1429                    wx.EVT_MENU(self, id, plug.on_perspective)
1430
1431            if not plug_data_count or not plug_no_data_count:
1432                self._applications_menu.RemoveItem(separator)
1433            # Windows introduces a "Window" menu item during the layout process
1434            # somehow.  We want it to be next to the last item with Help as
1435            # last. However Analysis gets stuck after Window in normal ordering
1436            # so force it to be next after the Tools menu item.  Should we add
1437            # another menu item will need to check if this is still where we
1438            # want Analysis.  This is NOT an issue on the Mac which does not
1439            # have the extra Window menu item.
1440            #      March 2016 Code Camp  -- PDB
1441            Tools_pos = self._menubar.FindMenu("Tools")
1442            self._menubar.Insert(Tools_pos+1, self._applications_menu,
1443                                 '&Analysis')
1444            self._check_applications_menu()
1445
1446    def _populate_file_menu(self):
1447        """
1448        Insert menu item under file menu
1449        """
1450        for plugin in self.plugins:
1451            if len(plugin.populate_file_menu()) > 0:
1452                for item in plugin.populate_file_menu():
1453                    m_name, m_hint, m_handler = item
1454                    wx_id = wx.NewId()
1455                    self._file_menu.Append(wx_id, m_name, m_hint)
1456                    wx.EVT_MENU(self, wx_id, m_handler)
1457                self._file_menu.AppendSeparator()
1458
1459        style1 = self.__gui_style & GUIFRAME.MULTIPLE_APPLICATIONS
1460        if OPEN_SAVE_MENU:
1461            wx_id = wx.NewId()
1462            hint_load_file = "read all analysis states saved previously"
1463            self._save_appl_menu = self._file_menu.Append(wx_id,
1464                                                          '&Open Project',
1465                                                          hint_load_file)
1466            wx.EVT_MENU(self, wx_id, self._on_open_state_project)
1467
1468        if style1 == GUIFRAME.MULTIPLE_APPLICATIONS:
1469            # some menu of plugin to be seen under file menu
1470            hint_load_file = "Read a status files and load"
1471            hint_load_file += " them into the analysis"
1472            wx_id = wx.NewId()
1473            self._save_appl_menu = self._file_menu.Append(wx_id,
1474                                                          '&Open Analysis',
1475                                                          hint_load_file)
1476            wx.EVT_MENU(self, wx_id, self._on_open_state_application)
1477        if OPEN_SAVE_MENU:
1478            self._file_menu.AppendSeparator()
1479            wx_id = wx.NewId()
1480            self._file_menu.Append(wx_id, '&Save Project',
1481                                   'Save the state of the whole analysis')
1482            wx.EVT_MENU(self, wx_id, self._on_save_project)
1483        if style1 == GUIFRAME.MULTIPLE_APPLICATIONS:
1484            wx_id = wx.NewId()
1485            txt = '&Save Analysis'
1486            txt2 = 'Save state of the current active analysis panel'
1487            self._save_appl_menu = self._file_menu.Append(wx_id, txt, txt2)
1488            wx.EVT_MENU(self, wx_id, self._on_save_application)
1489        if not sys.platform == 'darwin':
1490            self._file_menu.AppendSeparator()
1491            wx_id = wx.NewId()
1492            self._file_menu.Append(wx_id, '&Quit', 'Exit')
1493            wx.EVT_MENU(self, wx_id, self.Close)
1494
1495    def _add_menu_file(self):
1496        """
1497        add menu file
1498        """
1499        # File menu
1500        self._file_menu = wx.Menu()
1501        # Add sub menus
1502        self._menubar.Append(self._file_menu, '&File')
1503
1504    def _add_menu_edit(self):
1505        """
1506        add menu edit
1507        """
1508        if not EDIT_MENU:
1509            return
1510        # Edit Menu
1511        self._edit_menu = wx.Menu()
1512        self._edit_menu.Append(GUIFRAME_ID.UNDO_ID, '&Undo',
1513                               'Undo the previous action')
1514        wx.EVT_MENU(self, GUIFRAME_ID.UNDO_ID, self.on_undo_panel)
1515        self._edit_menu.Append(GUIFRAME_ID.REDO_ID, '&Redo',
1516                               'Redo the previous action')
1517        wx.EVT_MENU(self, GUIFRAME_ID.REDO_ID, self.on_redo_panel)
1518        self._edit_menu.AppendSeparator()
1519        self._edit_menu.Append(GUIFRAME_ID.COPY_ID, '&Copy Params',
1520                               'Copy parameter values')
1521        wx.EVT_MENU(self, GUIFRAME_ID.COPY_ID, self.on_copy_panel)
1522        self._edit_menu.Append(GUIFRAME_ID.PASTE_ID, '&Paste Params',
1523                               'Paste parameter values')
1524        wx.EVT_MENU(self, GUIFRAME_ID.PASTE_ID, self.on_paste_panel)
1525
1526        self._edit_menu.AppendSeparator()
1527
1528        self._edit_menu_copyas = wx.Menu()
1529        # Sub menu for Copy As...
1530        self._edit_menu_copyas.Append(GUIFRAME_ID.COPYEX_ID,
1531                                      'Copy current tab to Excel',
1532                                      'Copy parameter values in tabular format')
1533        wx.EVT_MENU(self, GUIFRAME_ID.COPYEX_ID, self.on_copy_panel)
1534
1535        self._edit_menu_copyas.Append(GUIFRAME_ID.COPYLAT_ID,
1536                                      'Copy current tab to LaTeX',
1537                                      'Copy parameter values in tabular format')
1538        wx.EVT_MENU(self, GUIFRAME_ID.COPYLAT_ID, self.on_copy_panel)
1539
1540        self._edit_menu.AppendMenu(GUIFRAME_ID.COPYAS_ID, 'Copy Params as...',
1541                                   self._edit_menu_copyas,
1542                                   'Copy parameter values in various formats')
1543
1544        self._edit_menu.AppendSeparator()
1545
1546        self._edit_menu.Append(GUIFRAME_ID.PREVIEW_ID, '&Report Results',
1547                               'Preview current panel')
1548        wx.EVT_MENU(self, GUIFRAME_ID.PREVIEW_ID, self.on_preview_panel)
1549
1550        self._edit_menu.Append(GUIFRAME_ID.RESET_ID, '&Reset Page',
1551                               'Reset current panel')
1552        wx.EVT_MENU(self, GUIFRAME_ID.RESET_ID, self.on_reset_panel)
1553
1554        self._menubar.Append(self._edit_menu, '&Edit')
1555        self.enable_edit_menu()
1556
1557    def get_style(self):
1558        """
1559        Return the gui style
1560        """
1561        return self.__gui_style
1562
1563    def _add_menu_data(self):
1564        """
1565        Add menu item item data to menu bar
1566        """
1567        if self._data_plugin is not None:
1568            menu_list = self._data_plugin.populate_menu(self)
1569            if menu_list:
1570                for (menu, name) in menu_list:
1571                    self._menubar.Append(menu, name)
1572
1573    def _on_toggle_toolbar(self, event=None):
1574        """
1575        hide or show toolbar
1576        """
1577        if self._toolbar is None:
1578            return
1579        if self._toolbar.IsShown():
1580            if self._toolbar_menu is not None:
1581                self._toolbar_menu.SetItemLabel('Show Toolbar')
1582            self._toolbar.Hide()
1583        else:
1584            if self._toolbar_menu is not None:
1585                self._toolbar_menu.SetItemLabel('Hide Toolbar')
1586            self._toolbar.Show()
1587        self._toolbar.Realize()
1588
1589    def _on_status_event(self, evt):
1590        """
1591        Display status message
1592        """
1593        # This CallAfter fixes many crashes on MAC.
1594        wx.CallAfter(self.sb.set_status, evt)
1595
1596    def on_view(self, evt):
1597        """
1598        A panel was selected to be shown. If it's not already
1599        shown, display it.
1600
1601        :param evt: menu event
1602
1603        """
1604        panel_id = str(evt.GetId())
1605        self.on_set_plot_focus(self.panels[panel_id])
1606        wx.CallLater(5 * TIME_FACTOR, self.set_schedule(True))
1607        self.set_plot_unfocus()
1608
1609    def show_welcome_panel(self, event):
1610        """
1611        Display the welcome panel
1612        """
1613        if self.defaultPanel is None:
1614            return
1615        frame = self.panels['default'].get_frame()
1616        if frame is None:
1617            return
1618        # Show default panel
1619        if not frame.IsShown():
1620            frame.Show(True)
1621
1622    def on_close_welcome_panel(self):
1623        """
1624        Close the welcome panel
1625        """
1626        if self.defaultPanel is None:
1627            return
1628        default_panel = self.panels["default"].frame
1629        if default_panel.IsShown():
1630            default_panel.Show(False)
1631
1632    def delete_panel(self, uid):
1633        """
1634        delete panel given uid
1635        """
1636        ID = str(uid)
1637        config.printEVT("delete_panel: %s" % ID)
1638        if ID in self.panels.keys():
1639            self.panel_on_focus = None
1640            panel = self.panels[ID]
1641
1642            if hasattr(panel, "connect"):
1643                panel.connect.disconnect()
1644            self._plotting_plugin.delete_panel(panel.group_id)
1645
1646            if panel in self.schedule_full_draw_list:
1647                self.schedule_full_draw_list.remove(panel)
1648
1649            # delete uid number not str(uid)
1650            if ID in self.plot_panels.keys():
1651                del self.plot_panels[ID]
1652            if ID in self.panels.keys():
1653                del self.panels[ID]
1654        else:
1655            logger.error("delete_panel: No such plot id as %s" % ID)
1656
1657    def create_gui_data(self, data, path=None):
1658        """
1659        """
1660        return self._data_manager.create_gui_data(data, path)
1661
1662    def get_data(self, path):
1663        """
1664        """
1665        log_msg = ''
1666        basename = os.path.basename(path)
1667        _, extension = os.path.splitext(basename)
1668        if extension.lower() not in EXTENSIONS:
1669            log_msg = "File Loader cannot "
1670            log_msg += "load: %s\n" % str(basename)
1671            log_msg += "Try Data opening...."
1672            logger.error(log_msg)
1673            return
1674
1675        # reading a state file
1676        for plug in self.plugins:
1677            reader, ext = plug.get_extensions()
1678            if reader is not None:
1679                # read the state of the single plugin
1680                if extension == ext:
1681                    reader.read(path)
1682                    return
1683                elif extension == APPLICATION_STATE_EXTENSION:
1684                    try:
1685                        reader.read(path)
1686                    except:
1687                        msg = "DataLoader Error: Encounted Non-ASCII character"
1688                        msg += "\n(%s)" % sys.exc_value
1689                        wx.PostEvent(self, StatusEvent(status=msg,
1690                                                       info="error",
1691                                                       type="stop"))
1692                        return
1693
1694        style = self.__gui_style & GUIFRAME.MANAGER_ON
1695        if style == GUIFRAME.MANAGER_ON:
1696            if self._data_panel is not None:
1697                self._data_panel.frame.Show(True)
1698
1699    def load_from_cmd(self, path):
1700        """
1701        load data from cmd or application
1702        """
1703        if path is None:
1704            return
1705        else:
1706            path = os.path.abspath(path)
1707            if not os.path.isfile(path) and not os.path.isdir(path):
1708                return
1709
1710            if os.path.isdir(path):
1711                self.load_folder(path)
1712                return
1713
1714        basename = os.path.basename(path)
1715        _, extension = os.path.splitext(basename)
1716        if extension.lower() not in EXTENSIONS:
1717            self.load_data(path)
1718        else:
1719            self.load_state(path)
1720
1721        self._default_save_location = os.path.dirname(path)
1722
1723    def load_state(self, path, is_project=False):
1724        """
1725        load data from command line or application
1726        """
1727        if path and (path is not None) and os.path.isfile(path):
1728            basename = os.path.basename(path)
1729            if APPLICATION_STATE_EXTENSION is not None \
1730                    and basename.endswith(APPLICATION_STATE_EXTENSION):
1731                if is_project:
1732                    for ID in self.plot_panels.keys():
1733                        panel = self.plot_panels[ID]
1734                        panel.on_close(None)
1735            self.get_data(path)
1736            wx.PostEvent(self, StatusEvent(status="Completed loading."))
1737        else:
1738            wx.PostEvent(self, StatusEvent(status=" "))
1739
1740    def load_data(self, path):
1741        """
1742        load data from command line
1743        """
1744        if not os.path.isfile(path):
1745            return
1746        basename = os.path.basename(path)
1747        _, extension = os.path.splitext(basename)
1748        if extension.lower() in EXTENSIONS:
1749            log_msg = "Data Loader cannot "
1750            log_msg += "load: %s\n" % str(path)
1751            log_msg += "Try File opening ...."
1752            logger.error(log_msg)
1753            return
1754        log_msg = ''
1755        output = {}
1756        error_message = ""
1757        try:
1758            logger.info("Loading Data...:\n" + str(path) + "\n")
1759            temp = self.loader.load(path)
1760            if temp.__class__.__name__ == "list":
1761                for item in temp:
1762                    data = self.create_gui_data(item, path)
1763                    output[data.id] = data
1764            else:
1765                data = self.create_gui_data(temp, path)
1766                output[data.id] = data
1767
1768            self.add_data(data_list=output)
1769        except:
1770            error_message = "Error while loading"
1771            error_message += " Data from cmd:\n %s\n" % str(path)
1772            error_message += str(sys.exc_value) + "\n"
1773            logger.error(error_message)
1774
1775    def load_folder(self, path):
1776        """
1777        Load entire folder
1778        """
1779        if not os.path.isdir(path):
1780            return
1781        if self._data_plugin is None:
1782            return
1783        try:
1784            if path is not None:
1785                self._default_save_location = os.path.dirname(path)
1786                file_list = self._data_plugin.get_file_path(path)
1787                self._data_plugin.get_data(file_list)
1788            else:
1789                return
1790        except:
1791            error_message = "Error while loading"
1792            error_message += " Data folder from cmd:\n %s\n" % str(path)
1793            error_message += str(sys.exc_value) + "\n"
1794            logger.error(error_message)
1795
1796    def _on_open_state_application(self, event):
1797        """
1798        """
1799        path = None
1800        if self._default_save_location is None:
1801            self._default_save_location = os.getcwd()
1802        wx.PostEvent(self, StatusEvent(status="Loading Analysis file..."))
1803        plug_wlist = self._on_open_state_app_helper()
1804        dlg = wx.FileDialog(self,
1805                            "Choose a file",
1806                            self._default_save_location, "",
1807                            plug_wlist)
1808        if dlg.ShowModal() == wx.ID_OK:
1809            path = dlg.GetPath()
1810            if path is not None:
1811                self._default_save_location = os.path.dirname(path)
1812        dlg.Destroy()
1813        self.load_state(path=path)
1814
1815    def _on_open_state_app_helper(self):
1816        """
1817        Helps '_on_open_state_application()' to find the extension of
1818        the current perspective/application
1819        """
1820        # No current perspective or no extension attr
1821        if self._current_perspective is None:
1822            return PLUGINS_WLIST
1823        try:
1824            # Find the extension of the perspective
1825            # and get that as 1st item in list
1826            ind = None
1827            app_ext = self._current_perspective._extensions
1828            plug_wlist = config.PLUGINS_WLIST
1829            for ext in set(plug_wlist):
1830                if ext.count(app_ext) > 0:
1831                    ind = ext
1832                    break
1833            # Found the extension
1834            if ind is not None:
1835                plug_wlist.remove(ind)
1836                plug_wlist.insert(0, ind)
1837                try:
1838                    plug_wlist = '|'.join(plug_wlist)
1839                except:
1840                    plug_wlist = ''
1841
1842        except:
1843            plug_wlist = PLUGINS_WLIST
1844
1845        return plug_wlist
1846
1847    def _on_open_state_project(self, event):
1848        """
1849        Load in a .svs project file after removing all data from SasView
1850        """
1851        path = None
1852        if self._default_save_location is None:
1853            self._default_save_location = os.getcwd()
1854        msg = "This operation will set remove all data, plots and analyses from"
1855        msg += " SasView before loading the project. Do you wish to continue?"
1856        msg_box = wx.MessageDialog(None, msg, 'Warning', wx.OK | wx.CANCEL)
1857        if msg_box.ShowModal() == wx.ID_OK:
1858            self._data_panel.selection_cbox.SetValue('Select all Data')
1859            self._data_panel._on_selection_type(None)
1860            for _, theory_dict in self._data_panel.list_cb_theory.iteritems():
1861                for key, value in theory_dict.iteritems():
1862                    item, _, _ = value
1863                    item.Check(True)
1864
1865            wx.PostEvent(self, StatusEvent(status="Loading Project file..."))
1866            dlg = wx.FileDialog(self, "Choose a file",
1867                                self._default_save_location, "",
1868                                APPLICATION_WLIST)
1869            if dlg.ShowModal() == wx.ID_OK:
1870                path = dlg.GetPath()
1871            if path is not None:
1872                self._default_save_location = os.path.dirname(path)
1873                dlg.Destroy()
1874                # Reset to a base state
1875                self._on_reset_state()
1876                self._data_panel.on_remove(None, False)
1877                # Load the project file
1878                self.load_state(path=path, is_project=True)
1879
1880    def _on_reset_state(self):
1881        """
1882        Resets SasView to its freshly opened state.
1883        :return: None
1884        """
1885        # Reset all plugins to their base state
1886        self._data_panel.set_panel_on_focus()
1887        # Remove all loaded data
1888        for plugin in self.plugins:
1889            plugin.clear_panel()
1890        # Reset plot number to 0
1891        self.graph_num = 0
1892
1893    def _on_save_application(self, event):
1894        """
1895        save the state of the current active application
1896        """
1897        if self.cpanel_on_focus is not None:
1898            try:
1899                wx.PostEvent(self,
1900                             StatusEvent(status="Saving Analysis file..."))
1901                self.cpanel_on_focus.on_save(event)
1902                wx.PostEvent(self,
1903                             StatusEvent(status="Completed saving."))
1904            except Exception:
1905                msg = "Error occurred while saving: "
1906                msg += traceback.format_exc()
1907                msg += "To save, the application panel should have a data set.."
1908                wx.PostEvent(self, StatusEvent(status=msg))
1909
1910    def _on_save_project(self, event):
1911        """
1912        save the state of the SasView as *.svs
1913        """
1914        if self._current_perspective is None:
1915            return
1916        wx.PostEvent(self, StatusEvent(status="Saving Project file..."))
1917        path = None
1918        extension = '*' + APPLICATION_STATE_EXTENSION
1919        dlg = wx.FileDialog(self, "Save Project file",
1920                            self._default_save_location, "sasview_proj",
1921                            extension,
1922                            wx.SAVE)
1923        if dlg.ShowModal() == wx.ID_OK:
1924            path = dlg.GetPath()
1925            self._default_save_location = os.path.dirname(path)
1926        else:
1927            return None
1928        dlg.Destroy()
1929        try:
1930            if path is None:
1931                return
1932            # default cansas xml doc
1933            doc = None
1934            for panel in self.panels.values():
1935                temp = panel.save_project(doc)
1936                if temp is not None:
1937                    doc = temp
1938
1939            # Write the XML document
1940            extens = APPLICATION_STATE_EXTENSION
1941            fName = os.path.splitext(path)[0] + extens
1942            if doc is not None:
1943                fd = open(fName, 'w')
1944                fd.write(doc.toprettyxml())
1945                fd.close()
1946                wx.PostEvent(self, StatusEvent(status="Completed Saving."))
1947            else:
1948                msg = "Error occurred while saving the project: "
1949                msg += "To save, at least one application panel "
1950                msg += "should have a data set "
1951                msg += "and model selected. "
1952                msg += "No project was saved to %s" % (str(path))
1953                logger.warning(msg)
1954                wx.PostEvent(self, StatusEvent(status=msg, info="error"))
1955        except Exception:
1956            msg = "Error occurred while saving: "
1957            msg += traceback.format_exc()
1958            msg += "To save, at least one application panel "
1959            msg += "should have a data set.."
1960            wx.PostEvent(self, StatusEvent(status=msg, info="error"))
1961
1962    def on_save_helper(self, doc, reader, panel, path):
1963        """
1964        Save state into a file
1965        """
1966        if reader is not None:
1967            # case of a panel with multi-pages
1968            if hasattr(panel, "opened_pages"):
1969                for _, page in panel.opened_pages.iteritems():
1970                    data = page.get_data()
1971                    # state must be cloned
1972                    state = page.get_state().clone()
1973                    if data is not None:
1974                        new_doc = reader.write_toXML(data, state)
1975                        if doc is not None and hasattr(doc, "firstChild"):
1976                            child = new_doc.firstChild.firstChild
1977                            doc.firstChild.appendChild(child)
1978                        else:
1979                            doc = new_doc
1980            # case of only a panel
1981            else:
1982                data = panel.get_data()
1983                state = panel.get_state()
1984                if data is not None:
1985                    new_doc = reader.write_toXML(data, state)
1986                    if doc is not None and hasattr(doc, "firstChild"):
1987                        child = new_doc.firstChild.firstChild
1988                        doc.firstChild.appendChild(child)
1989                    else:
1990                        doc = new_doc
1991        return doc
1992
1993    def quit_guiframe(self):
1994        """
1995        Pop up message to make sure the user wants to quit the application
1996        """
1997        message = "\nDo you really want to exit this application?        \n\n"
1998        dial = wx.MessageDialog(self, message, 'Confirm Exit',
1999                                wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
2000        if dial.ShowModal() == wx.ID_YES:
2001            return True
2002        else:
2003            return False
2004
2005    def WindowClose(self, event=None):
2006        """
2007        Quit the application from x icon
2008        """
2009        flag = self.quit_guiframe()
2010        if flag:
2011            _pylab_helpers.Gcf.figs = {}
2012            self.Close()
2013
2014    def Close(self, event=None):
2015        """
2016        Quit the application
2017        """
2018        #IF SAS_OPENCL is set, settings are stored in the custom config file
2019        self._write_opencl_config_file()
2020        logger.info(" --- SasView session was closed --- \n")
2021        wx.Exit()
2022        sys.exit()
2023
2024    def _write_opencl_config_file(self):
2025        """
2026        Writes OpenCL settings to custom config file, so they can be remmbered
2027        from session to session
2028        """
2029        if custom_config is not None:
2030            sas_opencl = os.environ.get("SAS_OPENCL")
2031            new_config_lines = []
2032            config_file = open(custom_config.__file__)
2033            config_lines = config_file.readlines()
2034            for line in config_lines:
2035                if "SAS_OPENCL" in line:
2036                    if sas_opencl:
2037                        new_config_lines.append("SAS_OPENCL = \"" + sas_opencl
2038                                                + "\"\n")
2039                    else:
2040                        new_config_lines.append("SAS_OPENCL = \"None\"\n")
2041                else:
2042                    new_config_lines.append(line)
2043            config_file.close()
2044
2045            #If custom_config is None, settings will not be remmbered
2046            new_config_file = open(custom_config.__file__,"w")
2047            new_config_file.writelines(new_config_lines)
2048            new_config_file.close()
2049        else:
2050            logger.info("Failed to save OPENCL settings in custom config file")
2051
2052
2053    def _check_update(self, event=None):
2054        """
2055        Check with the deployment server whether a new version
2056        of the application is available.
2057        A thread is started for the connecting with the server. The thread calls
2058        a call-back method when the current version number has been obtained.
2059        """
2060        version_info = {"version": "0.0.0"}
2061        c = Connection(config.__update_URL__, config.UPDATE_TIMEOUT)
2062        response = c.connect()
2063        if response is not None:
2064            try:
2065                content = response.read().strip()
2066                logger.info("Connected to www.sasview.org. Latest version: %s", content)
2067                version_info = json.loads(content)
2068            except:
2069                logger.info("Failed to connect to www.sasview.org")
2070        self._process_version(version_info, standalone=event is None)
2071
2072
2073    def _process_version(self, version_info, standalone=True):
2074        """
2075        Call-back method for the process of checking for updates.
2076        This methods is called by a VersionThread object once the current
2077        version number has been obtained. If the check is being done in the
2078        background, the user will not be notified unless there's an update.
2079
2080        :param version: version string
2081        :param standalone: True of the update is being checked in
2082           the background, False otherwise.
2083
2084        """
2085        try:
2086            version = version_info["version"]
2087            if version == "0.0.0":
2088                msg = "Could not connect to the application server."
2089                msg += " Please try again later."
2090                self.SetStatusText(msg)
2091            elif cmp(version, config.__version__) > 0:
2092                msg = "Version %s is available! " % str(version)
2093                if not standalone:
2094                    import webbrowser
2095                    if "download_url" in version_info:
2096                        webbrowser.open(version_info["download_url"])
2097                    else:
2098                        webbrowser.open(config.__download_page__)
2099                else:
2100                    msg += "See the help menu to download it."
2101                self.SetStatusText(msg)
2102            else:
2103                if not standalone:
2104                    msg = "You have the latest version"
2105                    msg += " of %s" % str(config.__appname__)
2106                    self.SetStatusText(msg)
2107        except:
2108            msg = "guiframe: could not get latest application"
2109            msg += " version number\n  %s" % sys.exc_value
2110            logger.error(msg)
2111            if not standalone:
2112                msg = "Could not connect to the application server."
2113                msg += " Please try again later."
2114                self.SetStatusText(msg)
2115
2116    def _onAcknowledge(self, evt):
2117        """
2118        Pop up the acknowledge dialog
2119
2120        :param evt: menu event
2121
2122        """
2123        if config._do_acknowledge:
2124            import sas.sasgui.guiframe.acknowledgebox as AcknowledgeBox
2125            dialog = AcknowledgeBox.DialogAcknowledge(None, -1, "")
2126            dialog.ShowModal()
2127
2128    def _onAbout(self, evt):
2129        """
2130        Pop up the about dialog
2131
2132        :param evt: menu event
2133
2134        """
2135        if config._do_aboutbox:
2136            import sas.sasgui.guiframe.aboutbox as AboutBox
2137            dialog = AboutBox.DialogAbout(None, -1, "")
2138            dialog.ShowModal()
2139
2140    def _onTutorial(self, evt):
2141        """
2142        Pop up the tutorial dialog
2143
2144        :param evt: menu event
2145
2146        """
2147        if config._do_tutorial:
2148            path = config.TUTORIAL_PATH
2149            if IS_WIN:
2150                try:
2151                    from sas.sasgui.guiframe.pdfview import PDFFrame
2152                    dialog = PDFFrame(None, -1, "Tutorial", path)
2153                    # put icon
2154                    self.put_icon(dialog)
2155                    dialog.Show(True)
2156                except:
2157                    logger.error("Error in _onTutorial: %s" % sys.exc_value)
2158                    try:
2159                        # Try an alternate method
2160                        logger.error(
2161                            "Could not open the tutorial pdf, trying xhtml2pdf")
2162                        from xhtml2pdf import pisa
2163                        pisa.startViewer(path)
2164                    except:
2165                        logger.error(
2166                            "Could not open the tutorial pdf with xhtml2pdf")
2167                        msg = "This feature requires 'PDF Viewer'\n"
2168                        wx.MessageBox(msg, 'Error')
2169            else:
2170                try:
2171                    command = "open '%s'" % path
2172                    os.system(command)
2173                except:
2174                    try:
2175                        # Try an alternate method
2176                        logger.error(
2177                            "Could not open the tutorial pdf, trying xhtml2pdf")
2178                        from xhtml2pdf import pisa
2179                        pisa.startViewer(path)
2180                    except:
2181                        logger.error(
2182                            "Could not open the tutorial pdf with xhtml2pdf")
2183                        msg = "This feature requires the Preview application\n"
2184                        wx.MessageBox(msg, 'Error')
2185
2186    def _onSphinxDocs(self, evt):
2187        """
2188        Bring up Sphinx Documentation at top level whenever the menu item
2189        'documentation' is clicked. Calls DocumentationWindow with the top
2190        level path of "index.html"
2191
2192        :param evt: menu event
2193        """
2194        # Running SasView "in-place" using run.py means the docs will be in a
2195        # different place than they would otherwise.
2196        from documentation_window import DocumentationWindow
2197        _TreeLocation = "user/user.html"
2198        DocumentationWindow(self, -1, _TreeLocation, "",
2199                            "SasView Documentation")
2200
2201    def set_manager(self, manager):
2202        """
2203        Sets the application manager for this frame
2204
2205        :param manager: frame manager
2206        """
2207        self.app_manager = manager
2208
2209    def post_init(self):
2210        """
2211        This initialization method is called after the GUI
2212        has been created and all plug-ins loaded. It calls
2213        the post_init() method of each plug-in (if it exists)
2214        so that final initialization can be done.
2215        """
2216        for item in self.plugins:
2217            if hasattr(item, "post_init"):
2218                item.post_init()
2219
2220    def set_perspective(self, panels):
2221        """
2222        Sets the perspective of the GUI.
2223        Opens all the panels in the list, and closes
2224        all the others.
2225
2226        :param panels: list of panels
2227        """
2228        for item in self.panels.keys():
2229            # Check whether this is a sticky panel
2230            if hasattr(self.panels[item], "ALWAYS_ON"):
2231                if self.panels[item].ALWAYS_ON:
2232                    continue
2233            if self.panels[item] is None:
2234                continue
2235            if self.panels[item].window_name in panels:
2236                frame = self.panels[item].get_frame()
2237                if not frame.IsShown():
2238                    frame.Show(True)
2239            else:
2240                # always show the data panel if enable
2241                style = self.__gui_style & GUIFRAME.MANAGER_ON
2242                if (style == GUIFRAME.MANAGER_ON) \
2243                        and self.panels[item] == self._data_panel:
2244                    if 'data_panel' in self.panels.keys():
2245                        frame = self.panels['data_panel'].get_frame()
2246                        if frame is None:
2247                            continue
2248                        flag = frame.IsShown()
2249                        frame.Show(flag)
2250                else:
2251                    frame = self.panels[item].get_frame()
2252                    if frame is None:
2253                        continue
2254
2255                    if frame.IsShown():
2256                        frame.Show(False)
2257
2258    def show_data_panel(self, event=None, action=True):
2259        """
2260        show the data panel
2261        """
2262        if self._data_panel_menu is None:
2263            return
2264        label = self._data_panel_menu.GetText()
2265        pane = self.panels["data_panel"]
2266        frame = pane.get_frame()
2267        if label == 'Show Data Explorer':
2268            if action:
2269                frame.Show(True)
2270            self.__gui_style = self.__gui_style | GUIFRAME.MANAGER_ON
2271            self._data_panel_menu.SetText('Hide Data Explorer')
2272        else:
2273            if action:
2274                frame.Show(False)
2275            self.__gui_style = self.__gui_style & (~GUIFRAME.MANAGER_ON)
2276            self._data_panel_menu.SetText('Show Data Explorer')
2277
2278    def add_data_helper(self, data_list):
2279        """
2280        """
2281        if self._data_manager is not None:
2282            self._data_manager.add_data(data_list)
2283
2284    def add_data(self, data_list):
2285        """
2286        receive a dictionary of data from loader
2287        store them its data manager if possible
2288        send to data the current active perspective if the data panel
2289        is not active.
2290        :param data_list: dictionary of data's ID and value Data
2291        """
2292        # Store data into manager
2293        self.add_data_helper(data_list)
2294        # set data in the data panel
2295        if self._data_panel is not None:
2296            data_state = self._data_manager.get_data_state(data_list.keys())
2297            self._data_panel.load_data_list(data_state)
2298        # if the data panel is shown wait for the user to press a button
2299        # to send data to the current perspective. if the panel is not
2300        # show  automatically send the data to the current perspective
2301        style = self.__gui_style & GUIFRAME.MANAGER_ON
2302        if style == GUIFRAME.MANAGER_ON:
2303            # wait for button press from the data panel to set_data
2304            if self._data_panel is not None:
2305                self._data_panel.frame.Show(True)
2306        else:
2307            # automatically send that to the current perspective
2308            self.set_data(data_id=data_list.keys())
2309
2310    def set_data(self, data_id, theory_id=None):
2311        """
2312        set data to current perspective
2313        """
2314        list_data, _ = self._data_manager.get_by_id(data_id)
2315        if self._current_perspective is not None:
2316            self._current_perspective.set_data(list_data.values())
2317
2318        else:
2319            msg = "Guiframe does not have a current perspective"
2320            logger.info(msg)
2321
2322    def set_theory(self, state_id, theory_id=None):
2323        """
2324        """
2325        _, list_theory = self._data_manager.get_by_id(theory_id)
2326        if self._current_perspective is not None:
2327            try:
2328                self._current_perspective.set_theory(list_theory.values())
2329            except:
2330                msg = "Guiframe set_theory: \n" + str(sys.exc_value)
2331                logger.info(msg)
2332                wx.PostEvent(self, StatusEvent(status=msg, info="error"))
2333        else:
2334            msg = "Guiframe does not have a current perspective"
2335            logger.info(msg)
2336
2337    def plot_data(self, state_id, data_id=None,
2338                  theory_id=None, append=False):
2339        """
2340        send a list of data to plot
2341        """
2342        data_list, _ = self._data_manager.get_by_id(data_id)
2343        _, temp_list_theory = self._data_manager.get_by_id(theory_id)
2344        total_plot_list = data_list.values()
2345        for item in temp_list_theory.values():
2346            theory_data, theory_state = item
2347            total_plot_list.append(theory_data)
2348        GROUP_ID = wx.NewId()
2349        for new_plot in total_plot_list:
2350            if append:
2351                if self.panel_on_focus is None:
2352                    message = "cannot append plot. No plot panel on focus!"
2353                    message += "please click on any available plot to set focus"
2354                    wx.PostEvent(self, StatusEvent(status=message,
2355                                                   info='warning'))
2356                    return
2357                else:
2358                    if self.enable_add_data(new_plot):
2359                        new_plot.group_id = self.panel_on_focus.group_id
2360                    else:
2361                        message = "Only 1D Data can be append to"
2362                        message += " plot panel containing 1D data.\n"
2363                        message += "%s not be appended.\n" % str(new_plot.name)
2364                        message += "try new plot option.\n"
2365                        wx.PostEvent(self, StatusEvent(status=message,
2366                                                       info='warning'))
2367            else:
2368                # if not append then new plot
2369                from sas.sasgui.guiframe.dataFitting import Data2D
2370                if issubclass(Data2D, new_plot.__class__):
2371                    # for 2 D always plot in a separated new plot
2372                    new_plot.group_id = wx.NewId()
2373                else:
2374                    # plot all 1D in a new plot
2375                    new_plot.group_id = GROUP_ID
2376            title = "PLOT " + str(new_plot.title)
2377            wx.PostEvent(self, NewPlotEvent(plot=new_plot,
2378                                            title=title,
2379                                            group_id=new_plot.group_id))
2380
2381    def remove_data(self, data_id, theory_id=None):
2382        """
2383        Delete data state if data_id is provide
2384        delete theory created with data of id data_id if theory_id is provide
2385        if delete all true: delete the all state
2386        else delete theory
2387        """
2388        temp = data_id + theory_id
2389        for plug in self.plugins:
2390            plug.delete_data(temp)
2391        data_list, _ = self._data_manager.get_by_id(data_id)
2392        _, temp_list_theory = self._data_manager.get_by_id(theory_id)
2393        total_plot_list = data_list.values()
2394        for item in temp_list_theory.values():
2395            theory_data, theory_state = item
2396            total_plot_list.append(theory_data)
2397        for new_plot in total_plot_list:
2398            for group_id in new_plot.list_group_id:
2399                wx.PostEvent(self, NewPlotEvent(id=new_plot.id,
2400                                                group_id=group_id,
2401                                                action='remove'))
2402                wx.CallAfter(self._remove_res_plot, new_plot.id)
2403        self._data_manager.delete_data(data_id=data_id,
2404                                       theory_id=theory_id)
2405
2406    def _remove_res_plot(self, id):
2407        """
2408        Try to remove corresponding res plot
2409
2410        : param id: id of the data
2411        """
2412        try:
2413            wx.PostEvent(self, NewPlotEvent(id=("res" + str(id)),
2414                                            group_id=("res" + str(id)),
2415                                            action='remove'))
2416        except:
2417            logger.error(sys.exc_value)
2418
2419    def save_data1d(self, data, fname):
2420        """
2421        Save data dialog
2422        """
2423        default_name = fname
2424        wildcard = "Text files (*.txt)|*.txt|"\
2425                    "CanSAS 1D files (*.xml)|*.xml|"\
2426                     "NXcanSAS files (*.h5)|*.h5|"
2427        options = {0: ".txt",
2428                   1: ".xml",
2429                   2: ".h5"}
2430        dlg = wx.FileDialog(self, "Choose a file",
2431                            self._default_save_location,
2432                            default_name, wildcard, wx.SAVE)
2433
2434        if dlg.ShowModal() == wx.ID_OK:
2435            path = dlg.GetPath()
2436            # ext_num = 0 for .txt, ext_num = 1 for .xml
2437            # This is MAC Fix
2438            ext_num = dlg.GetFilterIndex()
2439
2440            ext_format = options[ext_num]
2441            path = os.path.splitext(path)[0] + ext_format
2442            mypath = os.path.basename(path)
2443            fName = os.path.splitext(path)[0] + ext_format
2444
2445            if os.path.splitext(mypath)[1].lower() == options[0]:
2446                # Make sure the ext included in the file name
2447                # especially on MAC
2448                self._onsaveTXT(data, fName)
2449            elif os.path.splitext(mypath)[1].lower() == options[1]:
2450                # Make sure the ext included in the file name
2451                # especially on MAC
2452                # Instantiate a loader
2453                loader = Loader()
2454                loader.save(fName, data, ext_format)
2455            elif os.path.splitext(mypath)[1].lower() == options[2]:
2456                nxcansaswriter = NXcanSASWriter()
2457                nxcansaswriter.write([data], fName)
2458            try:
2459                self._default_save_location = os.path.dirname(path)
2460            except:
2461                pass
2462        dlg.Destroy()
2463
2464    def _onsaveTXT(self, data, path):
2465        """
2466        Save file as txt
2467
2468        .. todo:: Refactor and remove this method. See 'TODO' in _onSave.
2469        """
2470        if path is not None:
2471            out = open(path, 'w')
2472            has_errors = True
2473            if data.dy is None or data.dy == []:
2474                has_errors = False
2475            # Sanity check
2476            if has_errors:
2477                try:
2478                    if len(data.y) != len(data.dy):
2479                        has_errors = False
2480                except:
2481                    has_errors = False
2482            if has_errors:
2483                if data.dx is not None and data.dx != []:
2484                    out.write("<X>   <Y>   <dY>   <dX>\n")
2485                else:
2486                    out.write("<X>   <Y>   <dY>\n")
2487            else:
2488                out.write("<X>   <Y>\n")
2489
2490            for i in range(len(data.x)):
2491                if has_errors:
2492                    if data.dx is not None and data.dx != []:
2493                        if data.dx[i] is not None:
2494                            out.write("%g  %g  %g  %g\n" % (data.x[i],
2495                                                            data.y[i],
2496                                                            data.dy[i],
2497                                                            data.dx[i]))
2498                        else:
2499                            out.write("%g  %g  %g\n" % (data.x[i],
2500                                                        data.y[i],
2501                                                        data.dy[i]))
2502                    else:
2503                        out.write("%g  %g  %g\n" % (data.x[i],
2504                                                    data.y[i],
2505                                                    data.dy[i]))
2506                else:
2507                    out.write("%g  %g\n" % (data.x[i],
2508                                            data.y[i]))
2509            out.close()
2510
2511    def show_data1d(self, data, name):
2512        """
2513        Show data dialog
2514        """
2515        try:
2516            xmin = min(data.x)
2517            ymin = min(data.y)
2518        except:
2519            msg = "Unable to find min/max of \n data named %s" % \
2520                        data.filename
2521            wx.PostEvent(self, StatusEvent(status=msg,
2522                                           info="error"))
2523            raise ValueError, msg
2524        # text = str(data)
2525        text = data.__str__()
2526        text += 'Data Min Max:\n'
2527        text += 'X_min = %s:  X_max = %s\n' % (xmin, max(data.x))
2528        text += 'Y_min = %s:  Y_max = %s\n' % (ymin, max(data.y))
2529        if data.dy is not None:
2530            text += 'dY_min = %s:  dY_max = %s\n' % (min(data.dy), max(data.dy))
2531        text += '\nData Points:\n'
2532        x_st = "X"
2533        for index in range(len(data.x)):
2534            if data.dy is not None and len(data.dy) > index:
2535                dy_val = data.dy[index]
2536            else:
2537                dy_val = 0.0
2538            if data.dx is not None and len(data.dx) > index:
2539                dx_val = data.dx[index]
2540            else:
2541                dx_val = 0.0
2542            if data.dxl is not None and len(data.dxl) > index:
2543                if index == 0:
2544                    x_st = "Xl"
2545                dx_val = data.dxl[index]
2546            elif data.dxw is not None and len(data.dxw) > index:
2547                if index == 0:
2548                    x_st = "Xw"
2549                dx_val = data.dxw[index]
2550
2551            if index == 0:
2552                text += "<index> \t<X> \t<Y> \t<dY> \t<d%s>\n" % x_st
2553            text += "%s \t%s \t%s \t%s \t%s\n" % (index,
2554                                                  data.x[index],
2555                                                  data.y[index],
2556                                                  dy_val,
2557                                                  dx_val)
2558        from pdfview import TextFrame
2559        frame = TextFrame(None, -1, "Data Info: %s" % data.name, text)
2560        # put icon
2561        self.put_icon(frame)
2562        frame.Show(True)
2563
2564    def save_data2d(self, data, fname):
2565        """
2566        Save data2d dialog
2567        """
2568        default_name = fname
2569        wildcard = "IGOR/DAT 2D file in Q_map (*.dat)|*.DAT"
2570        dlg = wx.FileDialog(self, "Choose a file",
2571                            self._default_save_location,
2572                            default_name, wildcard, wx.SAVE)
2573
2574        if dlg.ShowModal() == wx.ID_OK:
2575            path = dlg.GetPath()
2576            # ext_num = 0 for .txt, ext_num = 1 for .xml
2577            # This is MAC Fix
2578            ext_num = dlg.GetFilterIndex()
2579            if ext_num == 0:
2580                ext_format = '.dat'
2581            else:
2582                ext_format = ''
2583            path = os.path.splitext(path)[0] + ext_format
2584            mypath = os.path.basename(path)
2585
2586            # Instantiate a loader
2587            loader = Loader()
2588
2589            ext_format = ".dat"
2590            if os.path.splitext(mypath)[1].lower() == ext_format:
2591                # Make sure the ext included in the file name
2592                # especially on MAC
2593                fileName = os.path.splitext(path)[0] + ext_format
2594                loader.save(fileName, data, ext_format)
2595            try:
2596                self._default_save_location = os.path.dirname(path)
2597            except:
2598                pass
2599        dlg.Destroy()
2600
2601    def show_data2d(self, data, name):
2602        """
2603        Show data dialog
2604        """
2605        wx.PostEvent(self, StatusEvent(status="Gathering Data2D Info.",
2606                                       type='start'))
2607        text = data.__str__()
2608        text += 'Data Min Max:\n'
2609        text += 'I_min = %s\n' % min(data.data)
2610        text += 'I_max = %s\n\n' % max(data.data)
2611        text += 'Data (First 2501) Points:\n'
2612        text += 'Data columns include err(I).\n'
2613        text += 'ASCII data starts here.\n'
2614        text += "<index> \t<Qx> \t<Qy> \t<I> \t<dI> \t<dQparal> \t<dQperp>\n"
2615        di_val = 0.0
2616        dx_val = 0.0
2617        dy_val = 0.0
2618        len_data = len(data.qx_data)
2619        for index in xrange(0, len_data):
2620            x_val = data.qx_data[index]
2621            y_val = data.qy_data[index]
2622            i_val = data.data[index]
2623            if data.err_data is not None:
2624                di_val = data.err_data[index]
2625            if data.dqx_data is not None:
2626                dx_val = data.dqx_data[index]
2627            if data.dqy_data is not None:
2628                dy_val = data.dqy_data[index]
2629
2630            text += "%s \t%s \t%s \t%s \t%s \t%s \t%s\n" % (index,
2631                                                            x_val,
2632                                                            y_val,
2633                                                            i_val,
2634                                                            di_val,
2635                                                            dx_val,
2636                                                            dy_val)
2637            # Takes too long time for typical data2d: Break here
2638            if index >= 2500:
2639                text += ".............\n"
2640                break
2641
2642        from pdfview import TextFrame
2643        frame = TextFrame(None, -1, "Data Info: %s" % data.name, text)
2644        # put icon
2645        self.put_icon(frame)
2646        frame.Show(True)
2647        wx.PostEvent(self, StatusEvent(status="Data2D Info Displayed",
2648                                       type='stop'))
2649
2650    def set_current_perspective(self, perspective):
2651        """
2652        set the current active perspective
2653        """
2654        self._current_perspective = perspective
2655        name = "No current analysis selected"
2656        if self._current_perspective is not None:
2657            self._add_current_plugin_menu()
2658            for panel in self.panels.values():
2659                if hasattr(panel, 'CENTER_PANE') and panel.CENTER_PANE:
2660                    for name in self._current_perspective.get_perspective():
2661                        frame = panel.get_frame()
2662                        if frame is not None:
2663                            if name == panel.window_name:
2664                                panel.on_set_focus(event=None)
2665                                frame.Show(True)
2666                            else:
2667                                frame.Show(False)
2668            name = self._current_perspective.sub_menu
2669            if self._data_panel is not None:
2670                self._data_panel.set_active_perspective(name)
2671                self._check_applications_menu()
2672            # Set the SasView title
2673            self._set_title_name(name)
2674
2675    def _set_title_name(self, name):
2676        """
2677        Set the SasView title w/ the current application name
2678
2679        : param name: application name [string]
2680        """
2681        # Set SanView Window title w/ application anme
2682        title = self.title + "  - " + name + " -"
2683        self.SetTitle(title)
2684
2685    def _check_applications_menu(self):
2686        """
2687        check the menu of the current application
2688        """
2689        if self._applications_menu is not None:
2690            for menu in self._applications_menu.GetMenuItems():
2691                if self._current_perspective is not None:
2692                    name = self._current_perspective.sub_menu
2693                    if menu.IsCheckable():
2694                        if menu.GetLabel() == name:
2695                            menu.Check(True)
2696                        else:
2697                            menu.Check(False)
2698
2699    def enable_add_data(self, new_plot):
2700        """
2701        Enable append data on a plot panel
2702        """
2703
2704        if self.panel_on_focus \
2705                not in self._plotting_plugin.plot_panels.values():
2706            return
2707        check = "Theory1D"
2708        is_theory = len(self.panel_on_focus.plots) <= 1 and \
2709            self.panel_on_focus.plots.values()[0].__class__.__name__ == check
2710
2711        is_data2d = hasattr(new_plot, 'data')
2712
2713        is_data1d = self.panel_on_focus.__class__.__name__ == "ModelPanel1D"\
2714            and self.panel_on_focus.group_id is not None
2715        has_meta_data = hasattr(new_plot, 'meta_data')
2716
2717        # disable_add_data if the data is being recovered from  a saved state
2718        is_state_data = False
2719        if has_meta_data:
2720            if 'invstate' in new_plot.meta_data:
2721                is_state_data = True
2722            if 'prstate' in new_plot.meta_data:
2723                is_state_data = True
2724            if 'fitstate' in new_plot.meta_data:
2725                is_state_data = True
2726
2727        return is_data1d and not is_data2d and not is_theory \
2728               and not is_state_data
2729
2730    def check_multimode(self, perspective=None):
2731        """
2732        Check the perspective have batch mode capablitity
2733        """
2734        if perspective is None or self._data_panel is None:
2735            return
2736        flag = perspective.get_batch_capable()
2737        flag_on = perspective.batch_on
2738        if flag:
2739            self._data_panel.rb_single_mode.SetValue(not flag_on)
2740            self._data_panel.rb_batch_mode.SetValue(flag_on)
2741        else:
2742            self._data_panel.rb_single_mode.SetValue(True)
2743            self._data_panel.rb_batch_mode.SetValue(False)
2744        self._data_panel.rb_single_mode.Enable(flag)
2745        self._data_panel.rb_batch_mode.Enable(flag)
2746
2747    def enable_edit_menu(self):
2748        """
2749        enable menu item under edit menu depending on the panel on focus
2750        """
2751        if self.cpanel_on_focus is not None and self._edit_menu is not None:
2752            flag = self.cpanel_on_focus.get_undo_flag()
2753            self._edit_menu.Enable(GUIFRAME_ID.UNDO_ID, flag)
2754            flag = self.cpanel_on_focus.get_redo_flag()
2755            self._edit_menu.Enable(GUIFRAME_ID.REDO_ID, flag)
2756            flag = self.cpanel_on_focus.get_copy_flag()
2757            self._edit_menu.Enable(GUIFRAME_ID.COPY_ID, flag)
2758            flag = self.cpanel_on_focus.get_paste_flag()
2759            self._edit_menu.Enable(GUIFRAME_ID.PASTE_ID, flag)
2760
2761            # Copy menu
2762            flag = self.cpanel_on_focus.get_copy_flag()
2763            self._edit_menu_copyas.Enable(GUIFRAME_ID.COPYEX_ID, flag)
2764            self._edit_menu_copyas.Enable(GUIFRAME_ID.COPYLAT_ID, flag)
2765
2766            flag = self.cpanel_on_focus.get_preview_flag()
2767            self._edit_menu.Enable(GUIFRAME_ID.PREVIEW_ID, flag)
2768            flag = self.cpanel_on_focus.get_reset_flag()
2769            self._edit_menu.Enable(GUIFRAME_ID.RESET_ID, flag)
2770        else:
2771            flag = False
2772            self._edit_menu.Enable(GUIFRAME_ID.UNDO_ID, flag)
2773            self._edit_menu.Enable(GUIFRAME_ID.REDO_ID, flag)
2774            self._edit_menu.Enable(GUIFRAME_ID.COPY_ID, flag)
2775            self._edit_menu.Enable(GUIFRAME_ID.PASTE_ID, flag)
2776            self._edit_menu.Enable(GUIFRAME_ID.PREVIEW_ID, flag)
2777            self._edit_menu.Enable(GUIFRAME_ID.RESET_ID, flag)
2778
2779    def on_undo_panel(self, event=None):
2780        """
2781        undo previous action of the last panel on focus if possible
2782        """
2783        if self.cpanel_on_focus is not None:
2784            self.cpanel_on_focus.on_undo(event)
2785
2786    def on_redo_panel(self, event=None):
2787        """
2788        redo the last cancel action done on the last panel on focus
2789        """
2790        if self.cpanel_on_focus is not None:
2791            self.cpanel_on_focus.on_redo(event)
2792
2793    def on_copy_panel(self, event=None):
2794        """
2795        copy the last panel on focus if possible
2796        """
2797        if self.cpanel_on_focus is not None:
2798            self.cpanel_on_focus.on_copy(event)
2799
2800    def on_paste_panel(self, event=None):
2801        """
2802        paste clipboard to the last panel on focus
2803        """
2804        if self.cpanel_on_focus is not None:
2805            self.cpanel_on_focus.on_paste(event)
2806
2807    def on_bookmark_panel(self, event=None):
2808        """
2809        bookmark panel
2810        """
2811        if self.cpanel_on_focus is not None:
2812            self.cpanel_on_focus.on_bookmark(event)
2813
2814    def append_bookmark(self, event=None):
2815        """
2816        Bookmark available information of the panel on focus
2817        """
2818        self._toolbar.append_bookmark(event)
2819
2820    def on_save_panel(self, event=None):
2821        """
2822        save possible information on the current panel
2823        """
2824        if self.cpanel_on_focus is not None:
2825            self.cpanel_on_focus.on_save(event)
2826
2827    def on_preview_panel(self, event=None):
2828        """
2829        preview information on the panel on focus
2830        """
2831        if self.cpanel_on_focus is not None:
2832            self.cpanel_on_focus.on_preview(event)
2833
2834    def on_print_panel(self, event=None):
2835        """
2836        print available information on the last panel on focus
2837        """
2838        if self.cpanel_on_focus is not None:
2839            self.cpanel_on_focus.on_print(event)
2840
2841    def on_zoom_panel(self, event=None):
2842        """
2843        zoom on the current panel if possible
2844        """
2845        if self.cpanel_on_focus is not None:
2846            self.cpanel_on_focus.on_zoom(event)
2847
2848    def on_zoom_in_panel(self, event=None):
2849        """
2850        zoom in of the panel on focus
2851        """
2852        if self.cpanel_on_focus is not None:
2853            self.cpanel_on_focus.on_zoom_in(event)
2854
2855    def on_zoom_out_panel(self, event=None):
2856        """
2857        zoom out on the panel on focus
2858        """
2859        if self.cpanel_on_focus is not None:
2860            self.cpanel_on_focus.on_zoom_out(event)
2861
2862    def on_drag_panel(self, event=None):
2863        """
2864        drag apply to the panel on focus
2865        """
2866        if self.cpanel_on_focus is not None:
2867            self.cpanel_on_focus.on_drag(event)
2868
2869    def on_reset_panel(self, event=None):
2870        """
2871        reset the current panel
2872        """
2873        if self.cpanel_on_focus is not None:
2874            self.cpanel_on_focus.on_reset(event)
2875
2876    def on_change_caption(self, name, old_caption, new_caption):
2877        """
2878        Change the panel caption
2879
2880        :param name: window_name of the pane
2881        :param old_caption: current caption [string]
2882        :param new_caption: new caption [string]
2883        """
2884        # wx.aui.AuiPaneInfo
2885        pane_info = self.get_paneinfo(old_caption)
2886        # update the data_panel.cb_plotpanel
2887        if 'data_panel' in self.panels.keys():
2888            # remove from data_panel combobox
2889            data_panel = self.panels["data_panel"]
2890            if data_panel.cb_plotpanel is not None:
2891                # Check if any panel has the same caption
2892                has_newstring = data_panel.cb_plotpanel.FindString(
2893                    str(new_caption))
2894                caption = new_caption
2895                if has_newstring != wx.NOT_FOUND:
2896                    captions = self._get_plotpanel_captions()
2897                    # Append nummber
2898                    inc = 1
2899                    # FIXME: fix this terrible loop
2900                    while (1):
2901                        caption = new_caption + '_%s' % str(inc)
2902                        if caption not in captions:
2903                            break
2904                        inc += 1
2905                    # notify to users
2906                    msg = "Found Same Title: Added '_%s'" % str(inc)
2907                    wx.PostEvent(self, StatusEvent(status=msg))
2908                # update data_panel cb
2909                pos = data_panel.cb_plotpanel.FindString(str(old_caption))
2910                if pos != wx.NOT_FOUND:
2911                    data_panel.cb_plotpanel.SetString(pos, caption)
2912                    data_panel.cb_plotpanel.SetStringSelection(caption)
2913        # New Caption
2914        pane_info.SetTitle(caption)
2915        return caption
2916
2917    def get_paneinfo(self, name):
2918        """
2919        Get pane Caption from window_name
2920
2921        :param name: window_name in AuiPaneInfo
2922        :returns: AuiPaneInfo of the name
2923        """
2924        for panel in self.plot_panels.values():
2925            if panel.frame.GetTitle() == name:
2926                return panel.frame
2927        return None
2928
2929    def enable_undo(self):
2930        """
2931        enable undo related control
2932        """
2933        if self.cpanel_on_focus is not None:
2934            self._toolbar.enable_undo(self.cpanel_on_focus)
2935
2936    def enable_redo(self):
2937        """
2938        enable redo
2939        """
2940        if self.cpanel_on_focus is not None:
2941            self._toolbar.enable_redo(self.cpanel_on_focus)
2942
2943    def enable_copy(self):
2944        """
2945        enable copy related control
2946        """
2947        if self.cpanel_on_focus is not None:
2948            self._toolbar.enable_copy(self.cpanel_on_focus)
2949
2950    def enable_paste(self):
2951        """
2952        enable paste
2953        """
2954        if self.cpanel_on_focus is not None:
2955            self._toolbar.enable_paste(self.cpanel_on_focus)
2956
2957    def enable_bookmark(self):
2958        """
2959        Bookmark
2960        """
2961        if self.cpanel_on_focus is not None:
2962            self._toolbar.enable_bookmark(self.cpanel_on_focus)
2963
2964    def enable_save(self):
2965        """
2966        save
2967        """
2968        if self.cpanel_on_focus is not None:
2969            self._toolbar.enable_save(self.cpanel_on_focus)
2970
2971    def enable_preview(self):
2972        """
2973        preview
2974        """
2975        if self.cpanel_on_focus is not None:
2976            self._toolbar.enable_preview(self.cpanel_on_focus)
2977
2978    def enable_print(self):
2979        """
2980        print
2981        """
2982        if self.cpanel_on_focus is not None:
2983            self._toolbar.enable_print(self.cpanel_on_focus)
2984
2985    def enable_zoom(self):
2986        """
2987        zoom
2988        """
2989        if self.cpanel_on_focus is not None:
2990            self._toolbar.enable_zoom(self.panel_on_focus)
2991
2992    def enable_zoom_in(self):
2993        """
2994        zoom in
2995        """
2996        if self.cpanel_on_focus is not None:
2997            self._toolbar.enable_zoom_in(self.panel_on_focus)
2998
2999    def enable_zoom_out(self):
3000        """
3001        zoom out
3002        """
3003        if self.cpanel_on_focus is not None:
3004            self._toolbar.enable_zoom_out(self.panel_on_focus)
3005
3006    def enable_drag(self, event=None):
3007        """
3008        drag
3009        """
3010        # Not implemeted
3011
3012    def enable_reset(self):
3013        """
3014        reset the current panel
3015        """
3016        if self.cpanel_on_focus is not None:
3017            self._toolbar.enable_reset(self.panel_on_focus)
3018
3019    def get_toolbar_height(self):
3020        """
3021        """
3022        size_y = 0
3023        if self.GetToolBar() is not None and self.GetToolBar().IsShown():
3024            if not IS_LINUX:
3025                _, size_y = self.GetToolBar().GetSizeTuple()
3026        return size_y
3027
3028    def set_schedule_full_draw(self, panel=None, func='del'):
3029        """
3030        Add/subtract the schedule full draw list with the panel given
3031
3032        :param panel: plot panel
3033        :param func: append or del [string]
3034        """
3035
3036        # append this panel in the schedule list if not in yet
3037        if func == 'append':
3038            if panel not in self.schedule_full_draw_list:
3039                self.schedule_full_draw_list.append(panel)
3040        # remove this panel from schedule list
3041        elif func == 'del':
3042            if len(self.schedule_full_draw_list) > 0:
3043                if panel in self.schedule_full_draw_list:
3044                    self.schedule_full_draw_list.remove(panel)
3045
3046        # reset the schdule
3047        if len(self.schedule_full_draw_list) == 0:
3048            self.schedule = False
3049        else:
3050            self.schedule = True
3051
3052    def full_draw(self):
3053        """
3054        Draw the panels with axes in the schedule to full dwar list
3055        """
3056
3057        count = len(self.schedule_full_draw_list)
3058        # if not self.schedule:
3059        if count < 1:
3060            self.set_schedule(False)
3061            return
3062
3063        else:
3064            ind = 0
3065            # if any of the panel is shown do full_draw
3066            for panel in self.schedule_full_draw_list:
3067                ind += 1
3068                if panel.frame.IsShown():
3069                    break
3070                # otherwise, return
3071                if ind == count:
3072                    return
3073        # Simple redraw only for a panel shown
3074
3075        def f_draw(panel):
3076            """
3077            Draw A panel in the full draw list
3078            """
3079            try:
3080                # This checking of GetCapture is to stop redrawing
3081                # while any panel is capture.
3082                frame = panel.frame
3083
3084                if not frame.GetCapture():
3085                    # draw if possible
3086                    panel.set_resizing(False)
3087                    # panel.Show(True)
3088                    panel.draw_plot()
3089                # Check if the panel is not shown
3090                flag = frame.IsShown()
3091                frame.Show(flag)
3092            except:
3093                pass
3094
3095        # Draw all panels
3096        if count == 1:
3097            f_draw(self.schedule_full_draw_list[0])
3098        else:
3099            map(f_draw, self.schedule_full_draw_list)
3100        # Reset the attr
3101        if len(self.schedule_full_draw_list) == 0:
3102            self.set_schedule(False)
3103        else:
3104            self.set_schedule(True)
3105
3106    def set_schedule(self, schedule=False):
3107        """
3108        Set schedule
3109        """
3110        self.schedule = schedule
3111
3112    def get_schedule(self):
3113        """
3114        Get schedule
3115        """
3116        return self.schedule
3117
3118    def on_set_plot_focus(self, panel):
3119        """
3120        Set focus on a plot panel
3121        """
3122        if panel is None:
3123            return
3124        # self.set_plot_unfocus()
3125        panel.on_set_focus(None)
3126        # set focusing panel
3127        self.panel_on_focus = panel
3128        self.set_panel_on_focus(None)
3129
3130    def set_plot_unfocus(self):
3131        """
3132        Un focus all plot panels
3133        """
3134        for plot in self.plot_panels.values():
3135            plot.on_kill_focus(None)
3136
3137    def get_window_size(self):
3138        """
3139        Get window size
3140
3141        :returns: size
3142        :rtype: tuple
3143        """
3144        width, height = self.GetSizeTuple()
3145        if not IS_WIN:
3146            # Subtract toolbar height to get real window side
3147            if self._toolbar.IsShown():
3148                height -= 45
3149        return (width, height)
3150
3151    def _onDrawIdle(self, *args, **kwargs):
3152        """
3153        ReDraw with axes
3154        """
3155        try:
3156            # check if it is time to redraw
3157            if self.GetCapture() is None:
3158                # Draw plot, changes resizing too
3159                self.full_draw()
3160        except:
3161            pass
3162
3163        # restart idle
3164        self._redraw_idle(*args, **kwargs)
3165
3166    def _redraw_idle(self, *args, **kwargs):
3167        """
3168        Restart Idle
3169        """
3170        # restart idle
3171        self.idletimer.Restart(100 * TIME_FACTOR, *args, **kwargs)
3172
3173
3174class DefaultPanel(wx.Panel, PanelBase):
3175    """
3176    Defines the API for a panels to work with
3177    the GUI manager
3178    """
3179    # Internal nickname for the window, used by the AUI manager
3180    window_name = "default"
3181    # Name to appear on the window title bar
3182    window_caption = "Welcome panel"
3183    # Flag to tell the AUI manager to put this panel in the center pane
3184    CENTER_PANE = True
3185
3186    def __init__(self, parent, *args, **kwds):
3187        wx.Panel.__init__(self, parent, *args, **kwds)
3188        PanelBase.__init__(self, parent)
3189
3190
3191class SasViewApp(wx.App):
3192    """
3193    SasView application
3194    """
3195    def OnInit(self):
3196        """
3197        When initialised
3198        """
3199        pos, size, self.is_max = self.window_placement((GUIFRAME_WIDTH,
3200                                                        GUIFRAME_HEIGHT))
3201        self.frame = ViewerFrame(parent=None,
3202                                 title=APPLICATION_NAME,
3203                                 pos=pos,
3204                                 gui_style=DEFAULT_STYLE,
3205                                 size=size)
3206        self.frame.Hide()
3207        if not IS_WIN:
3208            self.frame.EnableCloseButton(False)
3209        self.s_screen = None
3210
3211        try:
3212            self.open_file()
3213        except:
3214            msg = "%s Could not load " % str(APPLICATION_NAME)
3215            msg += "input file from command line.\n"
3216            logger.error(msg)
3217        # Display a splash screen on top of the frame.
3218        try:
3219            if os.path.isfile(SPLASH_SCREEN_PATH):
3220                self.s_screen = \
3221                    self.display_splash_screen(parent=self.frame,
3222                                               path=SPLASH_SCREEN_PATH)
3223            else:
3224                self.frame.Show()
3225        except:
3226            if self.s_screen is not None:
3227                self.s_screen.Close()
3228            msg = "Cannot display splash screen\n"
3229            msg += str(sys.exc_value)
3230            logger.error(msg)
3231            self.frame.Show()
3232
3233        self.SetTopWindow(self.frame)
3234
3235        return True
3236
3237    def maximize_win(self):
3238        """
3239        Maximize the window after the frame shown
3240        """
3241        if self.is_max:
3242            if self.frame.IsShown():
3243                # Max window size
3244                self.frame.Maximize(self.is_max)
3245
3246    def open_file(self):
3247        """
3248        open a state file at the start of the application
3249        """
3250        input_file = None
3251        if len(sys.argv) >= 2:
3252            cmd = sys.argv[0].lower()
3253            basename = os.path.basename(cmd)
3254            app_base = str(APPLICATION_NAME).lower()
3255            if os.path.isfile(cmd) or basename.lower() == app_base:
3256                app_py = app_base + '.py'
3257                app_exe = app_base + '.exe'
3258                app_app = app_base + '.app'
3259                if basename.lower() in [app_py, app_exe, app_app, app_base]:
3260                    data_base = sys.argv[1]
3261                    input_file = os.path.normpath(os.path.join(get_app_dir(),
3262                                                               data_base))
3263        if input_file is None:
3264            return
3265        if self.frame is not None:
3266            self.frame.set_input_file(input_file=input_file)
3267
3268    def clean_plugin_models(self, path):
3269        """
3270        Delete plugin models  in app folder
3271
3272        :param path: path of the plugin_models folder in app
3273        """
3274        # do it only the first time app loaded
3275        # delete unused model folder
3276        model_folder = os.path.join(get_app_dir(), path)
3277        if os.path.exists(model_folder) and os.path.isdir(model_folder):
3278            if len(os.listdir(model_folder)) > 0:
3279                try:
3280                    for filename in os.listdir(model_folder):
3281                        file_path = os.path.join(model_folder, filename)
3282                        if os.path.isfile(file_path):
3283                            os.remove(file_path)
3284                except:
3285                    logger.error("gui_manager.clean_plugin_models:\n  %s"
3286                                  % sys.exc_value)
3287
3288    def set_manager(self, manager):
3289        """
3290        Sets a reference to the application manager
3291        of the GUI manager (Frame)
3292        """
3293        self.frame.set_manager(manager)
3294
3295    def build_gui(self):
3296        """
3297        Build the GUI
3298        """
3299        # try to load file at the start
3300        self.open_file()
3301        self.frame.build_gui()
3302
3303    def set_welcome_panel(self, panel_class):
3304        """
3305        Set the welcome panel
3306
3307        :param panel_class: class of the welcome panel to be instantiated
3308
3309        """
3310        self.frame.welcome_panel_class = panel_class
3311
3312    def add_perspective(self, perspective):
3313        """
3314        Manually add a perspective to the application GUI
3315        """
3316        self.frame.add_perspective(perspective)
3317
3318    def window_placement(self, size):
3319        """
3320        Determines the position and size of the application frame such that it
3321        fits on the user's screen without obstructing (or being obstructed by)
3322        the Windows task bar.  The maximum initial size in pixels is bounded by
3323        WIDTH x HEIGHT.  For most monitors, the application
3324        will be centered on the screen; for very large monitors it will be
3325        placed on the left side of the screen.
3326        """
3327        is_maximized = False
3328        # Get size of screen without
3329        for screenCount in range(wx.Display().GetCount()):
3330            screen = wx.Display(screenCount)
3331            if screen.IsPrimary():
3332                displayRect = screen.GetClientArea()
3333                break
3334
3335        posX, posY, displayWidth, displayHeight = displayRect
3336        customWidth, customHeight = size
3337
3338        # If the custom size is default, set 90% of the screen size
3339        if customWidth <= 0 and customHeight <= 0:
3340            if customWidth == 0 and customHeight == 0:
3341                is_maximized = True
3342            customWidth = displayWidth * 0.9
3343            customHeight = displayHeight * 0.9
3344        else:
3345            # If the custom screen is bigger than the
3346            # window screen than make maximum size
3347            if customWidth > displayWidth:
3348                customWidth = displayWidth
3349            if customHeight > displayHeight:
3350                customHeight = displayHeight
3351
3352        # Note that when running Linux and using an Xming (X11) server on a PC
3353        # with a dual  monitor configuration, the reported display size may be
3354        # that of both monitors combined with an incorrect display count of 1.
3355        # To avoid displaying this app across both monitors, we check for
3356        # screen 'too big'.  If so, we assume a smaller width which means the
3357        # application will be placed towards the left hand side of the screen.
3358
3359        # If dual screen registered as 1 screen. Make width half.
3360        # MAC just follows the default behavior of pos
3361        if IS_WIN:
3362            if displayWidth > (displayHeight * 2):
3363                if customWidth == displayWidth:
3364                    customWidth = displayWidth / 2
3365                # and set the position to be the corner of the screen.
3366                posX = 0
3367                posY = 0
3368
3369            # Make the position the middle of the screen. (Not 0,0)
3370            else:
3371                posX = (displayWidth - customWidth) / 2
3372                posY = (displayHeight - customHeight) / 2
3373        # Return the suggested position and size for the application frame.
3374        return (posX, posY), (customWidth, customHeight), is_maximized
3375
3376    def display_splash_screen(self, parent,
3377                              path=SPLASH_SCREEN_PATH):
3378        """Displays the splash screen.  It will exactly cover the main frame."""
3379
3380        # Prepare the picture.  On a 2GHz intel cpu, this takes about a second.
3381        image = wx.Image(path, wx.BITMAP_TYPE_PNG)
3382        image.Rescale(SPLASH_SCREEN_WIDTH,
3383                      SPLASH_SCREEN_HEIGHT, wx.IMAGE_QUALITY_HIGH)
3384        bm = image.ConvertToBitmap()
3385
3386        # Create and show the splash screen.  It will disappear only when the
3387        # program has entered the event loop AND either the timeout has expired
3388        # or the user has left clicked on the screen.  Thus any processing
3389        # performed in this routine (including sleeping) or processing in the
3390        # calling routine (including doing imports) will prevent the splash
3391        # screen from disappearing.
3392        #
3393        # Note that on Linux, the timeout appears to occur immediately in which
3394        # case the splash screen disappears upon entering the event loop.
3395        s_screen = wx.SplashScreen(bitmap=bm,
3396                                   splashStyle=(wx.SPLASH_TIMEOUT |
3397                                                wx.SPLASH_CENTRE_ON_SCREEN),
3398                                   style=(wx.SIMPLE_BORDER |
3399                                          wx.FRAME_NO_TASKBAR |
3400                                          wx.FRAME_FLOAT_ON_PARENT),
3401                                   milliseconds=SS_MAX_DISPLAY_TIME,
3402                                   parent=parent,
3403                                   id=wx.ID_ANY)
3404        from sas.sasgui.guiframe.gui_statusbar import SPageStatusbar
3405        statusBar = SPageStatusbar(s_screen)
3406        s_screen.SetStatusBar(statusBar)
3407        s_screen.Bind(wx.EVT_CLOSE, self.on_close_splash_screen)
3408        s_screen.Show()
3409        return s_screen
3410
3411    def on_close_splash_screen(self, event):
3412        """
3413        When the splash screen is closed.
3414        """
3415        self.frame.Show(True)
3416        event.Skip()
3417        self.maximize_win()
3418
3419
3420class MDIFrame(CHILD_FRAME):
3421    """
3422    Frame for panels
3423    """
3424    def __init__(self, parent, panel, title="Untitled", size=(300, 200)):
3425        """
3426        comment
3427        :param parent: parent panel/container
3428        """
3429        # Initialize the Frame object
3430        CHILD_FRAME.__init__(self, parent=parent, id=wx.ID_ANY,
3431                             title=title, size=size)
3432        self.parent = parent
3433        self.name = "Untitled"
3434        self.batch_on = self.parent.batch_on
3435        self.panel = panel
3436        if panel is not None:
3437            self.set_panel(panel)
3438        self.Show(False)
3439
3440    def show_data_panel(self, action):
3441        """
3442        Turns on the data panel
3443
3444        The the data panel is optional.  Most of its functions can be
3445        performed from the menu bar and from the plots.
3446        """
3447        self.parent.show_data_panel(action)
3448
3449    def set_panel(self, panel):
3450        """
3451        """
3452        self.panel = panel
3453        self.name = panel.window_name
3454        self.SetTitle(panel.window_caption)
3455        self.SetHelpText(panel.help_string)
3456        width, height = self.parent._get_panels_size(panel)
3457        if hasattr(panel, "CENTER_PANE") and panel.CENTER_PANE:
3458            width *= 0.6
3459        self.SetSize((width, height))
3460        self.parent.put_icon(self)
3461        self.Bind(wx.EVT_SET_FOCUS, self.set_panel_focus)
3462        self.Bind(wx.EVT_CLOSE, self.OnClose)
3463        self.Show(False)
3464
3465    def set_panel_focus(self, event):
3466        """
3467        """
3468        if self.parent.panel_on_focus != self.panel:
3469            self.panel.SetFocus()
3470            self.parent.panel_on_focus = self.panel
3471
3472    def OnClose(self, event):
3473        """
3474        On Close event
3475        """
3476        self.panel.on_close(event)
3477
3478if __name__ == "__main__":
3479    app = SasViewApp(0)
3480    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.