source: sasview/calculatorview/src/sans/perspectives/calculator/gen_scatter_panel.py @ 1c6c1b7

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

Grid on/off for the simple plot except for 3d

  • Property mode set to 100644
File size: 68.3 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 the omf or sld 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, 15)])
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        self.draw_button.Enable(self.sld_data!=None)
583        wx.CallAfter(self.parent.set_sld_data, self.sld_data)
584        if is_draw:
585            wx.CallAfter(self.sld_draw, False)
586           
587    def _onparamEnter(self, event):
588        """
589        On param enter
590        """
591        try:
592            item = event.GetEventObject()
593            self._check_value()
594            item.Refresh()
595        except:
596            pass
597   
598    def sld_draw(self, has_arrow=False, event=None):
599        """
600        Draw 3D sld profile
601        """
602        color_dic = {'H':'blue', 'D':'purple', 'N': 'orange', 
603                     'O':'red', 'C':'green', 'P':'cyan', 'Other':'k'}
604        graph_title = self.file_name
605        graph_title += "   3D SLD Profile "
606       
607        flag = self.parent.check_omfpanel_inputs()
608        if not flag:
609            infor = 'Error'
610            msg = 'Error: Wrong inputs in the SLD info panel.'
611            # inform msg to wx
612            wx.PostEvent(self.parent.parent,
613                    StatusEvent(status=msg, info=infor))
614            self.SetFocus()
615            return
616
617        self.sld_data = self.parent.get_sld_from_omf()
618        output = self.sld_data 
619        #frame_size = wx.Size(470, 470)   
620        self.plot_frame = PlotFrame(self, -1, 'testView')
621        frame = self.plot_frame
622        frame.Show(False)
623        add_icon(self.parent, frame)
624        panel = frame.plotpanel
625       
626        try:
627            # mpl >= 1.0.0
628            ax = panel.figure.gca(projection='3d')
629        except:
630            # mpl < 1.0.0
631            try:
632                from mpl_toolkits.mplot3d import Axes3D
633                ax = Axes3D(panel.figure)
634            except:
635                logging.error("PlotPanel could not import Axes3D")
636                raise
637        panel.dimension = 3   
638        marker = ','
639        m_size = 2
640        if output.pix_type == 'atom':
641            marker = 'o'
642            m_size = 3.5
643        pos_x = output.pos_x
644        pos_y = output.pos_y
645        pos_z = output.pos_z
646        sld_mx = output.sld_mx
647        sld_my = output.sld_my
648        sld_mz = output.sld_mz
649        pix_symbol = output.pix_symbol
650
651        sld_tot = (numpy.fabs(sld_mx) + numpy.fabs(sld_my) + 
652                   numpy.fabs(sld_mz) + numpy.fabs(output.sld_n))
653        is_nonzero = sld_tot > 0.0 
654        is_zero = sld_tot == 0.0 
655       
656        # Plot null points
657        if is_zero.any():
658            ax.plot(pos_x[is_zero], pos_z[is_zero], pos_y[is_zero], marker, 
659                        c="y", alpha=0.5, markeredgecolor='y', 
660                        markersize=m_size) 
661            pos_x = pos_x[is_nonzero]
662            pos_y = pos_y[is_nonzero]
663            pos_z = pos_z[is_nonzero]
664            sld_mx = sld_mx[is_nonzero]
665            sld_my = sld_my[is_nonzero]
666            sld_mz = sld_mz[is_nonzero]
667            pix_symbol = output.pix_symbol[is_nonzero]
668        # Plot selective points in color
669        other_color = numpy.ones(len(pix_symbol), dtype='bool')
670        for key in color_dic.keys():
671            chosen_color = pix_symbol == key
672            if numpy.any(chosen_color):
673                other_color = other_color  & (chosen_color != True)
674                color = color_dic[key]
675                ax.plot(pos_x[chosen_color], pos_z[chosen_color], 
676                        pos_y[chosen_color], marker, c=color, alpha=0.5, 
677                        markeredgecolor=color, markersize=m_size, label=key) 
678        # Plot All others       
679        if numpy.any(other_color):
680            ax.plot(pos_x[other_color], pos_z[other_color], pos_y[other_color], 
681                    marker, c="k", alpha=0.5, markeredgecolor="k", 
682                    markersize=m_size, label="Other") 
683       
684        if has_arrow and len(pos_x) > 0:     
685            graph_title += "w/ Arrows" 
686            def _draw_arrow(input=None, elapsed=0.1):
687                """
688                draw w/arrow
689                """
690                max_mx = max(numpy.fabs(sld_mx))
691                max_my = max(numpy.fabs(sld_my))
692                max_mz = max(numpy.fabs(sld_mz))
693                max_m = max(max_mx, max_my, max_mz)
694                try:
695                    max_step = max(output.xstepsize, output.ystepsize, 
696                                   output.zstepsize)
697                except:
698                    max_step = 0
699                if max_step <= 0:
700                    max_step = 5
701                try:
702                    if max_m != 0:
703                        unit_x2 = sld_mx / max_m
704                        unit_y2 = sld_my / max_m
705                        unit_z2 = sld_mz / max_m
706                        # 0.8 is for avoiding the color becomes white=(1,1,1))
707                        color_x = numpy.fabs(unit_x2 * 0.8)
708                        color_y = numpy.fabs(unit_y2 * 0.8)
709                        color_z = numpy.fabs(unit_z2 * 0.8)
710                        x2 = pos_x + unit_x2 * max_step
711                        y2 = pos_y + unit_y2 * max_step
712                        z2 = pos_z + unit_z2 * max_step
713                        x_arrow = numpy.column_stack((pos_x, x2))
714                        y_arrow = numpy.column_stack((pos_y, y2))
715                        z_arrow = numpy.column_stack((pos_z, z2))
716                        colors =  numpy.column_stack((color_x, 
717                                                      color_y,
718                                                      color_z))
719                       
720                        arrows = Arrow3D(panel, x_arrow, z_arrow, y_arrow, 
721                                                  colors, 
722                                                  mutation_scale=10, lw=1, 
723                                                  arrowstyle="->", alpha = 0.5)
724                        ax.add_artist(arrows) 
725                except:
726                    pass 
727                msg = "Arrow Drawing completed.\n"
728                status_type = 'stop'
729                self._status_info(msg, status_type) 
730            msg = "Arrow Drawing is in progress..."
731           
732            status_type = 'progress'
733            self._status_info(msg, status_type) 
734            draw_out = CalcGen(input=ax,
735                             completefn=_draw_arrow, updatefn=self._update)
736            draw_out.queue()
737
738        panel.subplot.figure.subplots_adjust(left=0.05, right=0.95, 
739                                             bottom=0.05, top=0.96)
740        # Use y, z axes (in mpl 3d) as z, y axes
741        # that consistent with SANS coords.
742        ax.set_xlabel('x ($\A%s$)'% output.pos_unit)
743        ax.set_ylabel('z ($\A%s$)'% output.pos_unit)
744        ax.set_zlabel('y ($\A%s$)'% output.pos_unit)
745        if output.pix_type == 'atom':
746            ax.legend(loc='upper left', prop={'size':10})
747        num_graph = str(self.graph_num)
748        frame.SetTitle('Graph %s: %s'% (num_graph, graph_title))       
749        wx.CallAfter(frame.Show, True)
750        self.graph_num += 1
751       
752    def set_input_params(self):
753        """
754        Set model parameters
755        """
756        for list in self.parameters:
757            param_name = list[0].GetLabelText()
758            param_value = float(list[1].GetValue())
759            self.model.setParam(param_name, param_value)
760           
761    def on_compute(self, event):
762        """
763        Compute I(qx, qy)
764        """
765        flag = self.parent.check_omfpanel_inputs()
766        if not flag and self.parent.parent != None:
767            infor = 'Error'
768            msg = 'Error: Wrong inputs in the SLD info panel.'
769            # inform msg to wx
770            wx.PostEvent(self.parent.parent,
771                    StatusEvent(status=msg, info=infor))
772            self.SetFocus()
773            return
774        self.sld_data = self.parent.get_sld_from_omf()
775        if self.sld_data == None:
776            if self.parent.parent != None:
777                infor = 'Error'
778                msg = 'Error: No data has been selected.'
779                # inform msg to wx
780                wx.PostEvent(self.parent.parent,
781                        StatusEvent(status=msg, info=infor))
782                self.SetFocus()
783            return
784        flag = self._check_value()
785        if not flag:
786            _set_error(self, None, True)
787            return
788        try:
789            #vol = self.parent.get_pix_volumes()
790            #self.model.set_pixel_volumes(vol)
791            self.model.set_sld_data(self.sld_data)
792            self.set_input_params()
793            self._create_default_2d_data()
794            i_out = numpy.zeros(len(self.data.data))
795            msg = "Computation is in progress..."
796            status_type = 'progress'
797            self._status_info(msg, status_type)
798           
799            cal_out = CalcGen(input=[self.data.qx_data, 
800                                     self.data.qy_data, i_out], 
801                              completefn=self.complete, 
802                              updatefn=self._update)
803            cal_out.queue()
804           
805        except:
806            msg = "%s."% sys.exc_value
807            status_type = 'stop'
808            self._status_info(msg, status_type)
809            wx.PostEvent(self.parent.parent,
810                        StatusEvent(status=msg, info='Error'))
811            self.SetFocus()
812
813    def _check_value(self):
814        """
815        Check input values if float
816        """
817        flag = True
818        self.npt_ctl.SetBackgroundColour("white")
819        self.qmax_ctl.SetBackgroundColour("white")
820        try:
821            npt_val = float(self.npt_ctl.GetValue()) 
822            if npt_val < 2 or npt_val > 1000:
823                raise
824            self.npt_ctl.SetValue(str(int(npt_val)))
825            self.set_est_time()
826        except:
827            flag =  _set_error(self, self.npt_ctl)
828        try:
829            qmax_val = float(self.qmax_ctl.GetValue()) 
830            if qmax_val <= 0 or qmax_val > 1000:
831                raise
832        except:
833            flag = _set_error(self, self.qmax_ctl)       
834        for list in self.parameters:
835            list[1].SetBackgroundColour("white")
836            param_name = list[0].GetLabelText()
837            try: 
838                param_val = float(list[1].GetValue())
839                if param_name.count('frac') > 0:
840                    if param_val < 0 or param_val > 1:
841                       raise
842            except:
843                flag = _set_error(self, list[1])
844        return flag
845                   
846    def _status_info(self, msg = '', type = "update"):
847        """
848        Status msg
849        """
850        if type == "stop":
851            label = "Compute"
852            able = True
853        else:   
854            label = "Wait..."
855            able = False
856        self.bt_compute.Enable(able)
857        self.bt_compute.SetLabel(label)
858        self.bt_compute.SetToolTipString(label)
859        if self.parent.parent != None:
860            wx.PostEvent(self.parent.parent, 
861                             StatusEvent(status = msg, type = type )) 
862
863    def _update(self, time=None):
864        """
865        Update the output of plotting model
866        """
867        if self.parent.parent != None:
868            msg = "Computation/drawing is in progress..."
869            wx.PostEvent(self.parent.parent, 
870                         StatusEvent(status=msg, type="update"))
871                               
872    def complete(self, input, elapsed=None):   
873        """
874        Gen compute complete function
875        :Param input: input list [qx_data, qy_data, i_out]
876        """
877        out = self.model.runXY(input)
878        self._draw2D(out)
879        msg = "Gen computation completed.\n"
880        status_type = 'stop'
881        self._status_info(msg, status_type)
882       
883    def _create_default_2d_data(self):
884        """
885        Create 2D data by default
886        Only when the page is on theory mode.
887        :warning: This data is never plotted.
888        """
889        self.qmax_x = float(self.qmax_ctl.GetValue())
890        self.npts_x = int(float(self.npt_ctl.GetValue()))
891        self.data = Data2D()
892        qmax = self.qmax_x #/ numpy.sqrt(2)
893        self.data.xaxis('\\rm{Q_{x}}', '\AA^{-1}')
894        self.data.yaxis('\\rm{Q_{y}}', '\AA^{-1}')
895        self.data.is_data = False
896        self.data.id = str(self.uid) + " GenData"
897        self.data.group_id = str(self.uid) + " Model2D"
898        ## Default values
899        self.data.detector.append(Detector())
900        index = len(self.data.detector) - 1
901        self.data.detector[index].distance = 8000   # mm
902        self.data.source.wavelength = 6             # A
903        self.data.detector[index].pixel_size.x = 5  # mm
904        self.data.detector[index].pixel_size.y = 5  # mm
905        self.data.detector[index].beam_center.x = qmax
906        self.data.detector[index].beam_center.y = qmax
907        xmax = qmax
908        xmin = -qmax
909        ymax = qmax
910        ymin = -qmax
911        qstep = self.npts_x
912
913        x = numpy.linspace(start=xmin, stop=xmax, num=qstep, endpoint=True)
914        y = numpy.linspace(start=ymin, stop=ymax, num=qstep, endpoint=True)
915        ## use data info instead
916        new_x = numpy.tile(x, (len(y), 1))
917        new_y = numpy.tile(y, (len(x), 1))
918        new_y = new_y.swapaxes(0, 1)
919        # all data reuire now in 1d array
920        qx_data = new_x.flatten()
921        qy_data = new_y.flatten()
922        q_data = numpy.sqrt(qx_data * qx_data + qy_data * qy_data)
923        # set all True (standing for unmasked) as default
924        mask = numpy.ones(len(qx_data), dtype=bool)
925        # store x and y bin centers in q space
926        x_bins = x
927        y_bins = y
928        self.data.source = Source()
929        self.data.data = numpy.ones(len(mask))
930        self.data.err_data = numpy.ones(len(mask))
931        self.data.qx_data = qx_data
932        self.data.qy_data = qy_data
933        self.data.q_data = q_data
934        self.data.mask = mask
935        self.data.x_bins = x_bins
936        self.data.y_bins = y_bins
937        # max and min taking account of the bin sizes
938        self.data.xmin = xmin
939        self.data.xmax = xmax
940        self.data.ymin = ymin
941        self.data.ymax = ymax
942       
943    def _draw2D(self, image):
944        """
945        Complete get the result of modelthread and create model 2D
946        that can be plot.
947        """
948        page_id = self.id
949        data = self.data
950       
951        model = self.model
952        qmin = 0.0
953        state = None
954       
955        numpy.nan_to_num(image)
956        new_plot = Data2D(image=image, err_image=data.err_data)
957        new_plot.name = model.name + '2d'
958        new_plot.title = "Generic model 2D "
959        new_plot.id = str(page_id) + ': ' + self.file_name \
960                        + ' #%s'% str(self.graph_num)
961        new_plot.group_id = str(page_id) + " Model2D"
962        new_plot.detector = data.detector
963        new_plot.source = data.source
964        new_plot.is_data = False
965        new_plot.qx_data = data.qx_data
966        new_plot.qy_data = data.qy_data
967        new_plot.q_data = data.q_data
968        new_plot.mask = data.mask
969        ## plot boundaries
970        new_plot.ymin = data.ymin
971        new_plot.ymax = data.ymax
972        new_plot.xmin = data.xmin
973        new_plot.xmax = data.xmax
974        title = data.title
975        _yaxis, _yunit = data.get_yaxis()
976        _xaxis, _xunit = data.get_xaxis()
977        new_plot.xaxis(str(_xaxis), str(_xunit))
978        new_plot.yaxis(str(_yaxis), str(_yunit))
979       
980        new_plot.is_data = False
981        if data.is_data:
982            data_name = str(data.name)
983        else:
984            data_name = str(model.__class__.__name__) + '2d'
985
986        if len(title) > 1:
987            new_plot.title = "Gen Theory for %s "% model.name + data_name
988        new_plot.name = new_plot.id
989        new_plot.label = new_plot.id
990        #theory_data = deepcopy(new_plot)
991        if self.parent.parent != None:
992            self.parent.parent.update_theory(data_id=data.id,
993                                           theory=new_plot,
994                                           state=state)
995        title = new_plot.title
996        num_graph = str(self.graph_num)
997        wx.CallAfter(self.parent.draw_graph, new_plot, 
998                     title="Graph %s: "% num_graph + new_plot.id )
999        self.graph_num += 1
1000       
1001    def set_scale2d(self, scale):
1002        """
1003        Set SLD plot scale
1004        """
1005        self.scale2d = None
1006       
1007    def on_panel_close(self, event):
1008        """
1009        On Close SLD panel
1010        """
1011        #Not implemented   
1012                 
1013class OmfPanel(ScrolledPanel, PanelBase):
1014    """
1015        Provides the sas gen calculator GUI.
1016    """
1017    ## Internal nickname for the window, used by the AUI manager
1018    window_name = "SLD Pixel Info"
1019    ## Name to appear on the window title bar
1020    window_caption = "SLD Pixel Info "
1021    ## Flag to tell the AUI manager to put this panel in the center pane
1022    CENTER_PANE = False
1023   
1024    def __init__(self, parent, *args, **kwds):
1025        ScrolledPanel.__init__(self, parent, style=wx.RAISED_BORDER, 
1026                               *args, **kwds)
1027        PanelBase.__init__(self)
1028        #Font size
1029        self.SetWindowVariant(variant=FONT_VARIANT)
1030        self.SetupScrolling() 
1031        # Object that receive status event
1032        self.parent = parent
1033        self.sld_data = sans_gen.MagSLD([0], [0], [0]) 
1034        self.sld_ctl = None
1035        self.default_shape = 'rectangular'
1036        self._do_layout()
1037       
1038    def set_slddata(self, slddata):
1039        """
1040        Set sld data related items
1041        """
1042        self.sld_data = slddata
1043        self._set_slddata_ctr_val(slddata)
1044        # Make sure that self._set_slddata_ctr_val() is finished
1045        wx.CallAfter(self._set_omfdata_ctr, slddata)
1046   
1047    def get_sld_val(self):
1048        """
1049        Set sld_n of slddata on sld input
1050        """
1051        sld_sets = {}
1052        if not self.sld_data.is_data:
1053            self._get_other_val()
1054        for list in self.slds:
1055            if list[1].IsEnabled():
1056                list[1].SetBackgroundColour("white")
1057                list[1].Refresh()
1058                try:
1059                    val = float(list[1].GetValue())
1060                    sld_sets[list[0]] = val
1061                except:
1062                    flag = _set_error(self, list[1])
1063                    if not flag:
1064                        return self.sld_data
1065            else:
1066               sld_sets[list[0]] = None
1067        for key in sld_sets.keys():
1068            key_low = key.lower()
1069            if key_low.count('mx') > 0:
1070                if sld_sets[key] == None:
1071                    sld_sets[key] = self.sld_data.sld_mx
1072                mx = sld_sets[key]
1073            elif key_low.count('my') > 0:
1074                if sld_sets[key] == None:
1075                    sld_sets[key] = self.sld_data.sld_my
1076                my = sld_sets[key]
1077            elif key_low.count('mz') > 0:
1078                if sld_sets[key] == None:
1079                    sld_sets[key] = self.sld_data.sld_mz
1080                mz = sld_sets[key]
1081            else:
1082                if sld_sets[key] != None:
1083                    self.sld_data.set_sldn(sld_sets[key])
1084        self.sld_data.set_sldms(mx, my, mz)
1085        self._set_slddata_ctr_val(self.sld_data)
1086       
1087        return self.sld_data
1088   
1089    def get_pix_volumes(self):
1090        """
1091        Get the pixel volume
1092        """
1093        vol = self.sld_data.vol_pix
1094       
1095        return vol
1096               
1097    def _get_other_val(self): 
1098        """
1099        """
1100        omfdata = sans_gen.OMFData()
1101        sets = {}
1102        try:
1103            for lst in self.stepsize:
1104                if lst[1].IsEnabled():
1105                    val = float(lst[1].GetValue())
1106                    sets[lst[0]] = val
1107                else:
1108                    sets[lst[0]] = None
1109                    return
1110            for lst in self.nodes:
1111                if lst[1].IsEnabled():
1112                    val = float(lst[1].GetValue())
1113                    sets[lst[0]] = val
1114                else:
1115                    sets[lst[0]] = None
1116                    return
1117                   
1118            for key in sets.keys():
1119                exec "omfdata.%s = sets['%s']"% (key, key)           
1120
1121            omf2sld = sans_gen.OMF2SLD()
1122            omf2sld.set_data(omfdata, self.default_shape)
1123            self.sld_data = omf2sld.output
1124            self.sld_data.is_data = False
1125            self.sld_data.filename = "Default SLD Profile"
1126        except:
1127            msg = "OMF Panel: %s"% sys.exc_value
1128            infor = 'Error'
1129            #logging.error(msg)
1130            if self.parent.parent != None:
1131                # inform msg to wx
1132                wx.PostEvent(self.parent.parent,
1133                        StatusEvent(status=msg, info=infor))
1134                self.SetFocus()
1135               
1136    def _set_slddata_ctr_val(self, slddata):
1137        """
1138        Set slddata crl
1139        """
1140        try:
1141            val = str(len(slddata.sld_n))
1142        except:
1143            val = 'Unknown'
1144        self.npix_ctl.SetValue(val)
1145       
1146    def _set_omfdata_ctr(self, omfdata):   
1147        """
1148        Set the textctr box values
1149        """
1150       
1151        if omfdata == None:
1152            self._set_none_text()
1153            return
1154        nodes_list = self._get_nodes_key_list(omfdata)
1155        step_list = self._get_step_key_list(omfdata)
1156        for ctr_list in self.nodes:
1157            for key in nodes_list.keys():
1158                if ctr_list[0] == key:
1159                    ctr_list[1].SetValue(format_number(nodes_list[key], True))
1160                    ctr_list[1].Enable(not omfdata.is_data)
1161                    break
1162        for ctr_list in self.stepsize:
1163            for key in step_list.keys():
1164                if ctr_list[0] == key:
1165                    ctr_list[1].SetValue(format_number(step_list[key], True))
1166                    ctr_list[1].Enable(not omfdata.is_data)
1167                    break   
1168               
1169    def _set_none_text(self):
1170        """
1171        Set Unknown in textctrls
1172        """
1173        val = 'Unknown'
1174        for ctr_list in self.nodes:
1175            ctr_list[1].SetValue(val)
1176        for ctr_list in self.stepsize:
1177            ctr_list[1].SetValue(val)
1178               
1179    def _define_structure(self):
1180        """
1181        Define the main sizers building to build this application.
1182        """
1183        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
1184       
1185        self.npixels_sizer = wx.BoxSizer(wx.HORIZONTAL)
1186        self.box_sld = wx.StaticBox(self, -1, 
1187                                    str("Mean SLD"))
1188        self.box_node = wx.StaticBox(self, -1, str("Nodes"))
1189        self.boxsizer_sld = wx.StaticBoxSizer(self.box_sld, wx.VERTICAL)
1190        self.box_stepsize = wx.StaticBox(self, -1, str("Step Size"))
1191        self.boxsizer_node = wx.StaticBoxSizer(self.box_node, wx.VERTICAL)
1192        self.boxsizer_stepsize = wx.StaticBoxSizer(self.box_stepsize,
1193                                                    wx.VERTICAL)
1194        self.sld_sizer = wx.BoxSizer(wx.HORIZONTAL)
1195        self.node_sizer = wx.BoxSizer(wx.HORIZONTAL)
1196        self.step_sizer = wx.BoxSizer(wx.HORIZONTAL)
1197        self.hint_sizer = wx.BoxSizer(wx.HORIZONTAL)
1198        self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
1199               
1200    def _layout_npix(self):
1201        """
1202        Build No of pixels sizer
1203        """
1204        num_pix_text = wx.StaticText(self, -1, "No. of Pixels: ") 
1205        self.npix_ctl = OutputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
1206                                style=wx.TE_PROCESS_ENTER)
1207        self._set_slddata_ctr_val(self.sld_data)
1208        self._set_omfdata_ctr(self.sld_data) 
1209        self.npixels_sizer.AddMany([(num_pix_text, 0,
1210                                          wx.EXPAND|wx.LEFT|wx.TOP, 5),
1211                                     (self.npix_ctl, 0,
1212                                     wx.EXPAND|wx.TOP, 5)])
1213
1214    def _layout_slds(self):
1215        """
1216        Build nuclear sld sizer
1217        """
1218        self.slds = []
1219        omfdata = self.sld_data
1220        if omfdata == None:
1221            raise
1222        sld_key_list = self._get_slds_key_list(omfdata)
1223        # Dic is not sorted
1224        key_list = [key for key in sld_key_list.keys()]
1225        # Sort here
1226        key_list.sort()
1227        is_data = self.sld_data.is_data
1228        sizer = wx.GridBagSizer(2, 3)
1229        ix = 0
1230        iy = -1
1231        for key in key_list:
1232            value = sld_key_list[key]
1233            iy += 1
1234            ix = 0
1235            name = wx.StaticText(self, -1, key)
1236            sizer.Add(name, (iy, ix), (1, 1), \
1237                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
1238            ## add parameter value
1239            ix += 1
1240            ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
1241                                style=wx.TE_PROCESS_ENTER)
1242            ctl.SetValue(format_number(value, True))
1243            ctl.Enable(not is_data)
1244            sizer.Add(ctl, (iy, ix), (1, 1), wx.EXPAND)
1245            ## add unit
1246            ix += 1
1247            s_unit = '[' + omfdata.sld_unit + ']'
1248            unit = wx.StaticText(self, -1, s_unit)
1249            sizer.Add(unit, (iy, ix), (1, 1), \
1250                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
1251            self.slds.append([key, ctl, unit])
1252        self.sld_sizer.Add(sizer, 0, wx.LEFT, 10) 
1253               
1254    def _layout_nodes(self):
1255        """
1256        Fill the sizer containing data's name
1257        """
1258        self.nodes = []
1259        omfdata = self.sld_data
1260        if omfdata == None:
1261            raise
1262        key_list = self._get_nodes_key_list(omfdata)
1263        is_data = self.sld_data.is_data
1264        sizer = wx.GridBagSizer(2, 3)
1265        ix = 0
1266        iy = -1
1267        for key, value in key_list.iteritems():
1268            iy += 1
1269            ix = 0
1270            name = wx.StaticText(self, -1, key)
1271            sizer.Add(name, (iy, ix), (1, 1), \
1272                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
1273            ## add parameter value
1274            ix += 1
1275            ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
1276                                style=wx.TE_PROCESS_ENTER)
1277            ctl.Bind(wx.EVT_TEXT, self._onparamEnter )
1278            ctl.SetValue(format_number(value, True))
1279            ctl.Enable(not is_data)
1280            sizer.Add(ctl, (iy, ix), (1, 1), wx.EXPAND)
1281            ## add unit
1282            ix += 1
1283            unit = wx.StaticText(self, -1, '')
1284            sizer.Add(unit, (iy, ix), (1, 1), \
1285                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
1286            self.nodes.append([key, ctl, unit])
1287        self.node_sizer.Add(sizer, 0, wx.LEFT, 10)
1288           
1289    def _layout_stepsize(self):
1290        """
1291        Fill the sizer containing slit size information
1292        """
1293        self.stepsize = []
1294        omfdata = self.sld_data
1295        if omfdata == None:
1296            raise
1297        key_list = self._get_step_key_list(omfdata)
1298        is_data = self.sld_data.is_data
1299        sizer = wx.GridBagSizer(2, 3)
1300        ix = 0
1301        iy = -1
1302        #key_list.sort()
1303        for key, value in key_list.iteritems():
1304            iy += 1
1305            ix = 0
1306            name = wx.StaticText(self, -1, key)
1307            sizer.Add(name, (iy, ix), (1, 1), \
1308                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
1309            ## add parameter value
1310            ix += 1
1311            ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
1312                                style=wx.TE_PROCESS_ENTER)
1313            ctl.Bind(wx.EVT_TEXT, self._onstepsize )
1314            ctl.SetValue(format_number(value, True))
1315            ctl.Enable(not is_data)
1316            sizer.Add(ctl, (iy, ix), (1, 1), wx.EXPAND)
1317            ## add unit
1318            ix += 1
1319            p_unit = '[' + omfdata.pos_unit + ']'
1320            unit = wx.StaticText(self, -1, p_unit)
1321            sizer.Add(unit, (iy, ix), (1, 1), \
1322                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
1323            self.stepsize.append([key, ctl, unit])
1324        self.step_sizer.Add(sizer, 0, wx.LEFT, 10)
1325   
1326    def _layout_hint(self):
1327        """
1328        Fill the sizer containing hint
1329        """
1330        hint_msg = "Load an omf or 3d sld profile data file."
1331        self.hint_txt = wx.StaticText(self, -1, hint_msg)
1332        self.hint_sizer.AddMany([(self.hint_txt, 0, wx.LEFT, 15)])
1333   
1334    def _layout_button(self): 
1335        """
1336        Do the layout for the button widgets
1337        """ 
1338        self.bt_draw = wx.Button(self, wx.NewId(),'Draw Points')
1339        self.bt_draw.Bind(wx.EVT_BUTTON, self.on_sld_draw)
1340        self.bt_draw.SetToolTipString("Draw a scatter plot for sld profile.")
1341        self.bt_save = wx.Button(self, wx.NewId(),'Save SLD Data')
1342        self.bt_save.Bind(wx.EVT_BUTTON, self.on_save)
1343        self.bt_save.Enable(False)
1344        self.bt_save.SetToolTipString("Save SLD data.")
1345        self.button_sizer.AddMany([(self.bt_draw, 0, wx.LEFT, 10),
1346                                   (self.bt_save, 0, wx.LEFT, 10)])
1347       
1348    def _do_layout(self):
1349        """
1350        Draw window content
1351        """
1352        self._define_structure()
1353        self._layout_nodes()
1354        self._layout_stepsize()
1355        self._layout_npix()
1356        self._layout_slds()
1357        #self._layout_hint()
1358        self._layout_button()
1359        self.boxsizer_node.AddMany([(self.node_sizer, 0,
1360                                    wx.EXPAND|wx.TOP, 5),
1361                                     (self.hint_sizer, 0,
1362                                     wx.EXPAND|wx.TOP|wx.BOTTOM, 5)])
1363        self.boxsizer_stepsize.AddMany([(self.step_sizer, 0,
1364                                     wx.EXPAND|wx.TOP|wx.BOTTOM, 5),])
1365        self.boxsizer_sld.AddMany([(self.sld_sizer, 0,
1366                                     wx.EXPAND|wx.BOTTOM, 5),])
1367        self.main_sizer.AddMany([(self.npixels_sizer, 0, wx.EXPAND|wx.ALL, 10),
1368                        (self.boxsizer_sld, 0, wx.EXPAND|wx.ALL, 10),
1369                        (self.boxsizer_node, 0, wx.EXPAND|wx.ALL, 10),
1370                        (self.boxsizer_stepsize, 0, wx.EXPAND|wx.ALL, 10),
1371                        (self.button_sizer, 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5)])
1372        self.SetSizer(self.main_sizer)
1373        self.SetAutoLayout(True)
1374       
1375    def _get_nodes_key_list(self, data):
1376        """
1377        Return nodes key list
1378       
1379        :Param data: OMFData
1380        """
1381        key_list = {'xnodes' : data.xnodes, 
1382                    'ynodes' : data.ynodes,
1383                    'znodes' : data.znodes}
1384        return key_list
1385       
1386    def _get_slds_key_list(self, data):
1387        """
1388        Return nodes key list
1389       
1390        :Param data: OMFData
1391        """
1392        key_list = {'Nucl.' : data.sld_n, 
1393                    'Mx' : data.sld_mx,
1394                    'My' : data.sld_my,
1395                    'Mz' : data.sld_mz}
1396        return key_list
1397
1398    def _get_step_key_list(self, data):
1399        """
1400        Return step key list
1401       
1402        :Param data: OMFData
1403        """
1404        key_list = {'xstepsize' : data.xstepsize, 
1405                    'ystepsize' : data.ystepsize,
1406                    'zstepsize' : data.zstepsize}
1407        return key_list       
1408   
1409    def set_sld_ctr(self, sld_data):
1410        """
1411        Set sld textctrls
1412        """
1413        if sld_data == None:
1414            for ctr_list in self.slds:
1415                ctr_list[1].Enable(False)
1416                #break   
1417            return
1418       
1419        self.sld_data = sld_data
1420        sld_list = self._get_slds_key_list(sld_data)
1421        for ctr_list in self.slds:
1422            for key in sld_list.keys():
1423                if ctr_list[0] == key:
1424                    min_val = numpy.min(sld_list[key])
1425                    max_val = numpy.max(sld_list[key])
1426                    mean_val = numpy.mean(sld_list[key])
1427                    enable = (min_val == max_val) and \
1428                             sld_data.pix_type == 'pixel'
1429                    ctr_list[1].SetValue(format_number(mean_val, True))
1430                    ctr_list[1].Enable(enable)
1431                    #ctr_list[2].SetLabel("[" + sld_data.sld_unit + "]")
1432                    break   
1433
1434    def on_sld_draw(self, event):
1435        """
1436        Draw sld profile as scattered plot
1437        """
1438        self.parent.sld_draw()
1439         
1440    def on_save(self, event):
1441        """
1442        Close the window containing this panel
1443        """
1444        flag = True
1445        flag = self.check_inputs()
1446        if not flag:
1447            return
1448        self.sld_data = self.get_sld_val()
1449        self.parent.set_main_panel_sld_data(self.sld_data)
1450       
1451        reader = sans_gen.SLDReader() 
1452        extension = '*.sld'
1453        path = None
1454        data= None
1455        location = self.parent.get_path()
1456        dlg = wx.FileDialog(self, "Save sld file",
1457                            location, "sld_file",
1458                             extension, 
1459                             wx.SAVE)
1460        if dlg.ShowModal() == wx.ID_OK:
1461            path = dlg.GetPath()
1462            self.parent.set_file_location(os.path.dirname(path))
1463        else:
1464            return None
1465        dlg.Destroy()
1466        try:
1467            if path is None:
1468                return
1469           
1470            data = self.parent.get_sld_data()
1471            fName = os.path.splitext(path)[0] + '.' + extension.split('.')[-1]
1472            if data != None:
1473                try:
1474                    reader.write(fName, data)
1475                except:
1476                    raise
1477            else:
1478                msg = "%s cannot write %s\n" % ('Generic Scattering', str(path))
1479                infor = 'Error'
1480                #logging.error(msg)
1481                if self.parent.parent != None:
1482                    # inform msg to wx
1483                    wx.PostEvent(self.parent.parent,
1484                            StatusEvent(status=msg, info=infor))
1485                    self.SetFocus()
1486            return
1487        except:
1488            msg = "Error occurred while saving. "
1489            infor = 'Error'
1490            if self.parent.parent != None:
1491                # inform msg to wx
1492                wx.PostEvent(self.parent.parent,
1493                        StatusEvent(status=msg, info=infor)) 
1494                self.SetFocus()   
1495
1496    def _onparamEnter(self, event):
1497        """
1498        """
1499        flag = True
1500        if event != None:
1501            event.Skip()
1502            ctl = event.GetEventObject()
1503            ctl.SetBackgroundColour("white")
1504            #_set_error(self, ctl)
1505        try:
1506            float(ctl.GetValue())
1507        except:
1508            flag = _set_error(self, ctl)
1509        if flag:
1510            npts = 1
1511            for item in self.nodes:
1512                n_val = float(item[1].GetValue())
1513                if n_val <= 0:
1514                    item[1].SetBackgroundColour("pink")
1515                    npts = -1
1516                    break
1517                if numpy.isfinite(n_val):
1518                    npts *= int(n_val)
1519            if npts > 0:
1520                nop = self.set_npts_from_slddata()
1521                if nop == None:
1522                    nop = npts
1523                self.display_npts(nop)
1524        ctl.Refresh()
1525        return flag
1526   
1527    def _onstepsize(self, event):
1528        """
1529        On stepsize event
1530        """
1531        flag = True
1532        if event != None:
1533            event.Skip()
1534            ctl = event.GetEventObject()
1535            ctl.SetBackgroundColour("white")
1536
1537        if flag and not self.sld_data.is_data:#ctl.IsEnabled():
1538            s_size = 1.0
1539            try:
1540                for item in self.stepsize:
1541                    s_val = float(item[1].GetValue())
1542                    if s_val <= 0:
1543                        item[1].SetBackgroundColour("pink")
1544                        ctl.Refresh()
1545                        return
1546                    if numpy.isfinite(s_val):
1547                        s_size *= s_val
1548                self.sld_data.set_pixel_volumes(s_size)
1549            except:
1550                pass
1551        ctl.Refresh()
1552 
1553         
1554    def set_npts_from_slddata(self):
1555        """
1556        Set total n. of points form the sld data
1557        """
1558        try:
1559            sld_data = self.parent.get_sld_from_omf()
1560            #nop = (nop * numpy.pi) / 6
1561            nop = len(sld_data.sld_n)
1562        except:
1563            nop = None
1564        return nop
1565   
1566    def display_npts(self, nop):
1567        """
1568        Displays Npts ctrl
1569        """
1570        try:
1571            self.npix_ctl.SetValue(str(nop))
1572            self.npix_ctl.Refresh()
1573            self.parent.set_etime()
1574        except:
1575            # On Init
1576            pass
1577   
1578    def check_inputs(self):
1579        """
1580        check if the inputs are valid
1581        """
1582        flag = self._check_input_helper(self.slds)
1583        if flag:
1584            flag = self._check_input_helper(self.nodes)
1585        if flag:
1586            flag = self._check_input_helper(self.stepsize)
1587        return flag
1588   
1589    def _check_input_helper(self, list):
1590        """
1591        Check list values
1592        """
1593        flag = True
1594        for item in list:
1595            item[1].SetBackgroundColour("white")
1596            item[1].Refresh()
1597            try:
1598                float(item[1].GetValue())
1599            except:
1600                flag = _set_error(self, item[1])
1601                break
1602        return flag
1603
1604class SasGenWindow(wx.Frame):
1605    """
1606    GEN SAS main window
1607    """
1608    def __init__(self, parent=None, title="Generic Scattering Calculator",
1609                size=(PANEL_WIDTH * 1.3, PANEL_HEIGHT * 1.57), *args, **kwds):
1610        """
1611        Init
1612        """
1613        kwds['size'] = size
1614        kwds['title'] = title
1615       
1616        wx.Frame.__init__(self, parent, *args, **kwds)
1617        self.parent = parent
1618        self.omfpanel = OmfPanel(parent=self)
1619        self.panel = SasGenPanel(parent=self)
1620        self.data = None
1621        self.omfdata = sans_gen.OMFData()
1622        self.sld_data = None
1623        self._default_save_location = os.getcwd() 
1624       
1625        self._mgr = aui.AuiManager(self)
1626        self._mgr.SetDockSizeConstraint(0.5, 0.5)
1627        self._plot_title = ''
1628        self.scale2d = 'log_{10}'
1629       
1630        self._build_toolbar()
1631        self._build_menubar()
1632       
1633        self.build_panels()
1634        self.Centre()
1635        self.Show(True)
1636   
1637    def _build_toolbar(self):
1638        """
1639        Build toolbar
1640        """
1641        tsize = (20, 20)
1642        tb = self.CreateToolBar(wx.TB_HORIZONTAL | wx.TB_FLAT)
1643        open_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_TOOLBAR, 
1644                                            tsize)
1645        save_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE_AS, wx.ART_TOOLBAR,
1646                                            tsize)
1647        close_bmp = wx.ArtProvider.GetBitmap(wx.ART_QUIT, wx.ART_TOOLBAR, 
1648                                            tsize)
1649        help_bmp = wx.ArtProvider.GetBitmap(wx.ART_HELP, wx.ART_TOOLBAR, 
1650                                           (17, 20))
1651       
1652        id = wx.NewId()
1653        tb.AddLabelTool(id, "Open", open_bmp, shortHelp="Open", 
1654                        longHelp="Open sld/omf file")
1655        self.Bind(wx.EVT_TOOL, self.on_open_file, id=id)
1656
1657        id = wx.NewId()
1658        tb.AddSimpleTool(id, save_bmp, "Save", "Save as sld file")
1659        self.Bind(wx.EVT_TOOL, self.on_save_file, id=id)
1660       
1661        tb.AddSeparator()
1662        id = wx.NewId()
1663        tb.AddSimpleTool(id, close_bmp, "Quit", "Quit")
1664        self.Bind(wx.EVT_TOOL, self.on_close, id=id)
1665       
1666        tb.AddSeparator()
1667        id = wx.NewId()
1668        tb.AddSimpleTool(id, help_bmp, "Help", "Help")
1669        self.Bind(wx.EVT_TOOL, self.on_help, id=id)
1670
1671        tb.Realize()
1672       
1673    def _build_menubar(self):
1674        """
1675        Build menubar
1676        """
1677        tsize = (13, 13)
1678        open_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_TOOLBAR, 
1679                                            tsize)
1680        save_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE_AS, wx.ART_TOOLBAR,
1681                                            tsize)
1682        quit_bmp = wx.ArtProvider.GetBitmap(wx.ART_QUIT, wx.ART_TOOLBAR, 
1683                                           tsize)
1684        help_bmp = wx.ArtProvider.GetBitmap(wx.ART_HELP, wx.ART_TOOLBAR, 
1685                                           (13, 15))
1686       
1687        menu_bar = wx.MenuBar()
1688       
1689        menu = wx.Menu()
1690        id = wx.NewId()
1691        item = wx.MenuItem(menu, id, "&Open sld/omf file")
1692        item.SetBitmap(open_bmp)
1693        menu.AppendItem(item)
1694        wx.EVT_MENU(self, id, self.on_open_file)
1695       
1696        id = wx.NewId()
1697        item = wx.MenuItem(menu, id, "&Save as sld file")
1698        item.SetBitmap(save_bmp)
1699        menu.AppendItem(item)
1700        wx.EVT_MENU(self, id, self.on_save_file)
1701       
1702        menu.AppendSeparator()
1703        id = wx.NewId()
1704        item = wx.MenuItem(menu, id, "&Quit")
1705        item.SetBitmap(quit_bmp)
1706        menu.AppendItem(item)
1707
1708        menu_bar.Append(menu, "&File")
1709        wx.EVT_MENU(self, id, self.on_close)
1710       
1711        menu_help = wx.Menu()
1712        id = wx.NewId()
1713        item = wx.MenuItem(menu_help, id, "&Theory and GUI")
1714        item.SetBitmap(help_bmp)
1715        menu_help.AppendItem(item)
1716        wx.EVT_MENU(self, id, self.on_help)
1717       
1718        menu_bar.Append(menu_help, "&Help")
1719       
1720        self.SetMenuBar(menu_bar)
1721       
1722    def build_panels(self):
1723        """
1724        """
1725       
1726        self.set_sld_data(self.sld_data)
1727       
1728        self._mgr.AddPane(self.panel, aui.AuiPaneInfo().
1729                              Name(self.panel.window_name).
1730                              CenterPane().
1731                              # This is where we set the size of
1732                              # the application window
1733                              BestSize(wx.Size(PANEL_WIDTH, 
1734                                               PANEL_HEIGHT)).
1735                              Show()) 
1736        self._mgr.AddPane(self.omfpanel, aui.AuiPaneInfo().
1737                              Name(self.omfpanel.window_name).
1738                              Caption(self.omfpanel.window_caption).
1739                              CloseButton(False).
1740                              Right().
1741                              Floatable(False).
1742                              BestSize(wx.Size(PANEL_WIDTH/2.5, PANEL_HEIGHT)).
1743                              Show()) 
1744        self._mgr.Update()
1745
1746    def get_sld_data(self):
1747        """
1748        Return slddata
1749        """
1750        return self.sld_data
1751   
1752    def get_sld_from_omf(self):
1753        """
1754        """
1755        self.sld_data = self.omfpanel.get_sld_val()
1756        return self.sld_data
1757   
1758    def set_sld_n(self, sld):
1759        """
1760        """
1761        self.panel.sld_data = sld
1762        self.panel.model.set_sld_data(sld)
1763       
1764    def set_sld_data(self, data):
1765        """
1766        Set omfdata
1767        """
1768        if data == None:
1769            return
1770        self.sld_data = data
1771        enable = (not data==None)
1772        self._set_omfpanel_sld_data(self.sld_data)
1773        self.omfpanel.bt_save.Enable(enable)
1774        self.set_etime()
1775   
1776    def set_omfpanel_npts(self):
1777        """
1778        Set Npts in omf panel
1779        """
1780        nop = self.omfpanel.set_npts_from_slddata()
1781        self.omfpanel.display_npts(nop)
1782       
1783    def _set_omfpanel_sld_data(self, data):
1784        """
1785        Set sld_data in omf panel
1786        """
1787        self.omfpanel.set_slddata(data) 
1788        self.omfpanel.set_sld_ctr(data)
1789   
1790    def check_omfpanel_inputs(self):
1791        """
1792        Check OMF panel inputs
1793        """
1794        return self.omfpanel.check_inputs() 
1795       
1796    def set_main_panel_sld_data(self, sld_data):
1797        """
1798        """
1799        self.sld_data = sld_data
1800       
1801    def set_file_location(self, path):
1802        """
1803        File location
1804        """
1805        self._default_save_location = path
1806   
1807    def get_path(self):
1808        """
1809        File location
1810        """
1811        return self._default_save_location
1812   
1813    def draw_graph(self, plot, title=''):
1814        """
1815        """
1816        frame = PlotFrame(self, -1, 'testView', self.scale2d)
1817        add_icon(self.parent, frame)
1818        frame.add_plot(plot)
1819        frame.SetTitle(title)
1820        frame.Show(True)
1821        frame.SetFocus()
1822   
1823    def get_npix(self):
1824        """
1825        Get no. of pixels from omf panel
1826        """
1827        n_pix = self.omfpanel.npix_ctl.GetValue()
1828        return n_pix
1829   
1830    def get_pix_volumes(self):
1831        """
1832        Get a pixel volume
1833        """
1834        vol = self.omfpanel.get_pix_volumes()
1835        return vol
1836   
1837    def set_omfpanel_default_shap(self, shape):
1838        """
1839        Set default_shape in omfpanel
1840        """
1841        self.omfpanel.default_shape = shape
1842   
1843    def set_etime(self):
1844        """
1845        Sets est. computation time on panel
1846        """
1847        self.panel.set_est_time()
1848   
1849    def get_sld_data_from_omf(self):
1850        """
1851        """
1852        data = self.omfpanel.get_sld_val() 
1853        return data
1854       
1855    def set_scale2d(self, scale):
1856        """
1857        """
1858        self.scale2d = scale
1859           
1860    def on_panel_close(self, event):
1861        """
1862        """
1863        #Not implemented
1864         
1865    def on_open_file(self, event):
1866        """
1867        On Open
1868        """
1869        self.panel.on_load_data(event)
1870   
1871    def sld_draw(self):
1872        """
1873        sld draw
1874        """
1875        self.panel.sld_draw(has_arrow=False)
1876       
1877    def on_save_file(self, event):
1878        """
1879        On Close
1880        """
1881        self.omfpanel.on_save(event)
1882       
1883    def on_close(self, event):
1884        """
1885        Close
1886        """
1887        self.Destroy()
1888       
1889    def on_help(self, event):   
1890        """
1891        Gen scatter angle help panel
1892        """
1893        from sans.perspectives.calculator.help_panel import  HelpWindow
1894        # Get models help model_function path
1895        import sans.perspectives.calculator as calmedia
1896
1897        media = calmedia.get_data_path(media='media')
1898        path = os.path.join(media,"gen_sans_help.html") 
1899        name = "Polarization Angle"
1900        frame = HelpWindow(self, -1, title=' Help: Polarization Angle', 
1901                           pageToOpen=path, size=(865, 450))   
1902        try: 
1903            frame.splitter.DetachWindow(frame.lpanel)
1904            # Display only the right side one
1905            frame.lpanel.Hide() 
1906            frame.Show(True)
1907        except:
1908            frame.Destroy() 
1909            msg = 'Display Error\n'
1910            info = "Info"
1911            wx.MessageBox(msg, info)
1912           
1913if __name__ == "__main__": 
1914    app = wx.PySimpleApp()
1915    SGframe = SasGenWindow()   
1916    SGframe.Show(True)
1917    app.MainLoop()     
Note: See TracBrowser for help on using the repository browser.