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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since aefde77 was fa81e94, checked in by Piotr Rozyczko <rozyczko@…>, 7 years ago

Initial commit of the P(r) inversion perspective.
Code merged from Jeff Krzywon's ESS_GUI_Pr branch.
Also, minor 2to3 mods to sascalc/sasgui to enble error free setup.

  • Property mode set to 100755
File size: 21.8 KB
RevLine 
[959eb01]1"""
2    Invariant panel
3"""
4import wx
5import sys
6
7from sas.sasgui.guiframe.utils import format_number
[fa81e94]8from .invariant_widgets import OutputTextCtrl
[959eb01]9# Dimensions related to chart
10RECTANGLE_WIDTH = 400.0
11RECTANGLE_HEIGHT = 20
12# Invariant panel size
13_BOX_WIDTH = 76
14
15# scale to use for a bar of value zero
16RECTANGLE_SCALE = 0.0001
17DEFAULT_QSTAR = 1.0
18
19if sys.platform.count("win32") > 0:
20    _STATICBOX_WIDTH = 450
21    PANEL_WIDTH = 500
22    PANEL_HEIGHT = 430
23    FONT_VARIANT = 0
24else:
25    _STATICBOX_WIDTH = 480
26    PANEL_WIDTH = 530
27    PANEL_HEIGHT = 430
28    FONT_VARIANT = 1
29
30ERROR_COLOR = wx.Colour(255, 0, 0, 128)
31EXTRAPOLATION_COLOR = wx.Colour(169, 169, 168, 128)
32INVARIANT_COLOR = wx.Colour(67, 208, 128, 128)
33
34
35class InvariantContainer(wx.Object):
36    """
37    This class stores some values resulting resulting from invariant
38    calculations. Given the value of total invariant, this class can also
39    determine the percentage of invariants resulting from extrapolation.
40    """
41    def __init__(self):
42        # invariant at low range
43        self.qstar_low = None
44        # invariant at low range error
45        self.qstar_low_err = None
46        # invariant
47        self.qstar = None
48        # invariant error
49        self.qstar_err = None
50        # invariant at high range
51        self.qstar_high = None
52        # invariant at high range error
53        self.qstar_high_err = None
54        # invariant total
55        self.qstar_total = None
56        # invariant error
57        self.qstar_total_err = None
58        # scale
59        self.qstar_low_percent = None
60        self.qstar_high_percent = None
61        self.qstar_percent = None
62        # warning message
63        self.existing_warning = False
64        self.warning_msg = "No Details on calculations available...\n"
65
66    def compute_percentage(self):
67        """
68        Compute percentage of each invariant
69        """
70        if self.qstar_total is None:
71            self.qstar_percent = None
72            self.qstar_low = None
73            self.qstar_high = None
74            self.check_values()
75            return
76
77        # compute invariant percentage
78        if self.qstar is None:
79            self.qstar_percent = None
80        else:
81            try:
82                self.qstar_percent = float(self.qstar) / float(self.qstar_total)
83            except:
84                self.qstar_percent = 'error'
85        # compute low q invariant percentage
86        if self.qstar_low is None:
87            self.qstar_low_percent = None
88        else:
89            try:
90                self.qstar_low_percent = float(self.qstar_low)\
91                                            / float(self.qstar_total)
92            except:
93                self.qstar_low_percent = 'error'
94        # compute high q invariant percentage
95        if self.qstar_high is None:
96            self.qstar_high_percent = None
97        else:
98            try:
99                self.qstar_high_percent = float(self.qstar_high)\
100                                                / float(self.qstar_total)
101            except:
102                self.qstar_high_percent = 'error'
103        wx.CallAfter(self.check_values)
104
105    def check_values(self):
106        """
107        check the validity if invariant
108        """
109        if self.qstar_total is None and self.qstar is None:
110            self.warning_msg = "Invariant not calculated.\n"
111            return
112        if self.qstar_total == 0:
113            self.existing_warning = True
114            self.warning_msg = "Invariant is zero. \n"
115            self.warning_msg += "The calculations are likely "
116            self.warning_msg += "to be unreliable!\n"
117            return
118        # warning to the user when the extrapolated invariant is greater than %5
119        msg = ''
120        if self.qstar_percent == 'error':
121            try:
122                float(self.qstar)
123            except:
124                self.existing_warning = True
125                msg += 'Error occurred when computing invariant from data.\n '
126        if self.qstar_percent > 1:
127            self.existing_warning = True
128            msg += "Invariant Q  contribution is greater "
129            msg += "than 100% .\n"
130        if self.qstar_low_percent == 'error':
131            try:
132                float(self.qstar_low)
133            except:
134                self.existing_warning = True
135                msg += "Error occurred when computing extrapolated invariant"
136                msg += " at low-Q region.\n"
137        elif self.qstar_low_percent is not None:
138            if self.qstar_low_percent >= 0.05:
139                self.existing_warning = True
140                msg += "Extrapolated contribution at Low Q is higher "
141                msg += "than 5% of the invariant.\n"
142            elif self.qstar_low_percent < 0:
143                self.existing_warning = True
144                msg += "Extrapolated contribution at Low Q < 0.\n"
145            elif self.qstar_low_percent > 1:
146                self.existing_warning = True
147                msg += "Extrapolated contribution at Low Q is greater "
148                msg += "than 100% .\n"
149        if self.qstar_high_percent == 'error':
150            try:
151                float(self.qstar_high)
152            except:
153                self.existing_warning = True
154                msg += 'Error occurred when computing extrapolated'
155                msg += ' invariant at high-Q region.\n'
156        elif self.qstar_high_percent is not None:
157            if self.qstar_high_percent >= 0.05:
158                self.existing_warning = True
159                msg += "Extrapolated contribution at High Q is higher "
160                msg += "than 5% of the invariant.\n"
161            elif self.qstar_high_percent < 0:
162                self.existing_warning = True
163                msg += "Extrapolated contribution at High Q < 0.\n"
164            elif self.qstar_high_percent > 1:
165                self.existing_warning = True
166                msg += "Extrapolated contribution at High Q is greater "
167                msg += "than 100% .\n"
168        if (self.qstar_low_percent not in [None, "error"]) and \
169            (self.qstar_high_percent not in [None, "error"])\
170            and self.qstar_low_percent + self.qstar_high_percent >= 0.05:
171            self.existing_warning = True
172            msg += "The sum of all extrapolated contributions is higher "
173            msg += "than 5% of the invariant.\n"
174
175        if self.existing_warning:
176            self.warning_msg = ''
177            self.warning_msg += msg
178            self.warning_msg += "The calculations are likely to be"
179            self.warning_msg += " unreliable!\n"
180        else:
181            self.warning_msg = "No Details on calculations available...\n"
182
183class InvariantDetailsPanel(wx.Dialog):
184    """
185    This panel describes proportion of invariants
186    """
187    def __init__(self, parent=None, id=-1, qstar_container=None,
188                 title="Invariant Details",
189                 size=(PANEL_WIDTH, PANEL_HEIGHT)):
190        wx.Dialog.__init__(self, parent=parent, id=id, title=title, size=size)
191
192        # Font size
193        self.SetWindowVariant(variant=FONT_VARIANT)
194        self.parent = parent
195        # self.qstar_container
196        self.qstar_container = qstar_container
197        # warning message
198        self.warning_msg = self.qstar_container.warning_msg
199
200        # Define scale of each bar
201        self.low_inv_percent = self.qstar_container.qstar_low_percent
202        self.low_scale = self.get_scale(percentage=self.low_inv_percent,
203                                        scale_name="Extrapolated at Low Q")
204        self.inv_percent = self.qstar_container.qstar_percent
205        self.inv_scale = self.get_scale(percentage=self.inv_percent,
206                                        scale_name="Inv in Q range")
207        self.high_inv_percent = self.qstar_container.qstar_high_percent
208        self.high_scale = self.get_scale(percentage=self.high_inv_percent,
209                                         scale_name="Extrapolated at High Q")
210
211        # Default color the extrapolation bar is grey
212        self.extrapolation_color_low = EXTRAPOLATION_COLOR
213        self.extrapolation_color_high = EXTRAPOLATION_COLOR
214        self.invariant_color = INVARIANT_COLOR
215        # change color of high and low bar when necessary
216        self.set_color_bar()
217        # draw the panel itself
218        self._do_layout()
219        self.set_values()
220
221    def _define_structure(self):
222        """
223        Define main sizers needed for this panel
224        """
225        # Box sizers must be defined first before defining buttons/textctrls
226        # (MAC).
227        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
228        # Sizer related to chart
229        chart_box = wx.StaticBox(self, -1, "Invariant Chart")
230        self.chart_sizer = wx.StaticBoxSizer(chart_box, wx.VERTICAL)
231        self.chart_sizer.SetMinSize((PANEL_WIDTH - 50, 110))
232        # Sizer related to invariant values
233        self.invariant_sizer = wx.GridBagSizer(4, 4)
234        invariant_box = wx.StaticBox(self, -1, "Numerical Values")
235        self.invariant_box_sizer = wx.StaticBoxSizer(invariant_box, wx.HORIZONTAL)
236
237        self.invariant_box_sizer.SetMinSize((PANEL_WIDTH - 50, -1))
238        # Sizer related to warning message
239        warning_box = wx.StaticBox(self, -1, "Warning")
240        self.warning_sizer = wx.StaticBoxSizer(warning_box, wx.VERTICAL)
241        self.warning_sizer.SetMinSize((PANEL_WIDTH - 50, -1))
242        # Sizer related to button
243        self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
244
245    def _layout_shart(self):
246        """
247        Draw widgets related to chart
248        """
249        self.panel_chart = wx.Panel(self)
250        self.panel_chart.Bind(wx.EVT_PAINT, self.on_paint)
251        self.chart_sizer.Add(self.panel_chart, 1, wx.EXPAND | wx.ALL, 0)
252
253    def _layout_invariant(self):
254        """
255        Draw widgets related to invariant
256        """
257        uncertainty = "+/-"
258        unit_invariant = '[1/(cm * A^3)]'
259
260        invariant_txt = wx.StaticText(self, -1, 'Q* from Data ')
261        invariant_txt.SetToolTipString("Invariant in the data set's Q range.")
262        self.invariant_tcl = OutputTextCtrl(self, -1, size=(_BOX_WIDTH, -1))
263        hint_msg = "Invariant in the data set's Q range."
264        self.invariant_tcl.SetToolTipString(hint_msg)
265        self.invariant_err_tcl = OutputTextCtrl(self, -1, size=(_BOX_WIDTH, -1))
266        hint_msg = "Uncertainty on the invariant from data's range."
267        self.invariant_err_tcl.SetToolTipString(hint_msg)
268        invariant_units_txt = wx.StaticText(self, -1, unit_invariant)
269        hint_msg = "Unit of the invariant from data's Q range"
270        invariant_units_txt.SetToolTipString(hint_msg)
271
272        invariant_low_txt = wx.StaticText(self, -1, 'Q* from Low-Q')
273        hint_msg = "Extrapolated invariant from low-Q range."
274        invariant_low_txt.SetToolTipString(hint_msg)
275        self.invariant_low_tcl = OutputTextCtrl(self, -1, size=(_BOX_WIDTH, -1))
276        hint_msg = "Extrapolated invariant from low-Q range."
277        self.invariant_low_tcl.SetToolTipString(hint_msg)
278        self.invariant_low_err_tcl = OutputTextCtrl(self, -1,
279                                                    size=(_BOX_WIDTH, -1))
280        hint_msg = "Uncertainty on the invariant from low-Q range."
281        self.invariant_low_err_tcl.SetToolTipString(hint_msg)
282        invariant_low_units_txt = wx.StaticText(self, -1, unit_invariant)
283        hint_msg = "Unit of the extrapolated invariant from  low-Q range"
284        invariant_low_units_txt.SetToolTipString(hint_msg)
285
286        invariant_high_txt = wx.StaticText(self, -1, 'Q* from High-Q')
287        hint_msg = "Extrapolated invariant from  high-Q range"
288        invariant_high_txt.SetToolTipString(hint_msg)
289        self.invariant_high_tcl = OutputTextCtrl(self, -1,
290                                                 size=(_BOX_WIDTH, -1))
291        hint_msg = "Extrapolated invariant from  high-Q range"
292        self.invariant_high_tcl.SetToolTipString(hint_msg)
293        self.invariant_high_err_tcl = OutputTextCtrl(self, -1,
294                                                     size=(_BOX_WIDTH, -1))
295        hint_msg = "Uncertainty on the invariant from high-Q range."
296        self.invariant_high_err_tcl.SetToolTipString(hint_msg)
297        invariant_high_units_txt = wx.StaticText(self, -1, unit_invariant)
298        hint_msg = "Unit of the extrapolated invariant from  high-Q range"
299        invariant_high_units_txt.SetToolTipString(hint_msg)
300
301        # Invariant low
302        iy = 0
303        ix = 0
304        self.invariant_sizer.Add(invariant_low_txt, (iy, ix), (1, 1),
305                                 wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
306        ix += 1
307        self.invariant_sizer.Add(self.invariant_low_tcl, (iy, ix), (1, 1),
308                                 wx.EXPAND | wx.ADJUST_MINSIZE, 0)
309        ix += 1
310        self.invariant_sizer.Add(wx.StaticText(self, -1, uncertainty),
311                                 (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
312        ix += 1
313        self.invariant_sizer.Add(self.invariant_low_err_tcl, (iy, ix), (1, 1),
314                                 wx.EXPAND | wx.ADJUST_MINSIZE, 0)
315        ix += 1
316        self.invariant_sizer.Add(invariant_low_units_txt,
317                                 (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
318        # Invariant
319        iy += 1
320        ix = 0
321        self.invariant_sizer.Add(invariant_txt, (iy, ix), (1, 1),
322                                 wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
323        ix += 1
324        self.invariant_sizer.Add(self.invariant_tcl, (iy, ix), (1, 1),
325                                 wx.EXPAND | wx.ADJUST_MINSIZE, 0)
326        ix += 1
327        self.invariant_sizer.Add(wx.StaticText(self, -1, uncertainty),
328                                 (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
329        ix += 1
330        self.invariant_sizer.Add(self.invariant_err_tcl, (iy, ix), (1, 1),
331                                 wx.EXPAND | wx.ADJUST_MINSIZE, 0)
332        ix += 1
333        self.invariant_sizer.Add(invariant_units_txt,
334                                 (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
335        # Invariant high
336        iy += 1
337        ix = 0
338        self.invariant_sizer.Add(invariant_high_txt, (iy, ix), (1, 1),
339                                 wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
340        ix += 1
341        self.invariant_sizer.Add(self.invariant_high_tcl, (iy, ix), (1, 1),
342                                 wx.EXPAND | wx.ADJUST_MINSIZE, 0)
343        ix += 1
344        self.invariant_sizer.Add(wx.StaticText(self, -1, uncertainty),
345                                 (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
346        ix += 1
347        self.invariant_sizer.Add(self.invariant_high_err_tcl, (iy, ix), (1, 1),
348                                 wx.EXPAND | wx.ADJUST_MINSIZE, 0)
349        ix += 1
350        self.invariant_sizer.Add(invariant_high_units_txt,
351                                 (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
352        self.invariant_box_sizer.Add(self.invariant_sizer, 0, wx.TOP | wx.BOTTOM, 10)
353
354    def _layout_warning(self):
355        """
356        Draw widgets related to warning
357        """
358        # Warning [string]
359        self.warning_msg_txt = wx.StaticText(self, -1, self.warning_msg)
360        if self.qstar_container.existing_warning:
361            self.warning_msg_txt.SetForegroundColour('red')
362        self.warning_sizer.AddMany([(self.warning_msg_txt, 0,
363                                     wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 10)])
364
365    def _layout_button(self):
366        """
367        Draw widgets related to button
368        """
369        # Close button
370        id = wx.NewId()
371        button_ok = wx.Button(self, id, "Ok")
372        button_ok.SetToolTipString("Give Details on Computation")
373        self.Bind(wx.EVT_BUTTON, self.on_close, id=id)
374        self.button_sizer.AddMany([((20, 20), 0, wx.LEFT, 350),
375                                   (button_ok, 0, wx.RIGHT, 10)])
376    def _do_layout(self):
377        """
378        Draw window content
379        """
380        self._define_structure()
381        self._layout_shart()
382        self._layout_invariant()
383        self._layout_warning()
384        self._layout_button()
385        self.main_sizer.AddMany([(self.chart_sizer, 0, wx.ALL, 10),
386                                 (self.invariant_box_sizer, 0, wx.ALL, 10),
387                                 (self.warning_sizer, 0, wx.ALL, 10),
388                                 (self.button_sizer, 0, wx.ALL, 10)])
389        self.SetSizer(self.main_sizer)
390
391
392    def set_values(self):
393        """
394        Set value of txtcrtl
395        """
396        value = format_number(self.qstar_container.qstar)
397        self.invariant_tcl.SetValue(value)
398        value = format_number(self.qstar_container.qstar_err)
399        self.invariant_err_tcl.SetValue(value)
400        value = format_number(self.qstar_container.qstar_low)
401        self.invariant_low_tcl.SetValue(value)
402        value = format_number(self.qstar_container.qstar_low_err)
403        self.invariant_low_err_tcl.SetValue(value)
404        value = format_number(self.qstar_container.qstar_high)
405        self.invariant_high_tcl.SetValue(value)
406        value = format_number(self.qstar_container.qstar_high_err)
407        self.invariant_high_err_tcl.SetValue(value)
408
409    def get_scale(self, percentage, scale_name='scale'):
410        """
411        Check scale receive in this panel.
412        """
413        scale = RECTANGLE_SCALE
414        try:
415            if percentage in [None, 0.0, "error"]:
416                scale = RECTANGLE_SCALE
417                return scale
418            elif percentage < 0:
419                scale = RECTANGLE_SCALE
420                return scale
421            scale = float(percentage)
422        except:
423            scale = RECTANGLE_SCALE
424            self.warning_msg += "Recieve an invalid scale for %s\n"
425            self.warning_msg += "check this value : %s\n" % str(percentage)
426        return  scale
427
428    def set_color_bar(self):
429        """
430        Change the color for low and high bar when necessary
431        """
432        self.extrapolation_color_low = EXTRAPOLATION_COLOR
433        self.extrapolation_color_high = EXTRAPOLATION_COLOR
434        self.invariant_color = INVARIANT_COLOR
435        # warning to the user when the extrapolated invariant is greater than %5
436        if self.low_scale >= 0.05 or self.low_scale > 1 or self.low_scale < 0:
437            self.extrapolation_color_low = ERROR_COLOR
438        if self.high_scale >= 0.05 or self.high_scale > 1 or self.high_scale < 0:
439            self.extrapolation_color_high = ERROR_COLOR
440        if self.inv_scale > 1 or self.inv_scale < 0:
441            self.invariant_color = ERROR_COLOR
442
443    def on_close(self, event):
444        """
445        Close the current window
446        """
447        self.Close()
448
449    def on_paint(self, event):
450        """
451        Draw the chart
452        """
453        dc = wx.PaintDC(self.panel_chart)
454        try:
455            gc = wx.GraphicsContext.Create(dc)
456        except NotImplementedError:
457            msg = "This build of wxPython does not support "
458            msg += "the wx.GraphicsContext family of classes."
459            dc.DrawText(msg, 25, 25)
460            return
461        # Start the drawing
462        font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
463        font.SetWeight(wx.BOLD)
464        gc.SetFont(font)
465        # Draw a rectangle
466        path = gc.CreatePath()
467        path.AddRectangle(-RECTANGLE_WIDTH / 2, -RECTANGLE_HEIGHT / 2,
468                          RECTANGLE_WIDTH / 2, RECTANGLE_HEIGHT / 2)
469        x_origine = 20
470        y_origine = 15
471        # Draw low rectangle
472        gc.PushState()
473        label = "Q* from Low-Q "
474        PathFunc = gc.DrawPath
475        w, h = gc.GetTextExtent(label)
476        gc.DrawText(label, x_origine, y_origine)
477        # Translate the rectangle
478        x_center = x_origine + RECTANGLE_WIDTH * self.low_scale / 2 + w + 10
479        y_center = y_origine + h
480        gc.Translate(x_center, y_center)
481        gc.SetPen(wx.Pen("black", 1))
482        gc.SetBrush(wx.Brush(self.extrapolation_color_low))
483        if self.low_inv_percent is None:
484            low_percent = 'Not Computed'
485        elif self.low_inv_percent == 'error':
486            low_percent = 'Error'
487        else:
488            low_percent = format_number(self.low_inv_percent * 100) + '%'
489        x_center = 20
490        y_center = -h
491        gc.DrawText(low_percent, x_center, y_center)
492        # Increase width by self.low_scale
493        gc.Scale(self.low_scale, 1.0)
494        PathFunc(path)
495        gc.PopState()
496        # Draw rectangle for invariant
497        gc.PushState()  # save it again
498        y_origine += 20
499        gc.DrawText("Q* from Data ", x_origine, y_origine)
500        # offset to the lower part of the window
501        x_center = x_origine + RECTANGLE_WIDTH * self.inv_scale / 2 + w + 10
502        y_center = y_origine + h
503        gc.Translate(x_center, y_center)
504        # 128 == half transparent
505        gc.SetBrush(wx.Brush(self.invariant_color))
506        # Increase width by self.inv_scale
507        if self.inv_percent is None:
508            inv_percent = 'Not Computed'
509        elif self.inv_percent == 'error':
510            inv_percent = 'Error'
511        else:
512            inv_percent = format_number(self.inv_percent * 100) + '%'
513        x_center = 20
514        y_center = -h
515        gc.DrawText(inv_percent, x_center, y_center)
516        gc.Scale(self.inv_scale, 1.0)
517        gc.DrawPath(path)
518        gc.PopState()
519        # restore saved state
520        # Draw rectangle for high invariant
521        gc.PushState()
522        y_origine += 20
523        gc.DrawText("Q* from High-Q ", x_origine, y_origine)
524        # define the position of the new rectangle
525        x_center = x_origine + RECTANGLE_WIDTH * self.high_scale / 2 + w + 10
526        y_center = y_origine + h
527        gc.Translate(x_center, y_center)
528        gc.SetBrush(wx.Brush(self.extrapolation_color_high))
529        # increase scale by self.high_scale
530        if self.high_inv_percent is None:
531            high_percent = 'Not Computed'
532        elif self.high_inv_percent == 'error':
533            high_percent = 'Error'
534        else:
535            high_percent = format_number(self.high_inv_percent * 100) + '%'
536        x_center = 20
537        y_center = -h
538        gc.DrawText(high_percent, x_center, y_center)
539
540        gc.Scale(self.high_scale, 1.0)
541        gc.DrawPath(path)
542        gc.PopState()
Note: See TracBrowser for help on using the repository browser.