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
Line 
1
2
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################################################################################
13
14
15import sys
16import wx
17import copy
18import logging
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
24from sas.perspectives.invariant.invariant_state import Reader as reader
25from sas.dataloader.loader import Loader
26from sas.perspectives.invariant.invariant_panel import InvariantPanel
27from sas.guiframe.plugin_base import PluginBase
28
29class Plugin(PluginBase):
30    """
31    This class defines the interface for invariant Plugin class
32    that can be used by the gui_manager.
33    """
34
35    def __init__(self, standalone=False):
36        PluginBase.__init__(self, name="Invariant", standalone=standalone)
37
38        # dictionary containing data name and error on dy of that data
39        self.err_dy = {}
40
41        # default state objects
42        self.state_reader = None
43        self._extensions = '.inv'
44        self.temp_state = None
45        self.__data = None
46
47        # Log startup
48        logging.info("Invariant plug-in started")
49
50    def help(self, evt):
51        """
52        Show a general help dialog.
53        """
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                    logging.error(sys.exc_value)
63        frame.Show(True)
64
65    def get_data(self):
66        """
67        """
68        return self.__data
69
70    def get_panels(self, parent):
71        """
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
82        """
83        # # Save a reference to the parent
84        self.parent = parent
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()
89        self.invariant_panel.set_manager(manager=self)
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)
96        # loader.associate_file_reader(".svs", self.state_reader)
97        # Return the list of panels
98        return [self.invariant_panel]
99
100    def get_context_menu(self, plotpanel=None):
101        """
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
120        """
121        graph = plotpanel.graph
122        invariant_option = "Compute invariant"
123        invariant_hint = "Will displays the invariant panel for"
124        invariant_hint += " further computation"
125
126        if graph.selected_plottable not in plotpanel.plots:
127            return []
128        data = plotpanel.plots[graph.selected_plottable]
129
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, self._compute_invariant]]
133        return []
134
135    def _compute_invariant(self, event):
136        """
137        Open the invariant panel to invariant computation
138        """
139        self.panel = event.GetEventObject()
140        Plugin.on_perspective(self, event=event)
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)
148            raise ValueError, msg
149        self.compute_helper(data=data)
150
151    def set_data(self, data_list=None):
152        """
153        receive a list of data and compute invariant
154        """
155        msg = ""
156        data = None
157        if data_list is None:
158            data_list = []
159        if len(data_list) >= 1:
160            if len(data_list) == 1:
161                data = data_list[0]
162            else:
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),
173                                                              str(data.__class__.__name__))
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:
179                    wx.PostEvent(self.parent, StatusEvent(status=msg, info='error'))
180                    return
181                msg += "Invariant panel does not allow multiple data!\n"
182                msg += "Please select one.\n"
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
192            if data is None:
193                msg += "invariant receives no data. \n"
194                wx.PostEvent(self.parent, StatusEvent(status=msg, info='error'))
195                return
196            if not issubclass(data.__class__, Data1D):
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
201            else:
202                wx.PostEvent(self.parent, NewPlotEvent(plot=data, title=data.title))
203                try:
204                    self.compute_helper(data)
205                except:
206                    msg = "Invariant Set_data: " + str(sys.exc_value)
207                    wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
208        else:
209            msg = "invariant cannot be computed for data of "
210            msg += "type %s" % (data.__class__.__name__)
211            wx.PostEvent(self.parent, StatusEvent(status=msg, info='error'))
212
213    def delete_data(self, data_id):
214        """
215        """
216        if self.__data is None:
217            return
218        for id in data_id:
219            if id == self.__data.id:
220                self.clear_panel()
221
222    def clear_panel(self):
223        """
224        """
225        self.invariant_panel.clear_panel()
226
227    def compute_helper(self, data):
228        """
229        """
230        if data is None:
231            return
232        # set current data if not it's a state data
233        if not self.invariant_panel.is_state_data:
234            # Store reference to data
235            self.__data = data
236            # Set the data set to be user for invariant calculation
237            self.invariant_panel.set_data(data=data)
238
239    def save_file(self, filepath, state=None):
240        """
241        Save data in provided state object.
242
243        :param filepath: path of file to write to
244        :param state: invariant state
245        """
246        # Write the state to file
247        # First, check that the data is of the right type
248        current_plottable = self.__data
249
250        if issubclass(current_plottable.__class__, Data1D):
251            self.state_reader.write(filepath, current_plottable, state)
252        else:
253            msg = "invariant.save_file: the data being saved is"
254            msg += " not a sas.dataloader.data_info.Data1D object"
255            raise RuntimeError, msg
256
257    def set_state(self, state=None, datainfo=None):
258        """
259        Call-back method for the state reader.
260        This method is called when a .inv/.svs file is loaded.
261
262        :param state: State object
263        """
264        self.temp_state = None
265        try:
266            if datainfo.__class__.__name__ == 'list':
267                data = datainfo[0]
268            else:
269                data = datainfo
270            if data is None:
271                msg = "invariant.set_state: datainfo parameter cannot"
272                msg += " be None in standalone mode"
273                raise RuntimeError, msg
274            # Make sure the user sees the invariant panel after loading
275            # self.parent.set_perspective(self.perspective)
276            self.on_perspective(event=None)
277            name = data.meta_data['invstate'].file
278            data.meta_data['invstate'].file = name
279            data.name = name
280            data.filename = name
281
282            data = self.parent.create_gui_data(data, None)
283            self.__data = data
284            wx.PostEvent(self.parent, NewPlotEvent(plot=self.__data,
285                                                   reset=True, title=self.__data.title))
286            data_dict = {self.__data.id:self.__data}
287            self.parent.add_data(data_list=data_dict)
288            # set state
289            self.invariant_panel.is_state_data = True
290
291            # Load the invariant states
292            self.temp_state = state
293            # Requires to have self.__data and self.temp_state  first.
294            self.on_set_state_helper(None)
295
296        except:
297            logging.error("invariant.set_state: %s" % sys.exc_value)
298
299    def on_set_state_helper(self, event=None):
300        """
301        Set the state when called by EVT_STATE_UPDATE event from guiframe
302        after a .inv/.svs file is loaded
303        """
304        self.invariant_panel.set_state(state=self.temp_state,
305                                       data=self.__data)
306        self.temp_state = None
307
308
309    def plot_theory(self, data=None, name=None):
310        """
311        Receive a data set and post a NewPlotEvent to parent.
312
313        :param data: extrapolated data to be plotted
314        :param name: Data's name to use for the legend
315        """
316        # import copy
317        if data is None:
318            id = str(self.__data.id) + name
319            group_id = self.__data.group_id
320            wx.PostEvent(self.parent, NewPlotEvent(id=id, group_id=group_id, action='Remove'))
321            return
322
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()
327
328        if scale != 0:
329            # Put back the sacle and bkg for plotting
330            data.y = (data.y + background) / scale
331            new_plot = Data1D(x=data.x, y=data.y, dy=None)
332            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
333        else:
334            msg = "Scale can not be zero."
335            raise ValueError, msg
336        if len(new_plot.x) == 0:
337            return
338
339        new_plot.name = name
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
343        new_plot.id = str(self.__data.id) + name
344        new_plot.title = self.__data.title
345        # Save theory_data in a state
346        if data != None:
347            name_head = name.split('-')
348            if name_head[0] == 'Low':
349                self.invariant_panel.state.theory_lowQ = copy.deepcopy(new_plot)
350            elif name_head[0] == 'High':
351                self.invariant_panel.state.theory_highQ = copy.deepcopy(new_plot)
352
353        self.parent.update_theory(data_id=self.__data.id, theory=new_plot)
354        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
355                                               title=self.__data.title))
356
357    def plot_data(self, scale, background):
358        """
359        replot the current data if the user enters a new scale or background
360        """
361        new_plot = scale * self.__data - background
362        new_plot.name = self.__data.name
363        new_plot.group_id = self.__data.group_id
364        new_plot.id = self.__data.id
365        new_plot.title = self.__data.title
366
367        # Save data in a state: but seems to never happen
368        if new_plot != None:
369            self.invariant_panel.state.data = copy.deepcopy(new_plot)
370        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
371                                               title=new_plot.title))
372
Note: See TracBrowser for help on using the repository browser.