source: sasview/src/sas/guiframe/gui_manager.py @ c76bdc3

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since c76bdc3 was 091e71a2, checked in by Doucet, Mathieu <doucetm@…>, 10 years ago

Every time I look at this code I want to cry…

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