source: sasview/src/sas/perspectives/calculator/density_panel.py @ 49ab5d7

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 49ab5d7 was 49ab5d7, checked in by Doucet, Mathieu <doucetm@…>, 5 years ago

Take care of white spaces (pylint)

  • Property mode set to 100644
File size: 15.8 KB
Line 
1"""
2This module provide GUI for the mass density calculator
3
4"""
5import wx
6import sys
7from sas.guiframe.panel_base import PanelBase
8from wx.lib.scrolledpanel import ScrolledPanel
9from sas.guiframe.utils import check_float
10from sas.guiframe.events import StatusEvent
11from periodictable import formula as Formula
12from sas.perspectives.calculator import calculator_widgets as widget
13from sas.guiframe.documentation_window import DocumentationWindow
14
15AVOGADRO = 6.02214129e23
16_INPUTS = ['Mass Density', 'Molar Volume']
17_UNITS = ['g/cm^(3)     ', 'cm^(3)/mol ']
18#Density panel size
19if sys.platform.count("win32") > 0:
20    _STATICBOX_WIDTH = 410
21    _BOX_WIDTH = 200
22    PANEL_SIZE = 440
23    FONT_VARIANT = 0
24else:
25    _STATICBOX_WIDTH = 430
26    _BOX_WIDTH = 200
27    PANEL_SIZE = 460
28    FONT_VARIANT = 1
29
30class DensityPanel(ScrolledPanel, PanelBase):
31    """
32    Provides the mass density calculator GUI.
33    """
34    ## Internal nickname for the window, used by the AUI manager
35    window_name = "Mass Density Calculator"
36    ## Name to appear on the window title bar
37    window_caption = "Mass Density Calculator"
38    ## Flag to tell the AUI manager to put this panel in the center pane
39    CENTER_PANE = True
40
41    def __init__(self, parent, base=None, *args, **kwds):
42        """
43        """
44        ScrolledPanel.__init__(self, parent, *args, **kwds)
45        PanelBase.__init__(self)
46        self.SetupScrolling()
47        #Font size
48        self.SetWindowVariant(variant=FONT_VARIANT)
49        # Object that receive status event
50        self.base = base
51        # chemeical formula, string
52        self.compound = ''
53        # value of the density/volume, float
54        self.input = None
55        # text controls
56        self.compound_ctl = None
57        self.input_ctl = None
58        self.molar_mass_ctl = None
59        self.output_ctl = None
60        self.ctr_color = self.GetBackgroundColour()
61        # button
62        self.button_calculate = None
63        # list
64        self._input_list = _INPUTS
65        self._input = self._input_list[1]
66        self._output = self._input_list[0]
67        self._unit_list = _UNITS
68        #Draw the panel
69        self._do_layout()
70        self.SetAutoLayout(True)
71        self.Layout()
72
73    def _do_layout(self):
74        """
75        Draw window content
76        """
77        # units
78        unit_density = self._unit_list[0]
79        unit_volume = self._unit_list[1]
80
81        # sizers
82        sizer_input = wx.GridBagSizer(5, 5)
83        sizer_output = wx.GridBagSizer(5, 5)
84        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
85        self.sizer1 = wx.BoxSizer(wx.HORIZONTAL)
86        self.sizer2 = wx.BoxSizer(wx.HORIZONTAL)
87        sizer3 = wx.BoxSizer(wx.HORIZONTAL)
88        vbox = wx.BoxSizer(wx.VERTICAL)
89
90        # inputs
91        inputbox = wx.StaticBox(self, -1, "Inputs")
92        boxsizer1 = wx.StaticBoxSizer(inputbox, wx.VERTICAL)
93        boxsizer1.SetMinSize((_STATICBOX_WIDTH, -1))
94        compound_txt = wx.StaticText(self, -1, 'Molecular Formula ')
95        self.compound_ctl = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, -1))
96        self.compound_eg1 = wx.StaticText(self, -1, '     e.g., H2O')
97        self.compound_eg2 = wx.StaticText(self, -1, 'e.g., D2O')
98        self.input_cb = wx.ComboBox(self, -1, style=wx.CB_READONLY)
99        wx.EVT_COMBOBOX(self.input_cb, -1, self.on_select_input)
100        hint_input_name_txt = 'Mass or volume.'
101        self.input_cb.SetToolTipString(hint_input_name_txt)
102        unit_density1 = "     " + unit_density
103        self.input_ctl = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, -1))
104        self.unit_input_density = wx.StaticText(self, -1, unit_density1)
105        self.unit_input_volume = wx.StaticText(self, -1, unit_volume)
106        iy = 0
107        ix = 0
108        sizer_input.Add(compound_txt, (iy, ix), (1, 1),
109                             wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
110        ix += 1
111        sizer_input.Add(self.compound_ctl, (iy, ix), (1, 1),
112                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
113        ix += 1
114        sizer_input.Add(self.compound_eg1, (iy, ix), (1, 1),
115                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
116
117        ix += 1
118        sizer_input.Add(self.compound_eg2, (iy, ix), (1, 1),
119                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
120        self.compound_eg1.Show(False)
121        iy += 1
122        ix = 0
123        sizer_input.Add(self.input_cb, (iy, ix), (1, 1),
124                             wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
125        ix += 1
126        sizer_input.Add(self.input_ctl, (iy, ix), (1, 1),
127                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
128        ix += 1
129        sizer_input.Add(self.unit_input_density, (iy, ix), (1, 1),
130                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
131        ix += 1
132        self.unit_input_density.Show(False)
133        sizer_input.Add(self.unit_input_volume, (iy, ix), (1, 1),
134                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
135        boxsizer1.Add(sizer_input)
136        self.sizer1.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
137
138        # outputs
139        outputbox = wx.StaticBox(self, -1, "Outputs")
140        boxsizer2 = wx.StaticBoxSizer(outputbox, wx.VERTICAL)
141        boxsizer2.SetMinSize((_STATICBOX_WIDTH, -1))
142
143        molar_mass_txt = wx.StaticText(self, -1, 'Molar Mass ')
144        self.molar_mass_ctl = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, -1))
145        self.molar_mass_ctl.SetEditable(False)
146        self.molar_mass_ctl.SetBackgroundColour(self.ctr_color)
147        self.molar_mass_unit1 = wx.StaticText(self, -1, '     g/mol')
148        self.molar_mass_unit2 = wx.StaticText(self, -1, 'g/mol')
149
150        self.output_cb = wx.ComboBox(self, -1, style=wx.CB_READONLY)
151        wx.EVT_COMBOBOX(self.output_cb, -1, self.on_select_output)
152        hint_output_name_txt = 'Mass or volume.'
153        self.output_cb.SetToolTipString(hint_output_name_txt)
154        list = []
155        for item in self._input_list:
156            name = str(item)
157            list.append(name)
158        list.sort()
159        for idx in range(len(list)):
160            self.input_cb.Append(list[idx], idx)
161            self.output_cb.Append(list[idx], idx)
162        self.input_cb.SetStringSelection("Molar Volume")
163        self.output_cb.SetStringSelection("Mass Density")
164        unit_volume = "     " + unit_volume
165        self.output_ctl = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, -1))
166        self.output_ctl.SetEditable(False)
167        self.output_ctl.SetBackgroundColour(self.ctr_color)
168        self.unit_output_density = wx.StaticText(self, -1, unit_density)
169        self.unit_output_volume = wx.StaticText(self, -1, unit_volume)
170        iy = 0
171        ix = 0
172        sizer_output.Add(molar_mass_txt, (iy, ix), (1, 1),
173                             wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
174        ix += 1
175        sizer_output.Add(self.molar_mass_ctl, (iy, ix), (1, 1),
176                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
177        ix += 1
178        sizer_output.Add(self.molar_mass_unit1, (iy, ix), (1, 1),
179                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
180        ix += 1
181        sizer_output.Add(self.molar_mass_unit2, (iy, ix), (1, 1),
182                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
183        self.molar_mass_unit1.Show(False)
184        iy += 1
185        ix = 0
186        sizer_output.Add(self.output_cb, (iy, ix), (1, 1),
187                             wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
188        ix += 1
189        sizer_output.Add(self.output_ctl, (iy, ix), (1, 1),
190                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
191        ix += 1
192        sizer_output.Add(self.unit_output_volume,
193                         (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
194        ix += 1
195        sizer_output.Add(self.unit_output_density,
196                         (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
197
198        self.unit_output_volume.Show(False)
199        boxsizer2.Add(sizer_output)
200        self.sizer2.Add(boxsizer2, 0, wx.EXPAND | wx.ALL, 10)
201
202        # buttons
203        id = wx.NewId()
204        self.button_calculate = wx.Button(self, id, "Calculate")
205        self.button_calculate.SetToolTipString("Calculate.")
206        self.Bind(wx.EVT_BUTTON, self.calculate, id=id)
207
208        id = wx.NewId()
209        self.button_help = wx.Button(self, id, "HELP")
210        self.button_help.SetToolTipString("Help for density calculator.")
211        self.Bind(wx.EVT_BUTTON, self.on_help, id=id)
212
213        sizer_button.Add((150, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0)
214        sizer_button.Add(self.button_calculate, 0,
215                                        wx.RIGHT | wx.ADJUST_MINSIZE, 20)
216        sizer_button.Add(self.button_help, 0,
217                                        wx.RIGHT | wx.ADJUST_MINSIZE, 20)
218        sizer3.Add(sizer_button)
219
220        # layout
221        vbox.Add(self.sizer1)
222        vbox.Add(self.sizer2)
223        vbox.Add(sizer3)
224        vbox.Fit(self)
225        self.SetSizer(vbox)
226
227    def on_select_input(self, event):
228        """
229        On selection of input combobox,
230        update units and output combobox
231        """
232        if event == None:
233            return
234        event.Skip()
235
236        combo = event.GetEventObject()
237        self._input = combo.GetValue()
238        for name in self._input_list:
239            if self._input != name:
240                self._output = name
241                break
242
243        self.set_values()
244
245    def on_select_output(self, event):
246        """
247        On selection of output combobox,
248        update units and input combobox
249        """
250        if event == None:
251            return
252        event.Skip()
253
254        combo = event.GetEventObject()
255        self._output = combo.GetValue()
256        for name in self._input_list:
257            if self._output != name:
258                self._input = name
259                break
260
261        self.set_values()
262
263    def set_values(self):
264        """
265        Sets units and combobox values
266        """
267        input, output = self.get_input()
268        if input is None:
269            return
270        # input
271        self.input_cb.SetValue(str(input))
272        # output
273        self.output_cb.SetValue(str(output))
274        # unit
275        if self._input_list.index(input) == 0:
276            self.molar_mass_unit1.Show(True)
277            self.molar_mass_unit2.Show(False)
278            self.compound_eg1.Show(True)
279            self.compound_eg2.Show(False)
280            self.unit_input_density.Show(True)
281            self.unit_output_volume.Show(True)
282            self.unit_input_volume.Show(False)
283            self.unit_output_density.Show(False)
284        else:
285            self.molar_mass_unit1.Show(False)
286            self.molar_mass_unit2.Show(True)
287            self.compound_eg1.Show(False)
288            self.compound_eg2.Show(True)
289            self.unit_input_volume.Show(True)
290            self.unit_output_density.Show(True)
291            self.unit_input_density.Show(False)
292            self.unit_output_volume.Show(False)
293        # layout   
294        self.clear_outputs()
295        self.sizer1.Layout()
296        self.sizer2.Layout()
297
298    def get_input(self):
299        """
300        Return the current input and output combobox values
301        """
302        return self._input, self._output
303
304    def check_inputs(self):
305        """
306        Check validity user inputs
307        """
308        flag = True
309        msg = ""
310        if check_float(self.input_ctl):
311            self.input = float(self.input_ctl.GetValue())
312        else:
313            flag = False
314            input_type = str(self.input_cb.GetValue())
315            msg += "Error for %s value :expect float" % input_type
316
317        self.compound = self.compound_ctl.GetValue().lstrip().rstrip()
318        if self.compound != "":
319            try :
320                Formula(self.compound)
321                self.compound_ctl.SetBackgroundColour(wx.WHITE)
322                self.compound_ctl.Refresh()
323            except:
324                self.compound_ctl.SetBackgroundColour("pink")
325                self.compound_ctl.Refresh()
326                flag = False
327                msg += "Enter correct formula"
328        else:
329            self.compound_ctl.SetBackgroundColour("pink")
330            self.compound_ctl.Refresh()
331            flag = False
332            msg += "Enter Formula"
333        return flag, msg
334
335
336    def calculate(self, event):
337        """
338        Calculate the mass Density/molar Volume of the molecules
339        """
340        self.clear_outputs()
341        try:
342            #Check validity user inputs
343            flag, msg = self.check_inputs()
344            if self.base is not None and msg.lstrip().rstrip() != "":
345                msg = "Density/Volume Calculator: %s" % str(msg)
346                wx.PostEvent(self.base, StatusEvent(status=msg))
347            if not flag:
348               return
349            #get ready to compute
350            mol_formula = Formula(self.compound)
351            molar_mass = float(mol_formula.molecular_mass) * AVOGADRO
352            output = self._format_number(molar_mass / self.input)
353            self.molar_mass_ctl.SetValue(str(self._format_number(molar_mass)))
354            self.output_ctl.SetValue(str(output))
355        except:
356            if self.base is not None:
357                msg = "Density/Volume Calculator: %s" % (sys.exc_value)
358                wx.PostEvent(self.base, StatusEvent(status=msg))
359        if event is not None:
360            event.Skip()
361
362    def on_help(self, event):
363        """
364        Bring up the density/volume calculator Documentation whenever
365        the HELP button is clicked.
366
367        Calls DocumentationWindow with the path of the location within the
368        documentation tree (after /doc/ ....".  Note that when using old
369        versions of Wx (before 2.9) and thus not the release version of
370        installers, the help comes up at the top level of the file as
371        webbrowser does not pass anything past the # to the browser when it is
372        running "file:///...."
373
374    :param evt: Triggers on clicking the help button
375    """
376
377        _TreeLocation = "user/perspectives/calculator/density_calculator_help.html"
378        _doc_viewer = DocumentationWindow(self, -1, \
379             _TreeLocation, "Density/Volume Calculator Help")
380
381    def clear_outputs(self):
382        """
383        Clear the outputs textctrl
384        """
385        self.molar_mass_ctl.SetValue("")
386        self.output_ctl.SetValue("")
387
388    def _format_number(self, value=None):
389        """
390        Return a float in a standardized, human-readable formatted string
391        """
392        try:
393            value = float(value)
394        except:
395            output = ''
396            return output
397
398        output = "%-12.5f" % value
399        return output.lstrip().rstrip()
400
401class DensityWindow(widget.CHILD_FRAME):
402    """
403    """
404    def __init__(self, parent=None, title="Density/Volume Calculator",
405                  base=None, manager=None,
406                  size=(PANEL_SIZE * 1.05, PANEL_SIZE / 1.55), *args, **kwds):
407        """
408        """
409        kwds['title'] = title
410        kwds['size'] = size
411        widget.CHILD_FRAME.__init__(self, parent, *args, **kwds)
412        """
413        """
414        self.manager = manager
415        self.panel = DensityPanel(self, base=base)
416        self.Bind(wx.EVT_CLOSE, self.on_close)
417        self.SetPosition((25, 10))
418        self.Show(True)
419
420    def on_close(self, event):
421        """
422        On close event
423        """
424        if self.manager != None:
425            self.manager.cal_md_frame = None
426        self.Destroy()
427
428
429class ViewApp(wx.App):
430    """
431    """
432    def OnInit(self):
433        """
434        """
435        widget.CHILD_FRAME = wx.Frame
436        frame = DensityWindow(None, title="Density/Volume Calculator")
437        frame.Show(True)
438        self.SetTopWindow(frame)
439        return True
440
441
442if __name__ == "__main__":
443    app = ViewApp(0)
444    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.