source: sasview/src/sas/perspectives/invariant/invariant.py @ 2df0b74

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 2df0b74 was 78a205a, checked in by Mathieu Doucet <doucetm@…>, 10 years ago

pylint fixes

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