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
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.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
25from sas.sascalc.dataloader.loader import Loader
26from sas.sasgui.perspectives.invariant.invariant_panel import InvariantPanel
27from sas.sasgui.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):
36        PluginBase.__init__(self, name="Invariant")
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 get_data(self):
51        """
52        """
53        return self.__data
54
55    def get_panels(self, parent):
56        """
57        Create and return the list of wx.Panels for your plug-in.
58        Define the plug-in perspective.
59
60        Panels should inherit from DefaultPanel defined below,
61        or should present the same interface. They must define
62        "window_caption" and "window_name".
63
64        :param parent: parent window
65
66        :return: list of panels
67        """
68        # # Save a reference to the parent
69        self.parent = parent
70        self.frame = MDIFrame(self.parent, None, 'None', (100, 200))
71        self.invariant_panel = InvariantPanel(parent=self.frame)
72        self.frame.set_panel(self.invariant_panel)
73        self._frame_set_helper()
74        self.invariant_panel.set_manager(manager=self)
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
79        loader = Loader()
80        loader.associate_file_reader(".inv", self.state_reader)
81        # loader.associate_file_reader(".svs", self.state_reader)
82        # Return the list of panels
83        return [self.invariant_panel]
84
85    def get_context_menu(self, plotpanel=None):
86        """
87        This method is optional.
88
89        When the context menu of a plot is rendered, the
90        get_context_menu method will be called to give you a
91        chance to add a menu item to the context menu.
92
93        A ref to a Graph object is passed so that you can
94        investigate the plot content and decide whether you
95        need to add items to the context menu.
96
97        This method returns a list of menu items.
98        Each item is itself a list defining the text to
99        appear in the menu, a tool-tip help text, and a
100        call-back method.
101
102        :param graph: the Graph object to which we attach the context menu
103
104        :return: a list of menu items with call-back function
105        """
106        graph = plotpanel.graph
107        invariant_option = "Compute invariant"
108        invariant_hint = "Will displays the invariant panel for"
109        invariant_hint += " further computation"
110
111        if graph.selected_plottable not in plotpanel.plots:
112            return []
113        data = plotpanel.plots[graph.selected_plottable]
114
115        if issubclass(data.__class__, Data1D):
116            if data.name != "$I_{obs}(q)$" and  data.name != " $P_{fit}(r)$":
117                return [[invariant_option, invariant_hint, self._compute_invariant]]
118        return []
119
120    def _compute_invariant(self, event):
121        """
122        Open the invariant panel to invariant computation
123        """
124        self.panel = event.GetEventObject()
125        Plugin.on_perspective(self, event=event)
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)
133            raise ValueError, msg
134        self.compute_helper(data=data)
135
136    def set_data(self, data_list=None):
137        """
138        receive a list of data and compute invariant
139        """
140        msg = ""
141        data = None
142        if data_list is None:
143            data_list = []
144        if len(data_list) >= 1:
145            if len(data_list) == 1:
146                data = data_list[0]
147            else:
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),
158                                                              str(data.__class__.__name__))
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:
164                    wx.PostEvent(self.parent, StatusEvent(status=msg, info='error'))
165                    return
166                msg += "Invariant panel does not allow multiple data!\n"
167                msg += "Please select one.\n"
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
177            if data is None:
178                msg += "invariant receives no data. \n"
179                wx.PostEvent(self.parent, StatusEvent(status=msg, info='error'))
180                return
181            if not issubclass(data.__class__, Data1D):
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
186            else:
187                wx.PostEvent(self.parent, NewPlotEvent(plot=data, title=data.title))
188                try:
189                    self.compute_helper(data)
190                except:
191                    msg = "Invariant Set_data: " + str(sys.exc_value)
192                    wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
193        else:
194            msg = "invariant cannot be computed for data of "
195            msg += "type %s" % (data.__class__.__name__)
196            wx.PostEvent(self.parent, StatusEvent(status=msg, info='error'))
197
198    def delete_data(self, data_id):
199        """
200        """
201        if self.__data is None:
202            return
203        for id in data_id:
204            if id == self.__data.id:
205                self.clear_panel()
206
207    def clear_panel(self):
208        """
209        """
210        self.invariant_panel.clear_panel()
211
212    def compute_helper(self, data):
213        """
214        """
215        if data is None:
216            return
217        # set current data if not it's a state data
218        if not self.invariant_panel.is_state_data:
219            # Store reference to data
220            self.__data = data
221            # Set the data set to be user for invariant calculation
222            self.invariant_panel.set_data(data=data)
223
224    def save_file(self, filepath, state=None):
225        """
226        Save data in provided state object.
227
228        :param filepath: path of file to write to
229        :param state: invariant state
230        """
231        # Write the state to file
232        # First, check that the data is of the right type
233        current_plottable = self.__data
234
235        if issubclass(current_plottable.__class__, Data1D):
236            self.state_reader.write(filepath, current_plottable, state)
237        else:
238            msg = "invariant.save_file: the data being saved is"
239            msg += " not a sas.sascalc.dataloader.data_info.Data1D object"
240            raise RuntimeError, msg
241
242    def set_state(self, state=None, datainfo=None):
243        """
244        Call-back method for the state reader.
245        This method is called when a .inv/.svs file is loaded.
246
247        :param state: State object
248        """
249        self.temp_state = None
250        try:
251            if datainfo.__class__.__name__ == 'list':
252                data = datainfo[0]
253            else:
254                data = datainfo
255            if data is None:
256                msg = "invariant.set_state: datainfo parameter cannot"
257                msg += " be None in standalone mode"
258                raise RuntimeError, msg
259            # Make sure the user sees the invariant panel after loading
260            # self.parent.set_perspective(self.perspective)
261            self.on_perspective(event=None)
262            name = data.meta_data['invstate'].file
263            data.meta_data['invstate'].file = name
264            data.name = name
265            data.filename = name
266
267            data = self.parent.create_gui_data(data, None)
268            self.__data = data
269            wx.PostEvent(self.parent, NewPlotEvent(plot=self.__data,
270                                                   reset=True, title=self.__data.title))
271            data_dict = {self.__data.id:self.__data}
272            self.parent.add_data(data_list=data_dict)
273            # set state
274            self.invariant_panel.is_state_data = True
275
276            # Load the invariant states
277            self.temp_state = state
278            # Requires to have self.__data and self.temp_state  first.
279            self.on_set_state_helper(None)
280
281        except:
282            logging.error("invariant.set_state: %s" % sys.exc_value)
283
284    def on_set_state_helper(self, event=None):
285        """
286        Set the state when called by EVT_STATE_UPDATE event from guiframe
287        after a .inv/.svs file is loaded
288        """
289        self.invariant_panel.set_state(state=self.temp_state,
290                                       data=self.__data)
291        self.temp_state = None
292
293
294    def plot_theory(self, data=None, name=None):
295        """
296        Receive a data set and post a NewPlotEvent to parent.
297
298        :param data: extrapolated data to be plotted
299        :param name: Data's name to use for the legend
300        """
301        # import copy
302        if data is None:
303            id = str(self.__data.id) + name
304            group_id = self.__data.group_id
305            wx.PostEvent(self.parent, NewPlotEvent(id=id, group_id=group_id, action='Remove'))
306            return
307
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()
312
313        if scale != 0:
314            # Put back the sacle and bkg for plotting
315            data.y = (data.y + background) / scale
316            new_plot = Data1D(x=data.x, y=data.y, dy=None)
317            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
318        else:
319            msg = "Scale can not be zero."
320            raise ValueError, msg
321        if len(new_plot.x) == 0:
322            return
323
324        new_plot.name = name
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
328        new_plot.id = str(self.__data.id) + name
329        new_plot.title = self.__data.title
330        # Save theory_data in a state
331        if data != None:
332            name_head = name.split('-')
333            if name_head[0] == 'Low':
334                self.invariant_panel.state.theory_lowQ = copy.deepcopy(new_plot)
335            elif name_head[0] == 'High':
336                self.invariant_panel.state.theory_highQ = copy.deepcopy(new_plot)
337
338        self.parent.update_theory(data_id=self.__data.id, theory=new_plot)
339        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
340                                               title=self.__data.title))
341
342    def plot_data(self, scale, background):
343        """
344        replot the current data if the user enters a new scale or background
345        """
346        new_plot = scale * self.__data - background
347        new_plot.name = self.__data.name
348        new_plot.group_id = self.__data.group_id
349        new_plot.id = self.__data.id
350        new_plot.title = self.__data.title
351
352        # Save data in a state: but seems to never happen
353        if new_plot != None:
354            self.invariant_panel.state.data = copy.deepcopy(new_plot)
355        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
356                                               title=new_plot.title))
357
Note: See TracBrowser for help on using the repository browser.