source: sasview/src/sas/perspectives/invariant/invariant.py @ f89fa84

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 f89fa84 was b9a5f0e, checked in by krzywon, 10 years ago

90% complete with the conversion.

  • Property mode set to 100644
File size: 14.6 KB
RevLine 
[c128284]1
2
[d7a39e5]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.txt
10#
11#copyright 2009, University of Tennessee
12################################################################################
[d65a00a]13
14
[4e1c362]15import sys
[272d91e]16import wx
[4e1c362]17import copy
[4a2b054]18import logging
[79492222]19from sas.guiframe.gui_manager import MDIFrame
20from sas.guiframe.dataFitting import Data1D
21from sas.guiframe.events import NewPlotEvent
22from sas.guiframe.events import StatusEvent
23from sas.guiframe.gui_style import GUIFRAME_ID
[b9a5f0e]24from sas.perspectives.invariant.invariant_state import Reader as reader
[79492222]25from sas.dataloader.loader import Loader
[b9a5f0e]26from sas.perspectives.invariant.invariant_panel import InvariantPanel
[79492222]27from sas.guiframe.plugin_base import PluginBase
[d65a00a]28
29class Plugin(PluginBase):
[c128284]30    """
[d7a39e5]31    This class defines the interface for invariant Plugin class
[4e1c362]32    that can be used by the gui_manager.       
[c128284]33    """
34   
35    def __init__(self, standalone=False):
[d65a00a]36        PluginBase.__init__(self, name="Invariant", standalone=standalone)
[c128284]37       
38        #dictionary containing data name and error on dy of that data
39        self.err_dy = {}
[d65a00a]40       
[4da35bc]41        #default state objects
42        self.state_reader = None 
[35f2f49]43        self._extensions = '.inv'
[4da35bc]44        self.temp_state = None 
45        self.__data = None 
[d65a00a]46       
[4e1c362]47        # Log startup
48        logging.info("Invariant plug-in started")
[c128284]49       
[f29a433]50    def help(self, evt):
51        """
[d7a39e5]52        Show a general help dialog.
[f29a433]53        """
[64b4f0f]54        from help_panel import  HelpWindow
55        frame = HelpWindow(None, -1) 
56        if hasattr(frame, "IsIconized"):
57            if not frame.IsIconized():
58                try:
59                    icon = self.parent.GetIcon()
60                    frame.SetIcon(icon)
61                except:
62                    pass 
[f29a433]63        frame.Show(True)
64       
[35f2f49]65    def get_data(self):
66        """
67        """
68        return self.__data
69   
[c128284]70    def get_panels(self, parent):
71        """
[d7a39e5]72        Create and return the list of wx.Panels for your plug-in.
73        Define the plug-in perspective.
74       
75        Panels should inherit from DefaultPanel defined below,
76        or should present the same interface. They must define
77        "window_caption" and "window_name".
78       
79        :param parent: parent window
80       
81        :return: list of panels
[c128284]82        """
83        ## Save a reference to the parent
84        self.parent = parent
[ae84427]85        self.frame = MDIFrame(self.parent, None, 'None', (100, 200))     
86        self.invariant_panel = InvariantPanel(parent=self.frame)       
87        self.frame.set_panel(self.invariant_panel)
88        self._frame_set_helper()
[c128284]89        self.invariant_panel.set_manager(manager=self)
[4e1c362]90        self.perspective.append(self.invariant_panel.window_name) 
91        #Create reader when fitting panel are created
92        self.state_reader = reader(self.set_state)   
93        #append that reader to list of available reader
94        loader = Loader()
95        loader.associate_file_reader(".inv", self.state_reader)
[ca430a0]96        #loader.associate_file_reader(".svs", self.state_reader)
[c128284]97        # Return the list of panels
98        return [self.invariant_panel]
[d65a00a]99 
[a07e72f]100    def get_context_menu(self, plotpanel=None):
[c128284]101        """
[d7a39e5]102        This method is optional.
103   
104        When the context menu of a plot is rendered, the
105        get_context_menu method will be called to give you a
106        chance to add a menu item to the context menu.
107       
108        A ref to a Graph object is passed so that you can
109        investigate the plot content and decide whether you
110        need to add items to the context menu. 
111       
112        This method returns a list of menu items.
113        Each item is itself a list defining the text to
114        appear in the menu, a tool-tip help text, and a
115        call-back method.
116       
117        :param graph: the Graph object to which we attach the context menu
118       
119        :return: a list of menu items with call-back function
[c128284]120        """
[a07e72f]121        graph = plotpanel.graph
[c128284]122        invariant_option = "Compute invariant"
[4a2b054]123        invariant_hint = "Will displays the invariant panel for"
[76e894c]124        invariant_hint += " further computation"
[4e1c362]125       
[a07e72f]126        if graph.selected_plottable not in plotpanel.plots:
127            return []
128        data = plotpanel.plots[graph.selected_plottable]
[c128284]129       
[ec1584e]130        if issubclass(data.__class__, Data1D):
131           if data.name != "$I_{obs}(q)$" and  data.name != " $P_{fit}(r)$":
132               return [[invariant_option, invariant_hint, 
[a07e72f]133                                        self._compute_invariant]]
134        return []
135
[c128284]136    def _compute_invariant(self, event):   
137        """
[d7a39e5]138        Open the invariant panel to invariant computation
[c128284]139        """
140        self.panel = event.GetEventObject()
[4a2b054]141        Plugin.on_perspective(self, event=event)
[a07e72f]142        id = self.panel.graph.selected_plottable
143        data = self.panel.plots[self.panel.graph.selected_plottable]
144        if data is None:
145            return
146        if not issubclass(data.__class__, Data1D):
147            name = data.__class__.__name__
148            msg = "Invariant use only Data1D got: [%s] " % str(name)
149            raise ValueError, msg
150        self.compute_helper(data=data)
[4e1c362]151               
[e88ebfd]152    def set_data(self, data_list=None):
[f1e06a8e]153        """
154        receive a list of data and compute invariant
155        """
[9cec2dd]156        msg = ""
[a07e72f]157        data = None
[14cd91b1]158        if data_list is None:
159            data_list = []
[e88ebfd]160        if len(data_list) >= 1:
161            if len(data_list) == 1:
162                data = data_list[0]
163            else:
[9cec2dd]164                data_1d_list = []
165                data_2d_list = []
166                error_msg = ""
167                # separate data into data1d and data2d list
168                for data in data_list:
169                    if data is not None:
170                        if issubclass(data.__class__, Data1D):
171                            data_1d_list.append(data)
172                        else:
173                            error_msg += " %s  type %s \n" % (str(data.name),
174                                             str(data.__class__.__name__))
175                            data_2d_list.append(data)
176                if len(data_2d_list) > 0:
177                    msg = "Invariant does not support the following data types:\n"
178                    msg += error_msg
179                if len(data_1d_list) == 0:
180                    wx.PostEvent(self.parent, 
181                    StatusEvent(status=msg, info='error'))
182                    return
183                msg += "Invariant panel does not allow multiple data!\n"
[e88ebfd]184                msg += "Please select one.\n"
[6813da7d]185                if len(data_list) > 1:
186                    from invariant_widgets import DataDialog
187                    dlg = DataDialog(data_list=data_1d_list, text=msg)
188                    if dlg.ShowModal() == wx.ID_OK:
189                        data = dlg.get_data()
190                    else:
191                        data = None
192                    dlg.Destroy()
193
[e88ebfd]194            if data is None:
[9cec2dd]195                msg += "invariant receives no data. \n"
196                wx.PostEvent(self.parent, 
197                     StatusEvent(status=msg, info='error'))
[e88ebfd]198                return
[9cec2dd]199            if not issubclass(data.__class__, Data1D):
200                 msg += "invariant cannot be computed for data of "
201                 msg += "type %s\n" % (data.__class__.__name__)
202                 wx.PostEvent(self.parent, 
203                     StatusEvent(status=msg, info='error'))
204                 return
205            else:
[e88ebfd]206                wx.PostEvent(self.parent, NewPlotEvent(plot=data,
207                                           title=data.title))
208                try:
209                    self.compute_helper(data)
210                except:
[9cec2dd]211                    msg = "Invariant Set_data: " + str(sys.exc_value)
[e88ebfd]212                    wx.PostEvent(self.parent, StatusEvent(status=msg,
213                                                                info="error"))
[a07e72f]214        else:   
215            msg = "invariant cannot be computed for data of "
216            msg += "type %s" % (data.__class__.__name__)
217            wx.PostEvent(self.parent, 
218                     StatusEvent(status=msg, info='error'))
[20b482f]219    def delete_data(self, data_id):
220        """
221        """
[b24cfd9]222        if self.__data is None:
223            return
[20b482f]224        for id in data_id:
225            if id == self.__data.id:
226                self.clear_panel()
227               
[cb69775]228    def clear_panel(self):
[a07e72f]229        """
230        """
[cb69775]231        self.invariant_panel.clear_panel()
232       
[343fdb6]233    def compute_helper(self, data):
234        """
235        """
236        if data is None:
237            return 
[f24925ab]238        # set current data if not it's a state data
239        if not self.invariant_panel.is_state_data:
[9c1f463]240            # Store reference to data
241            self.__data = data
242            # Set the data set to be user for invariant calculation
[210ff4f]243            self.invariant_panel.set_data(data=data)
[f1e06a8e]244           
[4e1c362]245    def save_file(self, filepath, state=None):
246        """
247        Save data in provided state object.
248               
249        :param filepath: path of file to write to
250        :param state: invariant state
251        """     
252        # Write the state to file
253        # First, check that the data is of the right type
254        current_plottable = self.__data
255
[d4d78c9]256        if issubclass(current_plottable.__class__, Data1D):
[4e1c362]257            self.state_reader.write(filepath, current_plottable, state)
258        else:
[4a2b054]259            msg = "invariant.save_file: the data being saved is"
[79492222]260            msg += " not a sas.dataloader.data_info.Data1D object" 
[4a2b054]261            raise RuntimeError, msg
[4e1c362]262
[75fbd17]263    def set_state(self, state=None, datainfo=None):   
[4e1c362]264        """
265        Call-back method for the state reader.
[9b18735]266        This method is called when a .inv/.svs file is loaded.
[4e1c362]267       
268        :param state: State object
269        """
[4da35bc]270        self.temp_state = None
[4e1c362]271        try:
[75fbd17]272            if datainfo.__class__.__name__ == 'list':
273                data = datainfo[0]
[cb69775]274            else:
275                data = datainfo
[75fbd17]276            if data is None:
[4a2b054]277                msg = "invariant.set_state: datainfo parameter cannot"
278                msg += " be None in standalone mode"
279                raise RuntimeError, msg
[04295ea]280            # Make sure the user sees the invariant panel after loading
281            #self.parent.set_perspective(self.perspective)
282            self.on_perspective(event=None)
[75fbd17]283            name = data.meta_data['invstate'].file
284            data.meta_data['invstate'].file = name
285            data.name = name
286            data.filename = name
[04295ea]287
[cb69775]288            data = self.parent.create_gui_data(data,None)
[75fbd17]289            self.__data = data
290            wx.PostEvent(self.parent, NewPlotEvent(plot=self.__data,
291                                        reset=True, title=self.__data.title))
[2f97794]292            data_dict = {self.__data.id:self.__data}
293            self.parent.add_data(data_list=data_dict)
[f24925ab]294            # set state
295            self.invariant_panel.is_state_data = True
[9b18735]296           
297            # Load the invariant states
[cb69775]298            self.temp_state = state
[04295ea]299            # Requires to have self.__data and self.temp_state  first.
300            self.on_set_state_helper(None)
[cefb3fb]301
[75fbd17]302        except: 
[4e1c362]303            logging.error("invariant.set_state: %s" % sys.exc_value)
[4da35bc]304           
[9b18735]305    def on_set_state_helper(self, event=None):
[4da35bc]306        """
[9b18735]307        Set the state when called by EVT_STATE_UPDATE event from guiframe
308        after a .inv/.svs file is loaded
[4da35bc]309        """
[4a2b054]310        self.invariant_panel.set_state(state=self.temp_state,
311                                       data=self.__data)
[4da35bc]312        self.temp_state = None
[c128284]313       
[9b18735]314       
[2661d8b]315    def plot_theory(self, data=None, name=None):
[c128284]316        """
[d7a39e5]317        Receive a data set and post a NewPlotEvent to parent.
318       
319        :param data: extrapolated data to be plotted
320        :param name: Data's name to use for the legend
[c128284]321        """
[4a2b054]322        #import copy
[2661d8b]323        if data is None:
[a07e72f]324            id = str(self.__data.id) + name
[e88ebfd]325            group_id = self.__data.group_id
[a07e72f]326            wx.PostEvent(self.parent, NewPlotEvent(id=id,
327                                               group_id=group_id,
[c85c180]328                                               action='Remove'))
[a07e72f]329            return
330   
331        new_plot = Data1D(x=[], y=[], dy=None)
332        new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
333        scale = self.invariant_panel.get_scale()
334        background = self.invariant_panel.get_background()
335       
336        if scale != 0:
337            # Put back the sacle and bkg for plotting
338            data.y = (data.y + background)/scale
339            new_plot = Data1D(x=data.x, y=data.y, dy=None)
340            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
[2661d8b]341        else:
[a07e72f]342            msg = "Scale can not be zero."
343            raise ValueError, msg
344        if len(new_plot.x)== 0 :
345            return
346       
[c128284]347        new_plot.name = name
[2661d8b]348        new_plot.xaxis(self.__data._xaxis, self.__data._xunit)
349        new_plot.yaxis(self.__data._yaxis, self.__data._yunit)
350        new_plot.group_id = self.__data.group_id
[a07e72f]351        new_plot.id = str(self.__data.id) + name
[f1e06a8e]352        new_plot.title = self.__data.title
[4e1c362]353        # Save theory_data in a state
354        if data != None:
355            name_head = name.split('-')
[4a2b054]356            if name_head[0] == 'Low':
[4e1c362]357                self.invariant_panel.state.theory_lowQ = copy.deepcopy(new_plot)
[4a2b054]358            elif name_head[0] == 'High':
[f1e06a8e]359                self.invariant_panel.state.theory_highQ =copy.deepcopy(new_plot)
[c85c180]360           
[27f4bee]361        self.parent.update_theory(data_id=self.__data.id, theory=new_plot)
[4a2b054]362        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
[f1e06a8e]363                                               title=self.__data.title))
[6d55d81]364       
[18d0bba]365    def plot_data(self, scale, background):
[6d55d81]366        """
[d7a39e5]367        replot the current data if the user enters a new scale or background
[6d55d81]368        """
[18d0bba]369        new_plot = scale * self.__data - background
370        new_plot.name = self.__data.name
371        new_plot.group_id = self.__data.group_id
372        new_plot.id = self.__data.id
[f1e06a8e]373        new_plot.title = self.__data.title
[4e1c362]374       
375        # Save data in a state: but seems to never happen
376        if new_plot != None:
377            self.invariant_panel.state.data = copy.deepcopy(new_plot)
[4a2b054]378        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
[f1e06a8e]379                                               title=new_plot.title))
[c128284]380       
Note: See TracBrowser for help on using the repository browser.