source: sasview/calculatorview/src/sans/perspectives/calculator/gen_scatter_panel.py @ 644f95b

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 644f95b was 644f95b, checked in by Jae Cho <jhjcho@…>, 11 years ago

minor fix in atomic sld plot

  • Property mode set to 100644
File size: 70.5 KB
Line 
1"""
2Generic Scattering panel.
3This module relies on guiframe manager.
4"""
5
6import wx
7import sys
8import os
9import numpy
10#import math
11import wx.aui as aui
12#import wx.lib.agw.aui as aui
13import logging
14import time
15
16import matplotlib
17matplotlib.interactive(False)
18#Use the WxAgg back end. The Wx one takes too long to render
19matplotlib.use('WXAgg')
20
21from data_util.calcthread import CalcThread
22from sans.guiframe.local_perspectives.plotting.SimplePlot import PlotFrame
23from sans.guiframe.dataFitting import Data2D
24from sans.dataloader.data_info import Detector
25from sans.dataloader.data_info import Source
26from sans.guiframe.panel_base import PanelBase
27from sans.guiframe.utils import format_number
28from sans.guiframe.events import StatusEvent 
29from sans.calculator import sans_gen
30from sans.perspectives.calculator.calculator_widgets import OutputTextCtrl
31from sans.perspectives.calculator.calculator_widgets import InputTextCtrl
32from wx.lib.scrolledpanel import ScrolledPanel
33from sans.perspectives.calculator.load_thread import GenReader
34from danse.common.plottools.arrow3d import Arrow3D
35
36_BOX_WIDTH = 76
37#Slit length panel size
38if sys.platform.count("win32") > 0:
39    PANEL_WIDTH = 570
40    PANEL_HEIGHT = 370
41    FONT_VARIANT = 0
42else:
43    PANEL_WIDTH = 620
44    PANEL_HEIGHT = 370
45    FONT_VARIANT = 1
46_QMAX_DEFAULT = 0.3
47_NPTS_DEFAULT = 50 
48
49def add_icon(parent, frame):
50    """
51    Add icon in the frame
52    """
53    if parent != None:
54        if hasattr(frame, "IsIconized"):
55            if not frame.IsIconized():
56                try:
57                    icon = parent.GetIcon()
58                    frame.SetIcon(icon)
59                except:
60                    pass 
61
62def _set_error(panel, item, show_msg=False):
63    """
64    Set_error dialog
65    """
66    if item != None:
67        item.SetBackgroundColour("pink")
68        item.Refresh()
69    if show_msg:
70        msg = "Error: wrong (or out of range) value entered."
71        if panel.parent.parent != None:
72            wx.PostEvent(panel.parent.parent, 
73                     StatusEvent(status=msg, info='Error' )) 
74            panel.SetFocus()
75    return False
76
77class CalcGen(CalcThread):
78    """
79    Computation
80    """
81    def __init__(self,
82                 id=-1,
83                 input = None,
84                 completefn = None,
85                 updatefn   = None,
86                 elapsed = 0,
87                 yieldtime  = 0.005,
88                 worktime   = 0.01):
89        """
90        """
91        CalcThread.__init__(self, completefn,
92                 updatefn,
93                 yieldtime,
94                 worktime)
95        self.starttime = 0
96        self.id = id 
97        self.input = input 
98       
99    def compute(self):
100        """
101        excuting computation
102        """
103        elapsed = time.time() - self.starttime
104       
105        self.complete(input=self.input,
106                      elapsed = elapsed)
107           
108class SasGenPanel(ScrolledPanel, PanelBase):
109    """
110        Provides the sas gen calculator GUI.
111    """
112    ## Internal nickname for the window, used by the AUI manager
113    window_name = "Generic SANS Calculator"
114    ## Name to appear on the window title bar
115    window_caption = "Generic SANS "
116    ## Flag to tell the AUI manager to put this panel in the center pane
117    CENTER_PANE = True
118   
119    def __init__(self, parent, *args, **kwds):
120        ScrolledPanel.__init__(self, parent, style=wx.RAISED_BORDER, 
121                               *args, **kwds)
122        #kwds['style'] = wx.SUNKEN_BORDER
123        PanelBase.__init__(self)
124        #Font size
125        self.SetWindowVariant(variant=FONT_VARIANT)
126        self.SetupScrolling()
127        #thread to read data
128        self.reader = None
129        self.ext = None
130        self.id = 'Generic Scattering'
131        self.file_name = ''
132        self.time_text = None
133        self.orient_combo = None
134        self.omfreader = sans_gen.OMFReader()
135        self.sldreader = sans_gen.SLDReader()
136        self.pdbreader = sans_gen.PDBReader()
137        self.model = sans_gen.GenSAS()
138        self.param_dic = self.model.params
139        self.parameters = []
140        self.data = None
141        self.scale2d = None
142        self.plot_frame = None
143        self.qmax_x = _QMAX_DEFAULT
144        self.npts_x = _NPTS_DEFAULT
145        self.sld_data = None
146        self.graph_num = 1
147        self.default_shape = 'rectangular'
148        # Object that receive status event
149        self.parent = parent
150        self._do_layout()
151        self._create_default_sld_data()
152        self._create_default_2d_data()
153        wx.CallAfter(self._set_sld_data_helper)
154       
155    def _define_structure(self):
156        """
157            Define the main sizers building to build this application.
158        """
159        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
160        self.box_source = wx.StaticBox(self, -1, str("SLD Data File"))
161        self.box_parameters = wx.StaticBox(self, -1, str("Input Parameters"))
162        self.box_qrange = wx.StaticBox(self, -1, str("Q Range"))
163        self.boxsizer_source = wx.StaticBoxSizer(self.box_source,
164                                                    wx.VERTICAL)
165        self.boxsizer_parameters = wx.StaticBoxSizer(self.box_parameters,
166                                                    wx.VERTICAL)
167        self.boxsizer_qrange = wx.StaticBoxSizer(self.box_qrange,
168                                                    wx.VERTICAL)
169        self.data_name_sizer = wx.BoxSizer(wx.HORIZONTAL)
170        self.param_sizer = wx.BoxSizer(wx.HORIZONTAL)
171        self.shape_sizer = wx.BoxSizer(wx.HORIZONTAL)
172        self.hint_sizer = wx.BoxSizer(wx.HORIZONTAL)
173        self.qrange_sizer = wx.BoxSizer(wx.HORIZONTAL)
174        self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
175       
176    def _layout_data_name(self):
177        """
178            Fill the sizer containing data's name
179        """
180        data_name_txt = wx.StaticText(self, -1, 'Data: ')
181        self.data_name_tcl = OutputTextCtrl(self, -1, 
182                                            size=(_BOX_WIDTH * 4, -1))
183        data_hint = "Loaded data"
184        self.data_name_tcl.SetToolTipString(data_hint)
185        #control that triggers importing data
186        id = wx.NewId()
187        self.browse_button = wx.Button(self, id, "Load")
188        hint_on_browse = "Click on this button to load a data in this panel."
189        #self.browse_button.SetToolTipString(hint_on_browse)
190        self.Bind(wx.EVT_BUTTON, self.on_load_data, id=id)
191        self.data_name_sizer.AddMany([(data_name_txt, 0, wx.LEFT, 15),
192                                      (self.data_name_tcl, 0, wx.LEFT, 10),
193                                      (self.browse_button, 0, wx.LEFT, 10)])
194    def _layout_param_size(self):
195        """
196            Fill the sizer containing slit size information
197        """
198        self.parameters = []
199        sizer = wx.GridBagSizer(3, 6)
200        model = self.model
201        details = self.model.details
202        params = self.model.params
203        ix = 0
204        iy = 0
205        param_title = wx.StaticText(self, -1, 'Parameter')
206        sizer.Add(param_title, (iy, ix), (1, 1), \
207                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
208        ix += 1
209        value_title = wx.StaticText(self, -1, 'Value')
210        sizer.Add(value_title, (iy, ix), (1, 1), \
211                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
212        ix += 1
213        unit_title = wx.StaticText(self, -1, 'Unit')
214        sizer.Add(unit_title, (iy, ix), (1, 1), \
215                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
216        key_list = params.keys()
217        key_list.sort()
218        for param in key_list:
219            iy += 1
220            ix = 0
221            p_name = wx.StaticText(self, -1, param)
222            sizer.Add(p_name, (iy, ix), (1, 1), \
223                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
224            ## add parameter value
225            ix += 1
226            value = model.getParam(param)
227            ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH * 2, 20),
228                                style=wx.TE_PROCESS_ENTER)
229            #ctl.SetToolTipString(\
230            #            "Hit 'Enter' after typing to update the plot.")
231            ctl.SetValue(format_number(value, True))
232            sizer.Add(ctl, (iy, ix), (1, 1), wx.EXPAND)
233            ## add unit
234            ix += 1
235            unit = wx.StaticText(self, -1, details[param][0])
236            sizer.Add(unit, (iy, ix), (1, 1), \
237                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
238            self.parameters.append([p_name, ctl, unit])
239                                 
240        self.param_sizer.Add(sizer, 0, wx.LEFT, 10)
241   
242    def _layout_hint(self):
243        """
244            Fill the sizer containing hint
245        """
246        hint_msg = "We support omf, sld or pdb data files only."
247        hint_msg +=  "         "
248        if FONT_VARIANT < 1:
249            hint_msg +=  "Very "
250        hint_msg += "SLOW drawing -->"
251        hint_txt = wx.StaticText(self, -1, hint_msg)
252       
253        id = wx.NewId()
254        self.draw_button = wx.Button(self, id, "Arrow Draw")
255        hint_on_draw = "Draw with arrows. Caution: it is a very slow drawing."
256        self.draw_button.SetToolTipString(hint_on_draw)
257        self.draw_button.Bind(wx.EVT_BUTTON, self.sld_draw, id=id)
258       
259        self.draw_button.Enable(False)
260        self.hint_sizer.AddMany([(hint_txt, 0, wx.LEFT, 15),
261                                 (self.draw_button, 0, wx.LEFT, 7)])
262   
263    def _layout_shape(self):
264        """
265        Fill the shape sizer
266        """
267        label_txt = wx.StaticText(self, -1, "Shape:")
268        self.shape_combo = self._fill_shape_combo()
269        self.shape_sizer.AddMany([(label_txt, 0, wx.LEFT, 15),
270                                (self.shape_combo, 0, wx.LEFT, 5)])
271   
272    def _fill_shape_combo(self):
273        """
274        Fill up the shape combo box
275        """
276        shape_combo = wx.ComboBox(self, -1, size=(150, -1), 
277                                      style=wx.CB_READONLY) 
278        shape_combo.Append('Rectangular')
279        shape_combo.Append('Ellipsoid')
280        shape_combo.Bind(wx.EVT_COMBOBOX, self._on_shape_select)
281        shape_combo.SetSelection(0)
282        return shape_combo
283   
284    def _on_shape_select(self, event):
285        """
286        On selecting a shape
287        """
288        event.Skip()
289        label = event.GetEventObject().GetValue().lower() 
290        self.default_shape = label
291        self.parent.set_omfpanel_default_shap(self.default_shape)
292        self.parent.set_omfpanel_npts()
293       
294    def _fill_orient_combo(self):
295        """
296        Fill up the orientation combo box: used only for atomic structure
297        """
298        orient_combo = wx.ComboBox(self, -1, size=(150, -1), 
299                                      style=wx.CB_READONLY) 
300        orient_combo.Append('Fixed orientation')
301        orient_combo.Append('No orientation ')
302       
303        orient_combo.Bind(wx.EVT_COMBOBOX, self._on_orient_select)
304        orient_combo.SetSelection(0)
305        return orient_combo
306   
307    def _on_orient_select(self, event):
308        """
309        On selecting a orientation
310        """
311        event.Skip()
312        cb = event.GetEventObject()
313        is_avg = cb.GetCurrentSelection() == 1
314        self.model.set_is_avg(is_avg)         
315           
316    def _layout_qrange(self):
317        """
318        Fill the sizer containing qrange
319        """
320        sizer = wx.GridBagSizer(2, 3)
321        ix = 0
322        iy = 0
323        #key_list.sort()
324        name = wx.StaticText(self, -1, 'No. of Qx (Qy) bins: ')
325        sizer.Add(name, (iy, ix), (1, 1), \
326                        wx.EXPAND | wx.ADJUST_MINSIZE, 0)
327        ## add parameter value
328        ix += 1
329        self.npt_ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH * 1.5, 20),
330                            style=wx.TE_PROCESS_ENTER)
331        self.npt_ctl.Bind(wx.EVT_TEXT, self._onparamEnter)
332        self.npt_ctl.SetValue(format_number(self.npts_x, True))
333        sizer.Add(self.npt_ctl, (iy, ix), (1, 1), wx.EXPAND)
334        ## add unit
335        ix += 1
336        unit = wx.StaticText(self, -1, '')
337        sizer.Add(unit, (iy, ix), (1, 1), \
338                        wx.EXPAND | wx.ADJUST_MINSIZE, 0)
339        iy += 1
340        ix = 0
341        name = wx.StaticText(self, -1, 'Qx (Qy) Max: ')
342        sizer.Add(name, (iy, ix), (1, 1), \
343                        wx.EXPAND | wx.ADJUST_MINSIZE, 0)
344        ## add parameter value
345        ix += 1
346        self.qmax_ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH * 1.5, 20),
347                            style=wx.TE_PROCESS_ENTER)
348        self.qmax_ctl.Bind(wx.EVT_TEXT, self._onparamEnter )
349        self.qmax_ctl.SetValue(format_number(self.qmax_x, True))
350        sizer.Add(self.qmax_ctl, (iy, ix), (1, 1), wx.EXPAND)
351        ## add unit
352        ix += 1
353        unit = wx.StaticText(self, -1, '[1/A]')
354        sizer.Add(unit, (iy, ix), (1, 1), \
355                        wx.EXPAND | wx.ADJUST_MINSIZE, 0)
356        self.qrange_sizer.Add(sizer, 0, wx.LEFT, 10)
357   
358    def _layout_button(self): 
359        """
360            Do the layout for the button widgets
361        """ 
362        self.est_time = '*Estimated Computation time : %s sec'
363        self.time_text = wx.StaticText(self, -1, self.est_time% str(2) )
364        self.orient_combo = self._fill_orient_combo()
365        self.orient_combo.Show(False)
366        self.bt_compute = wx.Button(self, wx.NewId(),'Compute')
367        self.bt_compute.Bind(wx.EVT_BUTTON, self.on_compute)
368        self.bt_compute.SetToolTipString("Compute 2D Scattering Pattern.")
369        self.button_sizer.AddMany([(self.time_text , 0, wx.LEFT, 20),
370                                   (self.orient_combo , 0, wx.LEFT, 20),
371                                   (self.bt_compute, 0, wx.LEFT, 20)])
372       
373    def estimate_ctime(self):
374        """
375        Calculation time estimation
376        """
377        n_qbins = float(self.npt_ctl.GetValue())
378        n_qbins *= n_qbins
379        n_pixs = float(self.parent.get_npix())
380        x_in = n_qbins * n_pixs / 100000
381        # magic equation: not very accurate
382        etime = 1.0 + 0.085973 * x_in
383        return int(etime)
384       
385    def set_est_time(self):
386        """
387        Set text for est. computation time
388        """
389        if self.time_text != None:
390            etime = self.estimate_ctime()
391            self.time_text.SetLabel(self.est_time% str(etime))
392       
393    def _do_layout(self):
394        """
395        Draw window content
396        """
397        self._define_structure()
398        self._layout_data_name()
399        self._layout_param_size()
400        self._layout_qrange()
401        self._layout_hint()
402        self._layout_shape()
403        self._layout_button()
404        self.boxsizer_source.AddMany([(self.data_name_sizer, 0,
405                                        wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
406                                      (self.hint_sizer, 0,
407                                        wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
408                                      (self.shape_sizer, 0, 
409                                        wx.EXPAND|wx.TOP|wx.BOTTOM, 5)])
410        self.boxsizer_parameters.AddMany([(self.param_sizer, 0,
411                                     wx.EXPAND|wx.TOP|wx.BOTTOM, 5),])
412        self.boxsizer_qrange.AddMany([(self.qrange_sizer, 0,
413                                     wx.EXPAND|wx.TOP|wx.BOTTOM, 5),])
414        self.main_sizer.AddMany([(self.boxsizer_source, 0, 
415                                  wx.EXPAND|wx.ALL, 10),
416                                 (self.boxsizer_parameters, 0, 
417                                  wx.EXPAND|wx.ALL, 10),
418                                 (self.boxsizer_qrange, 0, 
419                                  wx.EXPAND|wx.ALL, 10),
420                                 (self.button_sizer, 0,
421                                  wx.EXPAND|wx.TOP|wx.BOTTOM, 5)])
422        self.SetSizer(self.main_sizer)
423        self.SetAutoLayout(True)
424   
425    def _create_default_sld_data(self):
426        """
427        Making default sld-data
428        """
429        sld_n_default = 6.97e-06
430        omfdata = sans_gen.OMFData()
431        omf2sld = sans_gen.OMF2SLD()
432        omf2sld.set_data(omfdata, self.default_shape)
433        self.sld_data = omf2sld.output
434        self.sld_data.is_data = False
435        self.sld_data.filename = "Default SLD Profile"
436        self.sld_data.set_sldn(sld_n_default)
437        self.data_name_tcl.SetValue(self.sld_data.filename)       
438               
439    def choose_data_file(self, location=None):
440        """
441        Choosing a dtata file
442        """
443        path = None
444        filename = ''
445        if location == None:
446            location = os.getcwd()
447       
448        omf_type = self.omfreader.type
449        sld_type = self.sldreader.type
450        pdb_type = self.pdbreader.type
451        wildcard = []
452        for type in sld_type:
453            wildcard.append(type)
454        for type in omf_type:
455            wildcard.append(type)
456        for type in pdb_type:
457            wildcard.append(type)
458        wildcard = '|'.join(wildcard)
459        dlg = wx.FileDialog(self, "Choose a file", location,
460                            "", wildcard, wx.OPEN)
461        if dlg.ShowModal() == wx.ID_OK:
462            path = dlg.GetPath()
463            filename = os.path.basename(path)
464        dlg.Destroy() 
465        return path
466       
467    def on_load_data(self, event):
468        """
469        Open a file dialog to allow the user to select a given file.
470        The user is only allow to load file with extension .omf, .txt, .sld.
471        Display the slit size corresponding to the loaded data.
472        """
473        location = self.parent.get_path()
474        path = self.choose_data_file(location=location)
475        if path is None:
476            return 
477       
478        self.shape_sizer.ShowItems(False)
479        self.default_shape = 'rectangular'
480        self.parent.set_omfpanel_default_shap(self.default_shape)
481       
482        self.parent.set_file_location(os.path.dirname(path))
483        try:
484            #Load data
485            self.ext = os.path.splitext(path)[-1]
486            if self.ext in self.omfreader.ext:
487                loader = self.omfreader
488            elif self.ext in self.sldreader.ext:
489                loader = self.sldreader
490            elif self.ext in self.pdbreader.ext:
491                loader = self.pdbreader
492            else:
493                loader = None
494            if self.reader is not None and self.reader.isrunning():
495                self.reader.stop()
496            self.browse_button.Enable(False)
497            self.browse_button.SetLabel("Loading...")
498            if self.parent.parent is not None:
499                wx.PostEvent(self.parent.parent, 
500                                StatusEvent(status="Loading...",
501                                type="progress"))
502            self.reader = GenReader(path=path, loader=loader,
503                                    completefn=self.complete_loading,
504                                    updatefn=self.load_update)
505            self.reader.queue()
506            #self.load_update()
507        except:
508            self.ext = None
509            if self.parent.parent is None:
510                return 
511            msg = "Generic SANS Calculator: %s" % (sys.exc_value)
512            wx.PostEvent(self.parent.parent,
513                          StatusEvent(status=msg, type='stop'))
514            self.SetFocus()
515            return 
516       
517    def load_update(self):
518        """
519        print update on the status bar
520        """       
521        if self.parent.parent is None:
522                return 
523        if self.reader.isrunning():
524            type = "progress"
525        else:
526            type = "stop"
527        wx.PostEvent(self.parent.parent, StatusEvent(status="",
528                                                  type=type))
529           
530    def complete_loading(self, data=None, filename=''):
531        """
532        Complete the loading
533        """
534        #compute the slit size
535        self.browse_button.Enable(True)
536        self.browse_button.SetLabel('Load')
537        try:
538            is_pdbdata = False
539            filename = data.filename
540            self.data_name_tcl.SetValue(str(filename))
541            self.file_name = filename.split('.')[0]
542            self.orient_combo.SetSelection(0)
543            if self.ext in self.omfreader.ext:
544                gen = sans_gen.OMF2SLD()
545                gen.set_data(data)
546                #omf_data = data
547                self.sld_data = gen.get_magsld()
548            elif self.ext in self.sldreader.ext:
549                self.sld_data = data
550            elif self.ext in self.pdbreader.ext:
551                self.sld_data = data
552                is_pdbdata = True
553                #omf_data = None
554            else:
555                raise
556            self.orient_combo.Show(is_pdbdata)
557            self.button_sizer.Layout()
558            self._set_sld_data_helper(True)
559        except:
560            if self.parent.parent is None:
561                raise
562            msg = "Loading Error: This file format is not supported "
563            msg += "for GenSANS." 
564            wx.PostEvent(self.parent.parent,
565                          StatusEvent(status=msg, type='stop', info='Error'))
566            self.SetFocus()
567            return 
568        if self.parent.parent is None:
569            return 
570       
571        msg = "Load Complete"
572        wx.PostEvent(self.parent.parent, StatusEvent(status=msg, type='stop'))
573        self.SetFocus()
574       
575    def _set_sld_data_helper(self, is_draw=False):
576        """
577        Set sld data helper
578        """
579        is_avg = self.orient_combo.GetCurrentSelection() == 1
580        self.model.set_is_avg(is_avg)
581        self.model.set_sld_data(self.sld_data)
582       
583        self.draw_button.Enable(self.sld_data!=None)
584        wx.CallAfter(self.parent.set_sld_data, self.sld_data)
585        self._update_model_params()
586        if is_draw:
587            wx.CallAfter(self.sld_draw, None, False)
588   
589    def _update_model_params(self):
590        """
591        Update the model parameter values
592        """
593        for list in self.parameters:
594            param_name = list[0].GetLabelText()
595            val = str(self.model.params[param_name])
596            list[1].SetValue(val)
597   
598    def set_volume_ctl_val(self, val):
599        """
600        Set volume txtctrl value
601        """
602        for list in self.parameters:
603            param_name = list[0].GetLabelText()
604            if param_name.lower() == 'total_volume':
605                list[1].SetValue(val)
606                list[1].Refresh()
607                break
608                           
609    def _onparamEnter(self, event):
610        """
611        On param enter
612        """
613        try:
614            item = event.GetEventObject()
615            self._check_value()
616            item.Refresh()
617        except:
618            pass
619   
620    def sld_draw(self, event=None, has_arrow=True):
621        """
622        Draw 3D sld profile
623        """
624        flag = self.parent.check_omfpanel_inputs()
625        if not flag:
626            infor = 'Error'
627            msg = 'Error: Wrong inputs in the SLD info panel.'
628            # inform msg to wx
629            wx.PostEvent(self.parent.parent,
630                    StatusEvent(status=msg, info=infor))
631            self.SetFocus()
632            return
633
634        self.sld_data = self.parent.get_sld_from_omf()
635        output = self.sld_data 
636        #frame_size = wx.Size(470, 470)   
637        self.plot_frame = PlotFrame(self, -1, 'testView')
638        frame = self.plot_frame
639        frame.Show(False)
640        add_icon(self.parent, frame)
641        panel = frame.plotpanel   
642        try:
643            # mpl >= 1.0.0
644            ax = panel.figure.gca(projection='3d')
645        except:
646            # mpl < 1.0.0
647            try:
648                from mpl_toolkits.mplot3d import Axes3D
649                ax = Axes3D(panel.figure)
650            except:
651                logging.error("PlotPanel could not import Axes3D")
652                raise
653        panel.dimension = 3   
654        graph_title = self._sld_plot_helper(ax, output, has_arrow)
655        # Use y, z axes (in mpl 3d) as z, y axes
656        # that consistent with our SANS detector coords.
657        ax.set_xlabel('x ($\A%s$)'% output.pos_unit)
658        ax.set_ylabel('z ($\A%s$)'% output.pos_unit)
659        ax.set_zlabel('y ($\A%s$)'% output.pos_unit)
660        panel.subplot.figure.subplots_adjust(left=0.05, right=0.95, 
661                                             bottom=0.05, top=0.96)
662        if output.pix_type == 'atom':
663            ax.legend(loc='upper left', prop={'size':10})
664        num_graph = str(self.graph_num)
665        frame.SetTitle('Graph %s: %s'% (num_graph, graph_title))       
666        wx.CallAfter(frame.Show, True)
667        self.graph_num += 1
668
669    def _sld_plot_helper(self, ax, output, has_arrow=False):
670        """
671        Actual plot definition happens here
672        :Param ax: axis3d
673        :Param output: sld_data [MagSLD]
674        :Param has_arrow: whether or not draws M vector [bool]
675        """
676        # Set the locals
677        color_dic = {'H':'blue', 'D':'purple', 'N': 'orange', 
678                     'O':'red', 'C':'green', 'P':'cyan', 'Other':'k'}
679        marker = ','
680        m_size = 2
681        graph_title = self.file_name
682        graph_title += "   3D SLD Profile "
683        pos_x = output.pos_x
684        pos_y = output.pos_y
685        pos_z = output.pos_z
686        sld_mx = output.sld_mx
687        sld_my = output.sld_my
688        sld_mz = output.sld_mz
689        pix_symbol = output.pix_symbol
690        if output.pix_type == 'atom':
691            marker = 'o'
692            m_size = 3.5
693        sld_tot = (numpy.fabs(sld_mx) + numpy.fabs(sld_my) + \
694                   numpy.fabs(sld_mz) + numpy.fabs(output.sld_n))
695        is_nonzero = sld_tot > 0.0 
696        is_zero = sld_tot == 0.0   
697        # I. Plot null points
698        if is_zero.any():
699            ax.plot(pos_x[is_zero], pos_z[is_zero], pos_y[is_zero], marker, 
700                    c="y", alpha=0.5, markeredgecolor='y', markersize=m_size) 
701            pos_x = pos_x[is_nonzero]
702            pos_y = pos_y[is_nonzero]
703            pos_z = pos_z[is_nonzero]
704            sld_mx = sld_mx[is_nonzero]
705            sld_my = sld_my[is_nonzero]
706            sld_mz = sld_mz[is_nonzero]
707            pix_symbol = output.pix_symbol[is_nonzero]
708        # II. Plot selective points in color
709        other_color = numpy.ones(len(pix_symbol), dtype='bool')
710        for key in color_dic.keys():
711            chosen_color = pix_symbol == key
712            if numpy.any(chosen_color):
713                other_color = other_color  & (chosen_color != True)
714                color = color_dic[key]
715                ax.plot(pos_x[chosen_color], pos_z[chosen_color], 
716                        pos_y[chosen_color], marker, c=color, alpha=0.5, 
717                        markeredgecolor=color, markersize=m_size, label=key) 
718        # III. Plot All others       
719        if numpy.any(other_color):
720            a_name = ''
721            if output.pix_type == 'atom':
722                # Get atom names not in the list
723                a_names = [symb  for symb in pix_symbol \
724                           if symb not in color_dic.keys()]
725                a_name = a_names[0]
726                for name in a_names:
727                    new_name = ", " + name
728                    if a_name.count(name) == 0:
729                        a_name += new_name
730            # plot in black
731            ax.plot(pos_x[other_color], pos_z[other_color], pos_y[other_color], 
732                    marker, c="k", alpha=0.5, markeredgecolor="k", 
733                    markersize=m_size, label=a_name) 
734        # IV. Draws atomic bond with grey lines if any
735        if output.has_conect:
736            for ind in range(len(output.line_x)):
737                ax.plot(output.line_x[ind], output.line_z[ind], 
738                        output.line_y[ind], '-', lw=0.6, c="grey", alpha=0.3) 
739        # V. Draws magnetic vectors
740        if has_arrow and len(pos_x) > 0:     
741            graph_title += " - Magnetic Vector as Arrow -" 
742            panel = self.plot_frame.plotpanel
743            def _draw_arrow(input=None, elapsed=0.1):
744                """
745                draw magnetic vectors w/arrow
746                """
747                max_mx = max(numpy.fabs(sld_mx))
748                max_my = max(numpy.fabs(sld_my))
749                max_mz = max(numpy.fabs(sld_mz))
750                max_m = max(max_mx, max_my, max_mz)
751                try:
752                    max_step = max(output.xstepsize, output.ystepsize, 
753                                   output.zstepsize)
754                except:
755                    max_step = 0
756                if max_step <= 0:
757                    max_step = 5
758                try:
759                    if max_m != 0:
760                        unit_x2 = sld_mx / max_m
761                        unit_y2 = sld_my / max_m
762                        unit_z2 = sld_mz / max_m
763                        # 0.8 is for avoiding the color becomes white=(1,1,1))
764                        color_x = numpy.fabs(unit_x2 * 0.8)
765                        color_y = numpy.fabs(unit_y2 * 0.8)
766                        color_z = numpy.fabs(unit_z2 * 0.8)
767                        x2 = pos_x + unit_x2 * max_step
768                        y2 = pos_y + unit_y2 * max_step
769                        z2 = pos_z + unit_z2 * max_step
770                        x_arrow = numpy.column_stack((pos_x, x2))
771                        y_arrow = numpy.column_stack((pos_y, y2))
772                        z_arrow = numpy.column_stack((pos_z, z2))
773                        colors = numpy.column_stack((color_x, color_y, color_z))
774                        arrows = Arrow3D(panel, x_arrow, z_arrow, y_arrow, 
775                                        colors, mutation_scale=10, lw=1, 
776                                        arrowstyle="->", alpha = 0.5)
777                        ax.add_artist(arrows) 
778                except:
779                    pass 
780                msg = "Arrow Drawing completed.\n"
781                status_type = 'stop'
782                self._status_info(msg, status_type) 
783            msg = "Arrow Drawing is in progress..."
784            status_type = 'progress'
785            self._status_info(msg, status_type) 
786            draw_out = CalcGen(input=ax,
787                             completefn=_draw_arrow, updatefn=self._update)
788            draw_out.queue()
789        return graph_title
790 
791    def set_input_params(self):
792        """
793        Set model parameters
794        """
795        for list in self.parameters:
796            param_name = list[0].GetLabelText()
797            param_value = float(list[1].GetValue())
798            self.model.setParam(param_name, param_value)
799           
800    def on_compute(self, event):
801        """
802        Compute I(qx, qy)
803        """
804        flag = self.parent.check_omfpanel_inputs()
805        if not flag and self.parent.parent != None:
806            infor = 'Error'
807            msg = 'Error: Wrong inputs in the SLD info panel.'
808            # inform msg to wx
809            wx.PostEvent(self.parent.parent,
810                    StatusEvent(status=msg, info=infor))
811            self.SetFocus()
812            return
813        self.sld_data = self.parent.get_sld_from_omf()
814        if self.sld_data == None:
815            if self.parent.parent != None:
816                infor = 'Error'
817                msg = 'Error: No data has been selected.'
818                # inform msg to wx
819                wx.PostEvent(self.parent.parent,
820                        StatusEvent(status=msg, info=infor))
821                self.SetFocus()
822            return
823        flag = self._check_value()
824        if not flag:
825            _set_error(self, None, True)
826            return
827        try:
828            self.model.set_sld_data(self.sld_data)
829            self.set_input_params()
830            self._create_default_2d_data()
831            i_out = numpy.zeros(len(self.data.data))
832            msg = "Computation is in progress..."
833            status_type = 'progress'
834            self._status_info(msg, status_type)
835           
836            cal_out = CalcGen(input=[self.data.qx_data, 
837                                     self.data.qy_data, i_out], 
838                              completefn=self.complete, 
839                              updatefn=self._update)
840            cal_out.queue()
841           
842        except:
843            msg = "%s."% sys.exc_value
844            status_type = 'stop'
845            self._status_info(msg, status_type)
846            wx.PostEvent(self.parent.parent,
847                        StatusEvent(status=msg, info='Error'))
848            self.SetFocus()
849
850    def _check_value(self):
851        """
852        Check input values if float
853        """
854        flag = True
855        self.npt_ctl.SetBackgroundColour("white")
856        self.qmax_ctl.SetBackgroundColour("white")
857        try:
858            npt_val = float(self.npt_ctl.GetValue()) 
859            if npt_val < 2 or npt_val > 1000:
860                raise
861            self.npt_ctl.SetValue(str(int(npt_val)))
862            self.set_est_time()
863        except:
864            flag =  _set_error(self, self.npt_ctl)
865        try:
866            qmax_val = float(self.qmax_ctl.GetValue()) 
867            if qmax_val <= 0 or qmax_val > 1000:
868                raise
869        except:
870            flag = _set_error(self, self.qmax_ctl)       
871        for list in self.parameters:
872            list[1].SetBackgroundColour("white")
873            param_name = list[0].GetLabelText()
874            try: 
875                param_val = float(list[1].GetValue())
876                if param_name.count('frac') > 0:
877                    if param_val < 0 or param_val > 1:
878                       raise
879            except:
880                flag = _set_error(self, list[1])
881        return flag
882                   
883    def _status_info(self, msg = '', type = "update"):
884        """
885        Status msg
886        """
887        if type == "stop":
888            label = "Compute"
889            able = True
890        else:   
891            label = "Wait..."
892            able = False
893        self.bt_compute.Enable(able)
894        self.bt_compute.SetLabel(label)
895        self.bt_compute.SetToolTipString(label)
896        if self.parent.parent != None:
897            wx.PostEvent(self.parent.parent, 
898                             StatusEvent(status = msg, type = type )) 
899
900    def _update(self, time=None):
901        """
902        Update the output of plotting model
903        """
904        if self.parent.parent != None:
905            msg = "Computation/drawing is in progress..."
906            wx.PostEvent(self.parent.parent, 
907                         StatusEvent(status=msg, type="update"))
908                               
909    def complete(self, input, elapsed=None):   
910        """
911        Gen compute complete function
912        :Param input: input list [qx_data, qy_data, i_out]
913        """
914        out = self.model.runXY(input)
915        self._draw2D(out)
916        msg = "Gen computation completed.\n"
917        status_type = 'stop'
918        self._status_info(msg, status_type)
919       
920    def _create_default_2d_data(self):
921        """
922        Create 2D data by default
923        Only when the page is on theory mode.
924        :warning: This data is never plotted.
925        """
926        self.qmax_x = float(self.qmax_ctl.GetValue())
927        self.npts_x = int(float(self.npt_ctl.GetValue()))
928        self.data = Data2D()
929        qmax = self.qmax_x #/ numpy.sqrt(2)
930        self.data.xaxis('\\rm{Q_{x}}', '\AA^{-1}')
931        self.data.yaxis('\\rm{Q_{y}}', '\AA^{-1}')
932        self.data.is_data = False
933        self.data.id = str(self.uid) + " GenData"
934        self.data.group_id = str(self.uid) + " Model2D"
935        ## Default values
936        self.data.detector.append(Detector())
937        index = len(self.data.detector) - 1
938        self.data.detector[index].distance = 8000   # mm
939        self.data.source.wavelength = 6             # A
940        self.data.detector[index].pixel_size.x = 5  # mm
941        self.data.detector[index].pixel_size.y = 5  # mm
942        self.data.detector[index].beam_center.x = qmax
943        self.data.detector[index].beam_center.y = qmax
944        xmax = qmax
945        xmin = -qmax
946        ymax = qmax
947        ymin = -qmax
948        qstep = self.npts_x
949
950        x = numpy.linspace(start=xmin, stop=xmax, num=qstep, endpoint=True)
951        y = numpy.linspace(start=ymin, stop=ymax, num=qstep, endpoint=True)
952        ## use data info instead
953        new_x = numpy.tile(x, (len(y), 1))
954        new_y = numpy.tile(y, (len(x), 1))
955        new_y = new_y.swapaxes(0, 1)
956        # all data reuire now in 1d array
957        qx_data = new_x.flatten()
958        qy_data = new_y.flatten()
959        q_data = numpy.sqrt(qx_data * qx_data + qy_data * qy_data)
960        # set all True (standing for unmasked) as default
961        mask = numpy.ones(len(qx_data), dtype=bool)
962        # store x and y bin centers in q space
963        x_bins = x
964        y_bins = y
965        self.data.source = Source()
966        self.data.data = numpy.ones(len(mask))
967        self.data.err_data = numpy.ones(len(mask))
968        self.data.qx_data = qx_data
969        self.data.qy_data = qy_data
970        self.data.q_data = q_data
971        self.data.mask = mask
972        self.data.x_bins = x_bins
973        self.data.y_bins = y_bins
974        # max and min taking account of the bin sizes
975        self.data.xmin = xmin
976        self.data.xmax = xmax
977        self.data.ymin = ymin
978        self.data.ymax = ymax
979       
980    def _draw2D(self, image):
981        """
982        Complete get the result of modelthread and create model 2D
983        that can be plot.
984        """
985        page_id = self.id
986        data = self.data
987       
988        model = self.model
989        qmin = 0.0
990        state = None
991       
992        numpy.nan_to_num(image)
993        new_plot = Data2D(image=image, err_image=data.err_data)
994        new_plot.name = model.name + '2d'
995        new_plot.title = "Generic model 2D "
996        new_plot.id = str(page_id) + ': ' + self.file_name \
997                        + ' #%s'% str(self.graph_num)
998        new_plot.group_id = str(page_id) + " Model2D"
999        new_plot.detector = data.detector
1000        new_plot.source = data.source
1001        new_plot.is_data = False
1002        new_plot.qx_data = data.qx_data
1003        new_plot.qy_data = data.qy_data
1004        new_plot.q_data = data.q_data
1005        new_plot.mask = data.mask
1006        ## plot boundaries
1007        new_plot.ymin = data.ymin
1008        new_plot.ymax = data.ymax
1009        new_plot.xmin = data.xmin
1010        new_plot.xmax = data.xmax
1011        title = data.title
1012        _yaxis, _yunit = data.get_yaxis()
1013        _xaxis, _xunit = data.get_xaxis()
1014        new_plot.xaxis(str(_xaxis), str(_xunit))
1015        new_plot.yaxis(str(_yaxis), str(_yunit))
1016       
1017        new_plot.is_data = False
1018        if data.is_data:
1019            data_name = str(data.name)
1020        else:
1021            data_name = str(model.__class__.__name__) + '2d'
1022
1023        if len(title) > 1:
1024            new_plot.title = "Gen Theory for %s "% model.name + data_name
1025        new_plot.name = new_plot.id
1026        new_plot.label = new_plot.id
1027        #theory_data = deepcopy(new_plot)
1028        if self.parent.parent != None:
1029            self.parent.parent.update_theory(data_id=data.id,
1030                                           theory=new_plot,
1031                                           state=state)
1032        title = new_plot.title
1033        num_graph = str(self.graph_num)
1034        wx.CallAfter(self.parent.draw_graph, new_plot, 
1035                     title="Graph %s: "% num_graph + new_plot.id )
1036        self.graph_num += 1
1037       
1038    def set_scale2d(self, scale):
1039        """
1040        Set SLD plot scale
1041        """
1042        self.scale2d = None
1043       
1044    def on_panel_close(self, event):
1045        """
1046        On Close SLD panel
1047        """
1048        #Not implemented   
1049                 
1050class OmfPanel(ScrolledPanel, PanelBase):
1051    """
1052        Provides the sas gen calculator GUI.
1053    """
1054    ## Internal nickname for the window, used by the AUI manager
1055    window_name = "SLD Pixel Info"
1056    ## Name to appear on the window title bar
1057    window_caption = "SLD Pixel Info "
1058    ## Flag to tell the AUI manager to put this panel in the center pane
1059    CENTER_PANE = False
1060   
1061    def __init__(self, parent, *args, **kwds):
1062        ScrolledPanel.__init__(self, parent, style=wx.RAISED_BORDER, 
1063                               *args, **kwds)
1064        PanelBase.__init__(self)
1065        #Font size
1066        self.SetWindowVariant(variant=FONT_VARIANT)
1067        self.SetupScrolling() 
1068        # Object that receive status event
1069        self.parent = parent
1070        self.sld_data = sans_gen.MagSLD([0], [0], [0]) 
1071        self.sld_ctl = None
1072        self.default_shape = 'rectangular'
1073        self._do_layout()
1074       
1075    def set_slddata(self, slddata):
1076        """
1077        Set sld data related items
1078        """
1079        self.sld_data = slddata
1080        self._set_slddata_ctr_val(slddata)
1081        # Make sure that self._set_slddata_ctr_val() is finished
1082        wx.CallAfter(self._set_omfdata_ctr, slddata)
1083   
1084    def get_sld_val(self):
1085        """
1086        Set sld_n of slddata on sld input
1087        """
1088        sld_sets = {}
1089        if not self.sld_data.is_data:
1090            self._get_other_val()
1091        for list in self.slds:
1092            if list[1].IsEnabled():
1093                list[1].SetBackgroundColour("white")
1094                list[1].Refresh()
1095                try:
1096                    val = float(list[1].GetValue())
1097                    sld_sets[list[0]] = val
1098                except:
1099                    flag = _set_error(self, list[1])
1100                    if not flag:
1101                        return self.sld_data
1102            else:
1103               sld_sets[list[0]] = None
1104        for key in sld_sets.keys():
1105            key_low = key.lower()
1106            if key_low.count('mx') > 0:
1107                if sld_sets[key] == None:
1108                    sld_sets[key] = self.sld_data.sld_mx
1109                mx = sld_sets[key]
1110            elif key_low.count('my') > 0:
1111                if sld_sets[key] == None:
1112                    sld_sets[key] = self.sld_data.sld_my
1113                my = sld_sets[key]
1114            elif key_low.count('mz') > 0:
1115                if sld_sets[key] == None:
1116                    sld_sets[key] = self.sld_data.sld_mz
1117                mz = sld_sets[key]
1118            else:
1119                if sld_sets[key] != None:
1120                    self.sld_data.set_sldn(sld_sets[key])
1121        self.sld_data.set_sldms(mx, my, mz)
1122        self._set_slddata_ctr_val(self.sld_data)
1123       
1124        return self.sld_data
1125   
1126    def get_pix_volumes(self):
1127        """
1128        Get the pixel volume
1129        """
1130        vol = self.sld_data.vol_pix
1131       
1132        return vol
1133               
1134    def _get_other_val(self): 
1135        """
1136        """
1137        omfdata = sans_gen.OMFData()
1138        sets = {}
1139        try:
1140            for lst in self.stepsize:
1141                if lst[1].IsEnabled():
1142                    val = float(lst[1].GetValue())
1143                    sets[lst[0]] = val
1144                else:
1145                    sets[lst[0]] = None
1146                    return
1147            for lst in self.nodes:
1148                if lst[1].IsEnabled():
1149                    val = float(lst[1].GetValue())
1150                    sets[lst[0]] = val
1151                else:
1152                    sets[lst[0]] = None
1153                    return
1154                   
1155            for key in sets.keys():
1156                exec "omfdata.%s = sets['%s']"% (key, key)           
1157
1158            omf2sld = sans_gen.OMF2SLD()
1159            omf2sld.set_data(omfdata, self.default_shape)
1160            self.sld_data = omf2sld.output
1161            self.sld_data.is_data = False
1162            self.sld_data.filename = "Default SLD Profile"
1163        except:
1164            msg = "OMF Panel: %s"% sys.exc_value
1165            infor = 'Error'
1166            #logging.error(msg)
1167            if self.parent.parent != None:
1168                # inform msg to wx
1169                wx.PostEvent(self.parent.parent,
1170                        StatusEvent(status=msg, info=infor))
1171                self.SetFocus()
1172               
1173    def _set_slddata_ctr_val(self, slddata):
1174        """
1175        Set slddata crl
1176        """
1177        try:
1178            val = str(len(slddata.sld_n))
1179        except:
1180            val = 'Unknown'
1181        self.npix_ctl.SetValue(val)
1182       
1183    def _set_omfdata_ctr(self, omfdata):   
1184        """
1185        Set the textctr box values
1186        """
1187       
1188        if omfdata == None:
1189            self._set_none_text()
1190            return
1191        nodes_list = self._get_nodes_key_list(omfdata)
1192        step_list = self._get_step_key_list(omfdata)
1193        for ctr_list in self.nodes:
1194            for key in nodes_list.keys():
1195                if ctr_list[0] == key:
1196                    ctr_list[1].SetValue(format_number(nodes_list[key], True))
1197                    ctr_list[1].Enable(not omfdata.is_data)
1198                    break
1199        for ctr_list in self.stepsize:
1200            for key in step_list.keys():
1201                if ctr_list[0] == key:
1202                    ctr_list[1].SetValue(format_number(step_list[key], True))
1203                    ctr_list[1].Enable(not omfdata.is_data)
1204                    break   
1205               
1206    def _set_none_text(self):
1207        """
1208        Set Unknown in textctrls
1209        """
1210        val = 'Unknown'
1211        for ctr_list in self.nodes:
1212            ctr_list[1].SetValue(val)
1213        for ctr_list in self.stepsize:
1214            ctr_list[1].SetValue(val)
1215               
1216    def _define_structure(self):
1217        """
1218        Define the main sizers building to build this application.
1219        """
1220        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
1221       
1222        self.npixels_sizer = wx.BoxSizer(wx.HORIZONTAL)
1223        self.box_sld = wx.StaticBox(self, -1, 
1224                                    str("Mean SLD"))
1225        self.box_node = wx.StaticBox(self, -1, str("Nodes"))
1226        self.boxsizer_sld = wx.StaticBoxSizer(self.box_sld, wx.VERTICAL)
1227        self.box_stepsize = wx.StaticBox(self, -1, str("Step Size"))
1228        self.boxsizer_node = wx.StaticBoxSizer(self.box_node, wx.VERTICAL)
1229        self.boxsizer_stepsize = wx.StaticBoxSizer(self.box_stepsize,
1230                                                    wx.VERTICAL)
1231        self.sld_sizer = wx.BoxSizer(wx.HORIZONTAL)
1232        self.node_sizer = wx.BoxSizer(wx.HORIZONTAL)
1233        self.step_sizer = wx.BoxSizer(wx.HORIZONTAL)
1234        self.hint_sizer = wx.BoxSizer(wx.HORIZONTAL)
1235        self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
1236               
1237    def _layout_npix(self):
1238        """
1239        Build No of pixels sizer
1240        """
1241        num_pix_text = wx.StaticText(self, -1, "No. of Pixels: ") 
1242        self.npix_ctl = OutputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
1243                                style=wx.TE_PROCESS_ENTER)
1244        self._set_slddata_ctr_val(self.sld_data)
1245        self._set_omfdata_ctr(self.sld_data) 
1246        self.npixels_sizer.AddMany([(num_pix_text, 0,
1247                                          wx.EXPAND|wx.LEFT|wx.TOP, 5),
1248                                     (self.npix_ctl, 0,
1249                                     wx.EXPAND|wx.TOP, 5)])
1250
1251    def _layout_slds(self):
1252        """
1253        Build nuclear sld sizer
1254        """
1255        self.slds = []
1256        omfdata = self.sld_data
1257        if omfdata == None:
1258            raise
1259        sld_key_list = self._get_slds_key_list(omfdata)
1260        # Dic is not sorted
1261        key_list = [key for key in sld_key_list.keys()]
1262        # Sort here
1263        key_list.sort()
1264        is_data = self.sld_data.is_data
1265        sizer = wx.GridBagSizer(2, 3)
1266        ix = 0
1267        iy = -1
1268        for key in key_list:
1269            value = sld_key_list[key]
1270            iy += 1
1271            ix = 0
1272            name = wx.StaticText(self, -1, key)
1273            sizer.Add(name, (iy, ix), (1, 1), \
1274                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
1275            ## add parameter value
1276            ix += 1
1277            ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
1278                                style=wx.TE_PROCESS_ENTER)
1279            ctl.SetValue(format_number(value, True))
1280            ctl.Enable(not is_data)
1281            sizer.Add(ctl, (iy, ix), (1, 1), wx.EXPAND)
1282            ## add unit
1283            ix += 1
1284            s_unit = '[' + omfdata.sld_unit + ']'
1285            unit = wx.StaticText(self, -1, s_unit)
1286            sizer.Add(unit, (iy, ix), (1, 1), \
1287                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
1288            self.slds.append([key, ctl, unit])
1289        self.sld_sizer.Add(sizer, 0, wx.LEFT, 10) 
1290               
1291    def _layout_nodes(self):
1292        """
1293        Fill the sizer containing data's name
1294        """
1295        self.nodes = []
1296        omfdata = self.sld_data
1297        if omfdata == None:
1298            raise
1299        key_list = self._get_nodes_key_list(omfdata)
1300        is_data = self.sld_data.is_data
1301        sizer = wx.GridBagSizer(2, 3)
1302        ix = 0
1303        iy = -1
1304        for key, value in key_list.iteritems():
1305            iy += 1
1306            ix = 0
1307            name = wx.StaticText(self, -1, key)
1308            sizer.Add(name, (iy, ix), (1, 1), \
1309                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
1310            ## add parameter value
1311            ix += 1
1312            ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
1313                                style=wx.TE_PROCESS_ENTER)
1314            ctl.Bind(wx.EVT_TEXT, self._onparamEnter )
1315            ctl.SetValue(format_number(value, True))
1316            ctl.Enable(not is_data)
1317            sizer.Add(ctl, (iy, ix), (1, 1), wx.EXPAND)
1318            ## add unit
1319            ix += 1
1320            unit = wx.StaticText(self, -1, '')
1321            sizer.Add(unit, (iy, ix), (1, 1), \
1322                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
1323            self.nodes.append([key, ctl, unit])
1324        self.node_sizer.Add(sizer, 0, wx.LEFT, 10)
1325           
1326    def _layout_stepsize(self):
1327        """
1328        Fill the sizer containing slit size information
1329        """
1330        self.stepsize = []
1331        omfdata = self.sld_data
1332        if omfdata == None:
1333            raise
1334        key_list = self._get_step_key_list(omfdata)
1335        is_data = self.sld_data.is_data
1336        sizer = wx.GridBagSizer(2, 3)
1337        ix = 0
1338        iy = -1
1339        #key_list.sort()
1340        for key, value in key_list.iteritems():
1341            iy += 1
1342            ix = 0
1343            name = wx.StaticText(self, -1, key)
1344            sizer.Add(name, (iy, ix), (1, 1), \
1345                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
1346            ## add parameter value
1347            ix += 1
1348            ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
1349                                style=wx.TE_PROCESS_ENTER)
1350            ctl.Bind(wx.EVT_TEXT, self._onstepsize )
1351            ctl.SetValue(format_number(value, True))
1352            ctl.Enable(not is_data)
1353            sizer.Add(ctl, (iy, ix), (1, 1), wx.EXPAND)
1354            ## add unit
1355            ix += 1
1356            p_unit = '[' + omfdata.pos_unit + ']'
1357            unit = wx.StaticText(self, -1, p_unit)
1358            sizer.Add(unit, (iy, ix), (1, 1), \
1359                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
1360            self.stepsize.append([key, ctl, unit])
1361        self.step_sizer.Add(sizer, 0, wx.LEFT, 10)
1362   
1363    def _layout_hint(self):
1364        """
1365        Fill the sizer containing hint
1366        """
1367        hint_msg = "Load an omf or 3d sld profile data file."
1368        self.hint_txt = wx.StaticText(self, -1, hint_msg)
1369        self.hint_sizer.AddMany([(self.hint_txt, 0, wx.LEFT, 15)])
1370   
1371    def _layout_button(self): 
1372        """
1373        Do the layout for the button widgets
1374        """ 
1375        self.bt_draw = wx.Button(self, wx.NewId(),'Draw Points')
1376        self.bt_draw.Bind(wx.EVT_BUTTON, self.on_sld_draw)
1377        self.bt_draw.SetToolTipString("Draw a scatter plot for sld profile.")
1378        self.bt_save = wx.Button(self, wx.NewId(),'Save SLD Data')
1379        self.bt_save.Bind(wx.EVT_BUTTON, self.on_save)
1380        self.bt_save.Enable(False)
1381        self.bt_save.SetToolTipString("Save SLD data.")
1382        self.button_sizer.AddMany([(self.bt_draw, 0, wx.LEFT, 10),
1383                                   (self.bt_save, 0, wx.LEFT, 10)])
1384       
1385    def _do_layout(self):
1386        """
1387        Draw window content
1388        """
1389        self._define_structure()
1390        self._layout_nodes()
1391        self._layout_stepsize()
1392        self._layout_npix()
1393        self._layout_slds()
1394        #self._layout_hint()
1395        self._layout_button()
1396        self.boxsizer_node.AddMany([(self.node_sizer, 0,
1397                                    wx.EXPAND|wx.TOP, 5),
1398                                     (self.hint_sizer, 0,
1399                                     wx.EXPAND|wx.TOP|wx.BOTTOM, 5)])
1400        self.boxsizer_stepsize.AddMany([(self.step_sizer, 0,
1401                                     wx.EXPAND|wx.TOP|wx.BOTTOM, 5),])
1402        self.boxsizer_sld.AddMany([(self.sld_sizer, 0,
1403                                     wx.EXPAND|wx.BOTTOM, 5),])
1404        self.main_sizer.AddMany([(self.npixels_sizer, 0, wx.EXPAND|wx.ALL, 10),
1405                        (self.boxsizer_sld, 0, wx.EXPAND|wx.ALL, 10),
1406                        (self.boxsizer_node, 0, wx.EXPAND|wx.ALL, 10),
1407                        (self.boxsizer_stepsize, 0, wx.EXPAND|wx.ALL, 10),
1408                        (self.button_sizer, 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5)])
1409        self.SetSizer(self.main_sizer)
1410        self.SetAutoLayout(True)
1411       
1412    def _get_nodes_key_list(self, data):
1413        """
1414        Return nodes key list
1415       
1416        :Param data: OMFData
1417        """
1418        key_list = {'xnodes' : data.xnodes, 
1419                    'ynodes' : data.ynodes,
1420                    'znodes' : data.znodes}
1421        return key_list
1422       
1423    def _get_slds_key_list(self, data):
1424        """
1425        Return nodes key list
1426       
1427        :Param data: OMFData
1428        """
1429        key_list = {'Nucl.' : data.sld_n, 
1430                    'Mx' : data.sld_mx,
1431                    'My' : data.sld_my,
1432                    'Mz' : data.sld_mz}
1433        return key_list
1434
1435    def _get_step_key_list(self, data):
1436        """
1437        Return step key list
1438       
1439        :Param data: OMFData
1440        """
1441        key_list = {'xstepsize' : data.xstepsize, 
1442                    'ystepsize' : data.ystepsize,
1443                    'zstepsize' : data.zstepsize}
1444        return key_list       
1445   
1446    def set_sld_ctr(self, sld_data):
1447        """
1448        Set sld textctrls
1449        """
1450        if sld_data == None:
1451            for ctr_list in self.slds:
1452                ctr_list[1].Enable(False)
1453                #break   
1454            return
1455       
1456        self.sld_data = sld_data
1457        sld_list = self._get_slds_key_list(sld_data)
1458        for ctr_list in self.slds:
1459            for key in sld_list.keys():
1460                if ctr_list[0] == key:
1461                    min_val = numpy.min(sld_list[key])
1462                    max_val = numpy.max(sld_list[key])
1463                    mean_val = numpy.mean(sld_list[key])
1464                    enable = (min_val == max_val) and \
1465                             sld_data.pix_type == 'pixel'
1466                    ctr_list[1].SetValue(format_number(mean_val, True))
1467                    ctr_list[1].Enable(enable)
1468                    #ctr_list[2].SetLabel("[" + sld_data.sld_unit + "]")
1469                    break   
1470
1471    def on_sld_draw(self, event):
1472        """
1473        Draw sld profile as scattered plot
1474        """
1475        self.parent.sld_draw()
1476         
1477    def on_save(self, event):
1478        """
1479        Close the window containing this panel
1480        """
1481        flag = True
1482        flag = self.check_inputs()
1483        if not flag:
1484            return
1485        self.sld_data = self.get_sld_val()
1486        self.parent.set_main_panel_sld_data(self.sld_data)
1487       
1488        reader = sans_gen.SLDReader() 
1489        extension = '*.sld'
1490        path = None
1491        data= None
1492        location = self.parent.get_path()
1493        dlg = wx.FileDialog(self, "Save sld file",
1494                            location, "sld_file",
1495                             extension, 
1496                             wx.SAVE)
1497        if dlg.ShowModal() == wx.ID_OK:
1498            path = dlg.GetPath()
1499            self.parent.set_file_location(os.path.dirname(path))
1500        else:
1501            return None
1502        dlg.Destroy()
1503        try:
1504            if path is None:
1505                return
1506           
1507            data = self.parent.get_sld_data()
1508            fName = os.path.splitext(path)[0] + '.' + extension.split('.')[-1]
1509            if data != None:
1510                try:
1511                    reader.write(fName, data)
1512                except:
1513                    raise
1514            else:
1515                msg = "%s cannot write %s\n" % ('Generic Scattering', str(path))
1516                infor = 'Error'
1517                #logging.error(msg)
1518                if self.parent.parent != None:
1519                    # inform msg to wx
1520                    wx.PostEvent(self.parent.parent,
1521                            StatusEvent(status=msg, info=infor))
1522                    self.SetFocus()
1523            return
1524        except:
1525            msg = "Error occurred while saving. "
1526            infor = 'Error'
1527            if self.parent.parent != None:
1528                # inform msg to wx
1529                wx.PostEvent(self.parent.parent,
1530                        StatusEvent(status=msg, info=infor)) 
1531                self.SetFocus()   
1532
1533    def _onparamEnter(self, event):
1534        """
1535        """
1536        flag = True
1537        if event != None:
1538            event.Skip()
1539            ctl = event.GetEventObject()
1540            ctl.SetBackgroundColour("white")
1541            #_set_error(self, ctl)
1542        try:
1543            float(ctl.GetValue())
1544        except:
1545            flag = _set_error(self, ctl)
1546        if flag:
1547            npts = 1
1548            for item in self.nodes:
1549                n_val = float(item[1].GetValue())
1550                if n_val <= 0:
1551                    item[1].SetBackgroundColour("pink")
1552                    npts = -1
1553                    break
1554                if numpy.isfinite(n_val):
1555                    npts *= int(n_val)
1556            if npts > 0:
1557                nop = self.set_npts_from_slddata()
1558                if nop == None:
1559                    nop = npts
1560                self.display_npts(nop)
1561               
1562        ctl.Refresh()
1563        return flag
1564   
1565    def _set_volume_ctr_val(self, npts):
1566        """
1567        Set total volume
1568        """
1569        total_volume = npts * self.sld_data.vol_pix[0]
1570        self.parent.set_volume_ctr_val(total_volume)
1571                   
1572    def _onstepsize(self, event):
1573        """
1574        On stepsize event
1575        """
1576        flag = True
1577        if event != None:
1578            event.Skip()
1579            ctl = event.GetEventObject()
1580            ctl.SetBackgroundColour("white")
1581
1582        if flag and not self.sld_data.is_data:#ctl.IsEnabled():
1583            s_size = 1.0
1584            try:
1585                for item in self.stepsize:
1586                    s_val = float(item[1].GetValue())
1587                    if s_val <= 0:
1588                        item[1].SetBackgroundColour("pink")
1589                        ctl.Refresh()
1590                        return
1591                    if numpy.isfinite(s_val):
1592                        s_size *= s_val
1593                self.sld_data.set_pixel_volumes(s_size)
1594                if ctl.IsEnabled():
1595                    total_volume = sum(self.sld_data.vol_pix)
1596                    self.parent.set_volume_ctr_val(total_volume)
1597            except:
1598                pass
1599        ctl.Refresh()
1600 
1601         
1602    def set_npts_from_slddata(self):
1603        """
1604        Set total n. of points form the sld data
1605        """
1606        try:
1607            sld_data = self.parent.get_sld_from_omf()
1608            #nop = (nop * numpy.pi) / 6
1609            nop = len(sld_data.sld_n)
1610        except:
1611            nop = None
1612        return nop
1613   
1614    def display_npts(self, nop):
1615        """
1616        Displays Npts ctrl
1617        """
1618        try:
1619            self.npix_ctl.SetValue(str(nop))
1620            self.npix_ctl.Refresh()
1621            self.parent.set_etime()
1622            wx.CallAfter(self._set_volume_ctr_val, nop)
1623        except:
1624            # On Init
1625            pass
1626   
1627    def check_inputs(self):
1628        """
1629        check if the inputs are valid
1630        """
1631        flag = self._check_input_helper(self.slds)
1632        if flag:
1633            flag = self._check_input_helper(self.nodes)
1634        if flag:
1635            flag = self._check_input_helper(self.stepsize)
1636        return flag
1637   
1638    def _check_input_helper(self, list):
1639        """
1640        Check list values
1641        """
1642        flag = True
1643        for item in list:
1644            item[1].SetBackgroundColour("white")
1645            item[1].Refresh()
1646            try:
1647                float(item[1].GetValue())
1648            except:
1649                flag = _set_error(self, item[1])
1650                break
1651        return flag
1652
1653class SasGenWindow(wx.Frame):
1654    """
1655    GEN SAS main window
1656    """
1657    def __init__(self, parent=None, title="Generic Scattering Calculator",
1658                size=(PANEL_WIDTH * 1.3, PANEL_HEIGHT * 1.65), *args, **kwds):
1659        """
1660        Init
1661        """
1662        kwds['size'] = size
1663        kwds['title'] = title
1664       
1665        wx.Frame.__init__(self, parent, *args, **kwds)
1666        self.parent = parent
1667        self.omfpanel = OmfPanel(parent=self)
1668        self.panel = SasGenPanel(parent=self)
1669        self.data = None
1670        self.omfdata = sans_gen.OMFData()
1671        self.sld_data = None
1672        self._default_save_location = os.getcwd() 
1673       
1674        self._mgr = aui.AuiManager(self)
1675        self._mgr.SetDockSizeConstraint(0.5, 0.5)
1676        self._plot_title = ''
1677        self.scale2d = 'log_{10}'
1678       
1679        self._build_toolbar()
1680        self._build_menubar()
1681       
1682        self.build_panels()
1683        self.Centre()
1684        self.Show(True)
1685   
1686    def _build_toolbar(self):
1687        """
1688        Build toolbar
1689        """
1690        tsize = (20, 20)
1691        tb = self.CreateToolBar(wx.TB_HORIZONTAL | wx.TB_FLAT)
1692        open_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_TOOLBAR, 
1693                                            tsize)
1694        save_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE_AS, wx.ART_TOOLBAR,
1695                                            tsize)
1696        close_bmp = wx.ArtProvider.GetBitmap(wx.ART_QUIT, wx.ART_TOOLBAR, 
1697                                            tsize)
1698        help_bmp = wx.ArtProvider.GetBitmap(wx.ART_HELP, wx.ART_TOOLBAR, 
1699                                           (17, 20))
1700       
1701        id = wx.NewId()
1702        tb.AddLabelTool(id, "Open", open_bmp, shortHelp="Open", 
1703                        longHelp="Open sld/omf file")
1704        self.Bind(wx.EVT_TOOL, self.on_open_file, id=id)
1705
1706        id = wx.NewId()
1707        tb.AddSimpleTool(id, save_bmp, "Save", "Save as sld file")
1708        self.Bind(wx.EVT_TOOL, self.on_save_file, id=id)
1709       
1710        tb.AddSeparator()
1711        id = wx.NewId()
1712        tb.AddSimpleTool(id, close_bmp, "Quit", "Quit")
1713        self.Bind(wx.EVT_TOOL, self.on_close, id=id)
1714       
1715        tb.AddSeparator()
1716        id = wx.NewId()
1717        tb.AddSimpleTool(id, help_bmp, "Help", "Help")
1718        self.Bind(wx.EVT_TOOL, self.on_help, id=id)
1719
1720        tb.Realize()
1721       
1722    def _build_menubar(self):
1723        """
1724        Build menubar
1725        """
1726        tsize = (13, 13)
1727        open_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_TOOLBAR, 
1728                                            tsize)
1729        save_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE_AS, wx.ART_TOOLBAR,
1730                                            tsize)
1731        quit_bmp = wx.ArtProvider.GetBitmap(wx.ART_QUIT, wx.ART_TOOLBAR, 
1732                                           tsize)
1733        help_bmp = wx.ArtProvider.GetBitmap(wx.ART_HELP, wx.ART_TOOLBAR, 
1734                                           (13, 15))
1735       
1736        menu_bar = wx.MenuBar()
1737       
1738        menu = wx.Menu()
1739        id = wx.NewId()
1740        item = wx.MenuItem(menu, id, "&Open sld/omf file")
1741        item.SetBitmap(open_bmp)
1742        menu.AppendItem(item)
1743        wx.EVT_MENU(self, id, self.on_open_file)
1744       
1745        id = wx.NewId()
1746        item = wx.MenuItem(menu, id, "&Save as sld file")
1747        item.SetBitmap(save_bmp)
1748        menu.AppendItem(item)
1749        wx.EVT_MENU(self, id, self.on_save_file)
1750       
1751        menu.AppendSeparator()
1752        id = wx.NewId()
1753        item = wx.MenuItem(menu, id, "&Quit")
1754        item.SetBitmap(quit_bmp)
1755        menu.AppendItem(item)
1756
1757        menu_bar.Append(menu, "&File")
1758        wx.EVT_MENU(self, id, self.on_close)
1759       
1760        menu_help = wx.Menu()
1761        id = wx.NewId()
1762        item = wx.MenuItem(menu_help, id, "&Theory and GUI")
1763        item.SetBitmap(help_bmp)
1764        menu_help.AppendItem(item)
1765        wx.EVT_MENU(self, id, self.on_help)
1766       
1767        menu_bar.Append(menu_help, "&Help")
1768       
1769        self.SetMenuBar(menu_bar)
1770       
1771    def build_panels(self):
1772        """
1773        """
1774       
1775        self.set_sld_data(self.sld_data)
1776       
1777        self._mgr.AddPane(self.panel, aui.AuiPaneInfo().
1778                              Name(self.panel.window_name).
1779                              CenterPane().
1780                              # This is where we set the size of
1781                              # the application window
1782                              BestSize(wx.Size(PANEL_WIDTH, 
1783                                               PANEL_HEIGHT)).
1784                              Show()) 
1785        self._mgr.AddPane(self.omfpanel, aui.AuiPaneInfo().
1786                              Name(self.omfpanel.window_name).
1787                              Caption(self.omfpanel.window_caption).
1788                              CloseButton(False).
1789                              Right().
1790                              Floatable(False).
1791                              BestSize(wx.Size(PANEL_WIDTH/2.5, PANEL_HEIGHT)).
1792                              Show()) 
1793        self._mgr.Update()
1794
1795    def get_sld_data(self):
1796        """
1797        Return slddata
1798        """
1799        return self.sld_data
1800   
1801    def get_sld_from_omf(self):
1802        """
1803        """
1804        self.sld_data = self.omfpanel.get_sld_val()
1805        return self.sld_data
1806   
1807    def set_sld_n(self, sld):
1808        """
1809        """
1810        self.panel.sld_data = sld
1811        self.panel.model.set_sld_data(sld)
1812       
1813    def set_sld_data(self, data):
1814        """
1815        Set omfdata
1816        """
1817        if data == None:
1818            return
1819        self.sld_data = data
1820        enable = (not data==None)
1821        self._set_omfpanel_sld_data(self.sld_data)
1822        self.omfpanel.bt_save.Enable(enable)
1823        self.set_etime()
1824   
1825    def set_omfpanel_npts(self):
1826        """
1827        Set Npts in omf panel
1828        """
1829        nop = self.omfpanel.set_npts_from_slddata()
1830        self.omfpanel.display_npts(nop)
1831       
1832    def _set_omfpanel_sld_data(self, data):
1833        """
1834        Set sld_data in omf panel
1835        """
1836        self.omfpanel.set_slddata(data) 
1837        self.omfpanel.set_sld_ctr(data)
1838   
1839    def check_omfpanel_inputs(self):
1840        """
1841        Check OMF panel inputs
1842        """
1843        return self.omfpanel.check_inputs() 
1844       
1845    def set_main_panel_sld_data(self, sld_data):
1846        """
1847        """
1848        self.sld_data = sld_data
1849       
1850    def set_file_location(self, path):
1851        """
1852        File location
1853        """
1854        self._default_save_location = path
1855   
1856    def get_path(self):
1857        """
1858        File location
1859        """
1860        return self._default_save_location
1861   
1862    def draw_graph(self, plot, title=''):
1863        """
1864        """
1865        frame = PlotFrame(self, -1, 'testView', self.scale2d)
1866        add_icon(self.parent, frame)
1867        frame.add_plot(plot)
1868        frame.SetTitle(title)
1869        frame.Show(True)
1870        frame.SetFocus()
1871   
1872    def get_npix(self):
1873        """
1874        Get no. of pixels from omf panel
1875        """
1876        n_pix = self.omfpanel.npix_ctl.GetValue()
1877        return n_pix
1878   
1879    def get_pix_volumes(self):
1880        """
1881        Get a pixel volume
1882        """
1883        vol = self.omfpanel.get_pix_volumes()
1884        return vol
1885   
1886    def set_volume_ctr_val(self, val):
1887        """
1888        Set volume txtctl value
1889        """
1890        try:
1891            self.panel.set_volume_ctl_val(str(val))
1892        except:
1893            print "self.panel is not initialized yet"
1894           
1895    def set_omfpanel_default_shap(self, shape):
1896        """
1897        Set default_shape in omfpanel
1898        """
1899        self.omfpanel.default_shape = shape
1900   
1901    def set_etime(self):
1902        """
1903        Sets est. computation time on panel
1904        """
1905        self.panel.set_est_time()
1906   
1907    def get_sld_data_from_omf(self):
1908        """
1909        """
1910        data = self.omfpanel.get_sld_val() 
1911        return data
1912       
1913    def set_scale2d(self, scale):
1914        """
1915        """
1916        self.scale2d = scale
1917           
1918    def on_panel_close(self, event):
1919        """
1920        """
1921        #Not implemented
1922         
1923    def on_open_file(self, event):
1924        """
1925        On Open
1926        """
1927        self.panel.on_load_data(event)
1928   
1929    def sld_draw(self):
1930        """
1931        sld draw
1932        """
1933        self.panel.sld_draw(event=None, has_arrow=False)
1934       
1935    def on_save_file(self, event):
1936        """
1937        On Close
1938        """
1939        self.omfpanel.on_save(event)
1940       
1941    def on_close(self, event):
1942        """
1943        Close
1944        """
1945        self.Destroy()
1946       
1947    def on_help(self, event):   
1948        """
1949        Gen scatter angle help panel
1950        """
1951        from sans.perspectives.calculator.help_panel import  HelpWindow
1952        # Get models help model_function path
1953        import sans.perspectives.calculator as calmedia
1954
1955        media = calmedia.get_data_path(media='media')
1956        path = os.path.join(media,"gen_sans_help.html") 
1957        name = "Polarization Angle"
1958        frame = HelpWindow(self, -1, title=' Help: Polarization Angle', 
1959                           pageToOpen=path, size=(865, 450))   
1960        try: 
1961            frame.splitter.DetachWindow(frame.lpanel)
1962            # Display only the right side one
1963            frame.lpanel.Hide() 
1964            frame.Show(True)
1965        except:
1966            frame.Destroy() 
1967            msg = 'Display Error\n'
1968            info = "Info"
1969            wx.MessageBox(msg, info)
1970           
1971if __name__ == "__main__": 
1972    app = wx.PySimpleApp()
1973    SGframe = SasGenWindow()   
1974    SGframe.Show(True)
1975    app.MainLoop()     
Note: See TracBrowser for help on using the repository browser.