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

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

Create Release Notes option in Help Menu and popup box

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