source: sasview/src/sas/sasgui/perspectives/invariant/invariant.py @ d619341

Last change on this file since d619341 was c10d9d6c, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 9 years ago

updated references to old paths.

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