source: sasview/src/sas/perspectives/calculator/density_panel.py @ 7a04dbb

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 7a04dbb was 79492222, checked in by krzywon, 10 years ago

Changed the file and folder names to remove all SANS references.

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