source: sasview/src/sas/sasgui/perspectives/corfunc/corfunc_panel.py @ ebc5470

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.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since ebc5470 was ebc5470, checked in by lewis, 8 years ago

Add option to input background level

  • Property mode set to 100644
File size: 14.8 KB
Line 
1import wx
2import sys
3from wx.lib.scrolledpanel import ScrolledPanel
4from sas.sasgui.guiframe.events import PlotQrangeEvent
5from sas.sasgui.guiframe.events import StatusEvent
6from sas.sasgui.guiframe.panel_base import PanelBase
7from sas.sasgui.guiframe.utils import check_float
8from sas.sasgui.perspectives.invariant.invariant_widgets import OutputTextCtrl
9from sas.sasgui.perspectives.invariant.invariant_widgets import InvTextCtrl
10from sas.sasgui.perspectives.fitting.basepage import ModelTextCtrl
11from sas.sasgui.perspectives.corfunc.corfunc_state import CorfuncState
12
13if sys.platform.count("win32") > 0:
14    _STATICBOX_WIDTH = 350
15    PANEL_WIDTH = 400
16    PANEL_HEIGHT = 700
17    FONT_VARIANT = 0
18else:
19    _STATICBOX_WIDTH = 390
20    PANEL_WIDTH = 430
21    PANEL_HEIGHT = 700
22    FONT_VARIANT = 1
23
24class CorfuncPanel(ScrolledPanel,PanelBase):
25    window_name = "Correlation Function"
26    window_caption = "Correlation Function"
27    CENTER_PANE = True
28
29    def __init__(self, parent, data=None, manager=None, *args, **kwds):
30        kwds["size"] = (PANEL_WIDTH, PANEL_HEIGHT)
31        kwds["style"] = wx.FULL_REPAINT_ON_RESIZE
32        ScrolledPanel.__init__(self, parent=parent, *args, **kwds)
33        PanelBase.__init__(self, parent)
34        self.SetupScrolling()
35        self.SetWindowVariant(variant=FONT_VARIANT)
36        self._manager = manager
37        self._data = data # The data to be analysed
38        self._data_name_box = None # Text box to show name of file
39        self._background_input = None
40        self._qmin_input = None
41        self._qmax1_input = None
42        self._qmax2_input = None
43        self.qmin = 0
44        self.qmax = (0, 0)
45        self.background = 0
46        # Dictionary for saving IDs of text boxes used to display output data
47        self._output_ids = None
48        self.state = None
49        self._do_layout()
50        self.set_state()
51        self._qmin_input.Bind(wx.EVT_TEXT, self._on_enter_input)
52        self._qmax1_input.Bind(wx.EVT_TEXT, self._on_enter_input)
53        self._qmax2_input.Bind(wx.EVT_TEXT, self._on_enter_input)
54
55    def set_state(self, state=None, data=None):
56        """
57        Set the state of the panel. If no state is provided, the panel will
58        be set to the default state.
59
60        :param state: A CorfuncState object
61        :param data: A Data1D object
62        """
63        if state is None:
64            self.state = CorfuncState()
65        else:
66            self.state = state
67        if data is not None:
68            self.state.data = data
69        self.set_data(self.state.data, set_qrange=False)
70        if self.state.qmin is not None:
71            self.set_qmin(self.state.qmin)
72        if self.state.qmax is not None and self.state.qmax != (None, None):
73            self.set_qmax(tuple(self.state.qmax))
74        if self.state.background is not None:
75            self.set_background(self.state.background)
76
77    def get_state(self):
78        """
79        Return the state of the panel
80        """
81        state = CorfuncState()
82        state.set_saved_state('qmin_tcl', self.qmin)
83        state.set_saved_state('qmax1_tcl', self.qmax[0])
84        state.set_saved_state('qmax2_tcl', self.qmax[1])
85        state.set_saved_state('background_tcl', self.background)
86        if self._data is not None:
87            state.file = self._data.title
88            state.data = self._data
89        self.state = state
90
91        return self.state
92
93    def onSetFocus(self, evt):
94        if evt is not None:
95            evt.Skip()
96        self._validate_inputs()
97
98    def set_data(self, data=None, set_qrange=True):
99        """
100        Update the GUI to reflect new data that has been loaded in
101
102        :param data: The data that has been loaded
103        """
104        if data is None:
105            return
106        self._data_name_box.SetValue(str(data.title))
107        self._data = data
108        if self._manager is not None:
109            self._manager.show_data(data=data, reset=True)
110        if set_qrange:
111            lower = data.x[-1]*0.05
112            upper1 = data.x[-1] - lower*5
113            upper2 = data.x[-1]
114            self.set_qmin(lower)
115            self.set_qmax((upper1, upper2))
116            self.set_background(0.0)
117
118    def get_data(self):
119        return self._data
120
121    def save_project(self, doc=None):
122        """
123        Return an XML node containing the state of the panel
124
125        :param doc: Am xml node to attach the project state to (optional)
126        """
127        data = self._data
128        state = self.get_state()
129        if data is not None:
130            new_doc, sasentry = self._manager.state_reader._to_xml_doc(data)
131            new_doc = state.toXML(doc=new_doc, entry_node=sasentry)
132            if new_doc is not None:
133                if doc is not None and hasattr(doc, "firstChild"):
134                    child = new_doc.getElementsByTagName("SASentry")
135                    for item in child:
136                        doc.firstChild.appendChild(item)
137                else:
138                    doc = new_doc
139        return doc
140
141    def set_qmin(self, qmin):
142        self.qmin = qmin
143        self._qmin_input.SetValue(str(qmin))
144
145    def set_qmax(self, qmax):
146        self.qmax = qmax
147        self._qmax1_input.SetValue(str(qmax[0]))
148        self._qmax2_input.SetValue(str(qmax[1]))
149
150    def set_background(self, bg):
151        self.background = bg
152        self._background_input.SetValue(str(bg))
153
154
155    def _on_enter_input(self, event=None):
156        """
157        Read values from input boxes and save to memory.
158        """
159        if event is not None: event.Skip()
160        if not self._validate_inputs():
161            return
162        self.qmin = float(self._qmin_input.GetValue())
163        new_qmax1 = float(self._qmax1_input.GetValue())
164        new_qmax2 = float(self._qmax2_input.GetValue())
165        self.qmax = (new_qmax1, new_qmax2)
166        self.background = float(self._background_input.GetValue())
167        data_id = self._manager.data_id
168        from sas.sasgui.perspectives.corfunc.corfunc import GROUP_ID_IQ_DATA
169        group_id = GROUP_ID_IQ_DATA
170        if event is not None:
171            wx.PostEvent(self._manager.parent, PlotQrangeEvent(
172                ctrl=[self._qmin_input, self._qmax1_input, self._qmax2_input],
173                active=event.GetEventObject(), id=data_id, group_id=group_id,
174                leftdown=False))
175
176    def _validate_inputs(self):
177        """
178        Check that the values for qmin and qmax in the input boxes are valid
179        """
180        if self._data is None:
181            return False
182        qmin_valid = check_float(self._qmin_input)
183        qmax1_valid = check_float(self._qmax1_input)
184        qmax2_valid = check_float(self._qmax2_input)
185        qmax_valid = qmax1_valid and qmax2_valid
186        background_valid = check_float(self._background_input)
187        msg = ""
188        if (qmin_valid and qmax_valid and background_valid):
189            qmin = float(self._qmin_input.GetValue())
190            qmax1 = float(self._qmax1_input.GetValue())
191            qmax2 = float(self._qmax2_input.GetValue())
192            background = float(self._background_input.GetValue())
193            if not qmin > self._data.x.min():
194                msg = "qmin must be greater than the lowest q value"
195                qmin_valid = False
196            elif qmax2 < qmax1:
197                msg = "qmax1 must be less than qmax2"
198                qmax_valid = False
199            elif qmin > qmax1:
200                msg = "qmin must be less than qmax"
201                qmin_valid = False
202            elif background < 0 or background > self._data.y.max():
203                msg = "background must be positive and less than highest I value"
204                background_valid = False
205        if not qmin_valid:
206            self._qmin_input.SetBackgroundColour('pink')
207        if not qmax_valid:
208            self._qmax1_input.SetBackgroundColour('pink')
209            self._qmax2_input.SetBackgroundColour('pink')
210        if not background_valid:
211            self._background_input.SetBackgroundColour('pink')
212            if msg != "":
213                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
214        if (qmin_valid and qmax_valid and background_valid):
215            self._qmin_input.SetBackgroundColour(wx.WHITE)
216            self._qmax1_input.SetBackgroundColour(wx.WHITE)
217            self._qmax2_input.SetBackgroundColour(wx.WHITE)
218            self._background_input.SetBackgroundColour(wx.WHITE)
219        self._qmin_input.Refresh()
220        self._qmax1_input.Refresh()
221        self._qmax2_input.Refresh()
222        return (qmin_valid and qmax_valid and background_valid)
223
224    def _do_layout(self):
225        """
226        Draw the window content
227        """
228        vbox = wx.GridBagSizer(0,0)
229
230        # I(q) data box
231        databox = wx.StaticBox(self, -1, "I(Q) Data Source")
232        databox_sizer = wx.StaticBoxSizer(databox, wx.VERTICAL)
233
234        file_sizer = wx.GridBagSizer(5, 5)
235
236        file_name_label = wx.StaticText(self, -1, "Name:")
237        file_sizer.Add(file_name_label, (0, 0), (1, 1),
238            wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
239
240        self._data_name_box = OutputTextCtrl(self, -1,
241            size=(300,20))
242        file_sizer.Add(self._data_name_box, (0, 1), (1, 1),
243            wx.CENTER | wx.ADJUST_MINSIZE, 15)
244
245        file_sizer.AddSpacer((1, 25), pos=(0,2))
246        databox_sizer.Add(file_sizer, wx.TOP, 15)
247
248        vbox.Add(databox_sizer, (0, 0), (1, 1),
249            wx.LEFT | wx.RIGHT | wx.EXPAND | wx.ADJUST_MINSIZE | wx.TOP, 15)
250
251
252        # Parameters
253        qbox = wx.StaticBox(self, -1, "Parameters")
254        qbox_sizer = wx.StaticBoxSizer(qbox, wx.VERTICAL)
255        qbox_sizer.SetMinSize((_STATICBOX_WIDTH, 75))
256
257        q_sizer = wx.GridBagSizer(5, 5)
258
259        # Explanation
260        explanation_txt = ("Corfunc will use all values in the lower range for"
261            " Guinier back extrapolation, and all values in the upper range "
262            "for Porod forward extrapolation.")
263        explanation_label = wx.StaticText(self, -1, explanation_txt,
264            size=(_STATICBOX_WIDTH, 60))
265
266        q_sizer.Add(explanation_label, (0,0), (1,4), wx.LEFT | wx.EXPAND, 5)
267
268        background_label = wx.StaticText(self, -1, "Background:", size=(80,20))
269        q_sizer.Add(background_label, (1,0), (1,1), wx.LEFT | wx.EXPAND, 5)
270
271        self._background_input = ModelTextCtrl(self, -1, size=(50,20),
272            style=wx.TE_PROCESS_ENTER, name='background_input',
273            text_enter_callback=self._on_enter_input)
274        self._background_input.SetToolTipString(("A background value to "
275            "subtract from all intensity values"))
276        q_sizer.Add(self._background_input, (1,1), (1,1), wx.RIGHT | wx.EXPAND, 5)
277
278        qrange_label = wx.StaticText(self, -1, "Q Range:", size=(50,20))
279        q_sizer.Add(qrange_label, (2,0), (1,1), wx.LEFT | wx.EXPAND, 5)
280
281        # Lower Q Range
282        qmin_label = wx.StaticText(self, -1, "Lower:", size=(50,20))
283        qmin_dash_label = wx.StaticText(self, -1, "-", size=(10,20),
284            style=wx.ALIGN_CENTER_HORIZONTAL)
285
286        qmin_lower = OutputTextCtrl(self, -1, size=(50, 20), value="0.0")
287        self._qmin_input = ModelTextCtrl(self, -1, size=(50, 20),
288                        style=wx.TE_PROCESS_ENTER, name='qmin_input',
289                        text_enter_callback=self._on_enter_input)
290        self._qmin_input.SetToolTipString(("Values with q < qmin will be used "
291            "for Guinier back extrapolation"))
292
293        q_sizer.Add(qmin_label, (3, 0), (1, 1), wx.LEFT | wx.EXPAND, 5)
294        q_sizer.Add(qmin_lower, (3, 1), (1, 1), wx.LEFT, 5)
295        q_sizer.Add(qmin_dash_label, (3, 2), (1, 1), wx.CENTER | wx.EXPAND, 5)
296        q_sizer.Add(self._qmin_input, (3, 3), (1, 1), wx.LEFT, 5)
297
298        # Upper Q range
299        qmax_tooltip = ("Values with qmax1 < q < qmax2 will be used for Porod"
300            " forward extrapolation")
301
302        qmax_label = wx.StaticText(self, -1, "Upper:", size=(50,20))
303        qmax_dash_label = wx.StaticText(self, -1, "-", size=(10,20),
304            style=wx.ALIGN_CENTER_HORIZONTAL)
305
306        self._qmax1_input = ModelTextCtrl(self, -1, size=(50, 20),
307            style=wx.TE_PROCESS_ENTER, name="qmax1_input",
308            text_enter_callback=self._on_enter_input)
309        self._qmax1_input.SetToolTipString(qmax_tooltip)
310        self._qmax2_input = ModelTextCtrl(self, -1, size=(50, 20),
311            style=wx.TE_PROCESS_ENTER, name="qmax2_input",
312            text_enter_callback=self._on_enter_input)
313        self._qmax2_input.SetToolTipString(qmax_tooltip)
314
315        q_sizer.Add(qmax_label, (4, 0), (1, 1), wx.LEFT | wx.EXPAND, 5)
316        q_sizer.Add(self._qmax1_input, (4, 1), (1, 1), wx.LEFT, 5)
317        q_sizer.Add(qmax_dash_label, (4, 2), (1, 1), wx.CENTER | wx.EXPAND, 5)
318        q_sizer.Add(self._qmax2_input, (4,3), (1, 1), wx.LEFT, 5)
319
320        qbox_sizer.Add(q_sizer, wx.TOP, 0)
321
322        vbox.Add(qbox_sizer, (1, 0), (1, 1),
323            wx.LEFT | wx.RIGHT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
324
325        # Output data
326        outputbox = wx.StaticBox(self, -1, "Output Measuments")
327        outputbox_sizer = wx.StaticBoxSizer(outputbox, wx.VERTICAL)
328
329        output_sizer = wx.GridBagSizer(5, 5)
330
331        label_strings = [
332            "Long Period (A): ",
333            "Average Hard Block Thickness (A): ",
334            "Average Interface Thickness (A): ",
335            "Average Core Thickness: ",
336            "PolyDispersity: ",
337            "Filling Fraction: "
338        ]
339        self._output_ids = dict()
340        for i in range(len(label_strings)):
341            # Create a label and a text box for each poperty
342            label = wx.StaticText(self, -1, label_strings[i])
343            output_box = OutputTextCtrl(self, wx.NewId(), size=(50, 20),
344                value="-", style=wx.ALIGN_CENTER_HORIZONTAL)
345            # Save the ID of each of the text boxes for accessing after the
346            # output data has been calculated
347            self._output_ids[label_strings[i]] = output_box.GetId()
348            output_sizer.Add(label, (i, 0), (1, 1), wx.LEFT | wx.EXPAND, 15)
349            output_sizer.Add(output_box, (i, 2), (1, 1),
350                wx.RIGHT | wx.EXPAND, 15)
351
352        outputbox_sizer.Add(output_sizer, wx.TOP, 0)
353
354        vbox.Add(outputbox_sizer, (2, 0), (1, 1),
355            wx.LEFT | wx.RIGHT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
356
357        # Controls
358        controlbox = wx.StaticBox(self, -1, "Controls")
359        controlbox_sizer = wx.StaticBoxSizer(controlbox, wx.VERTICAL)
360
361        controls_sizer = wx.BoxSizer(wx.VERTICAL)
362
363        extrapolate_btn = wx.Button(self, wx.NewId(), "Extrapolate")
364        transform_btn = wx.Button(self, wx.NewId(), "Transform")
365        compute_btn = wx.Button(self, wx.NewId(), "Compute Measuments")
366
367        controls_sizer.Add(extrapolate_btn, wx.CENTER | wx.EXPAND)
368        controls_sizer.Add(transform_btn, wx.CENTER | wx.EXPAND)
369        controls_sizer.Add(compute_btn, wx.CENTER | wx.EXPAND)
370
371        controlbox_sizer.Add(controls_sizer, wx.TOP | wx.EXPAND, 0)
372        vbox.Add(controlbox_sizer, (3, 0), (1, 1),
373            wx.LEFT | wx.RIGHT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
374
375        self.SetSizer(vbox)
Note: See TracBrowser for help on using the repository browser.