source: sasview/calculatorview/src/sans/perspectives/calculator/model_editor.py @ 83e0aa1

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 83e0aa1 was 46472c61, checked in by Jae Cho <jhjcho@…>, 13 years ago

fixed a bug: not proper line indentation for some cases

  • Property mode set to 100644
File size: 35.0 KB
Line 
1
2################################################################################
3#This software was developed by the University of Tennessee as part of the
4#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
5#project funded by the US National Science Foundation.
6#
7#See the license text in license.txt
8#
9#copyright 2009, University of Tennessee
10################################################################################
11
12
13import wx
14import sys
15import os
16from wx.py.editwindow import EditWindow
17
18if sys.platform.count("win32") > 0:
19    FONT_VARIANT = 0
20    PNL_WIDTH = 460
21    PNL_HITE = 210
22else:
23    FONT_VARIANT = 1
24    PNL_WIDTH = 500
25    PNL_HITE = 250
26
27EDITOR_WIDTH = 800
28EDITOR_HEIGTH = 700
29PANEL_WIDTH = 500
30_BOX_WIDTH = 51
31
32   
33def _compileFile(path):
34    """
35    Compile the file in the path
36    """
37    try:
38        import py_compile
39        py_compile.compile(file=path, doraise=True)
40        return ''
41    except:
42        type, value, traceback = sys.exc_info()
43        return value
44   
45def _deleteFile(path):
46    """
47    Delete file in the path
48    """
49    try:
50        os.remove(path)
51    except:
52        raise
53
54 
55class TextDialog(wx.Dialog):
56    """
57    Dialog for easy custom sum models 
58    """
59    def __init__(self, parent=None, id=None, title='', model_list=[]):
60        """
61        Dialog window popup when selecting 'Easy Custom Sum' on the menu
62        """
63        wx.Dialog.__init__(self, parent=parent, id=id, 
64                           title=title, size=(PNL_WIDTH, PNL_HITE))
65        self.parent = parent
66        #Font
67        self.SetWindowVariant(variant=FONT_VARIANT)
68        # default
69        self.model_list = model_list
70        self.model1_string = "SphereModel"
71        self.model2_string = "CylinderModel"
72        self._build_sizer()
73        self.model1_name = str(self.model1.GetValue())
74        self.model2_name = str(self.model2.GetValue())
75       
76    def _build_sizer(self):
77        """
78        Build gui
79        """
80        _BOX_WIDTH = 195 # combobox width
81        vbox  = wx.BoxSizer(wx.VERTICAL)
82        sizer = wx.GridBagSizer(1, 3)
83        sum_description= wx.StaticBox(self, -1, 'Select', 
84                                       size=(PNL_WIDTH-30, 70))
85        sum_box = wx.StaticBoxSizer(sum_description, wx.VERTICAL)
86        model1_box = wx.BoxSizer(wx.HORIZONTAL)
87        model2_box = wx.BoxSizer(wx.HORIZONTAL)
88        model_vbox = wx.BoxSizer(wx.VERTICAL)
89        self.model1 =  wx.ComboBox(self, -1, style=wx.CB_READONLY)
90        wx.EVT_COMBOBOX(self.model1, -1, self.on_model1)
91        self.model1.SetMinSize((_BOX_WIDTH, -1))
92        self.model1.SetToolTipString("model1")
93        self.model2 =  wx.ComboBox(self, -1, style=wx.CB_READONLY)
94        wx.EVT_COMBOBOX(self.model2, -1, self.on_model2)
95        self.model2.SetMinSize((_BOX_WIDTH, -1))
96        self.model2.SetToolTipString("model2")
97        self._set_model_list()
98       
99         # Buttons on the bottom
100        self.static_line_1 = wx.StaticLine(self, -1)
101        self.okButton = wx.Button(self,wx.ID_OK, 'OK', size=(_BOX_WIDTH/2, 25))
102        self.closeButton = wx.Button(self,wx.ID_CANCEL, 'Cancel', 
103                                     size=(_BOX_WIDTH/2, 25))
104        # Intro
105        explanation  = "  custom model = scale_factor * (model1 + model2)\n"
106        explanation  += "  Note: This will overwrite the previous sum model.\n"
107        model_string = " Model%s (p%s):"
108        vbox.Add(sizer)
109        ix = 0
110        iy = 1
111        sizer.Add(wx.StaticText(self, -1, explanation), (iy, ix),
112                 (1, 1), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
113        model1_box.Add(wx.StaticText(self,-1, model_string% (1, 1)), -1, 0)
114        model1_box.Add((_BOX_WIDTH-35,10))
115        model1_box.Add(wx.StaticText(self, -1, model_string% (2, 2)), -1, 0)
116        model2_box.Add(self.model1, -1, 0)
117        model2_box.Add((20,10))
118        model2_box.Add(self.model2, -1, 0)
119        model_vbox.Add(model1_box, -1, 0)
120        model_vbox.Add(model2_box, -1, 0)
121        sum_box.Add(model_vbox, -1, 10)
122        iy += 1
123        ix = 0
124        sizer.Add(sum_box, (iy, ix),
125                  (1, 1), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
126        vbox.Add((10,10))
127        vbox.Add(self.static_line_1, 0, wx.EXPAND, 10)
128        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
129        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
130        sizer_button.Add(self.okButton, 0, 
131                         wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 0)
132        sizer_button.Add(self.closeButton, 0,
133                          wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 15)       
134        vbox.Add(sizer_button, 0, wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
135        self.SetSizer(vbox)
136        self.Centre()
137                 
138    def _set_model_list(self):
139        """
140        Set the list of models
141        """
142        # list of model names
143        list = self.model_list
144        if len(list) > 1:
145            list.sort()
146        for idx in range(len(list)):
147            self.model1.Append(list[idx],idx) 
148            self.model2.Append(list[idx],idx)
149        self.model1.SetStringSelection(self.model1_string)
150        self.model2.SetStringSelection(self.model2_string)
151           
152    def on_model1(self, event):
153        """
154        Set model1
155        """
156        event.Skip()
157        self.model1_name = str(self.model1.GetValue())
158        self.model1_string = self.model1_name
159           
160    def on_model2(self, event):
161        """
162        Set model2
163        """
164        event.Skip()
165        self.model2_name = str(self.model2.GetValue())
166        self.model2_string = self.model2_name
167 
168    def getText(self):
169        """
170        Returns model name string as list
171        """
172        return [self.model1_name, self.model2_name]
173   
174    def write_string(self, fname, name1, name2):
175        """
176        Write and Save file
177        """
178        try:
179            out_f =  open(fname,'w')
180        except :
181            raise
182        lines = SUM_TEMPLATE.split('\n')
183        for line in lines:
184            if line.count("import %s as P1"):
185                out_f.write(line % (name1, name1) + "\n")
186            elif line.count("import %s as P2"):
187                out_f.write(line % (name2, name2) + "\n")
188            else:
189                out_f.write(line + "\n")
190        out_f.close() 
191       
192    def compile_file(self, path):
193        """
194        Compile the file in the path
195        """
196        _compileFile(path)
197       
198    def delete_file(self, path):
199        """
200        Delete file in the path
201        """
202        _deleteFile(path)
203
204
205class EditorPanel(wx.ScrolledWindow):
206    """
207    Custom model function editor
208    """
209    def __init__(self, parent, base, path, title, *args, **kwds):
210        kwds['name'] = title
211        kwds["size"] = (EDITOR_WIDTH, EDITOR_HEIGTH)
212        kwds["style"]= wx.FULL_REPAINT_ON_RESIZE
213        wx.ScrolledWindow.__init__(self, parent, *args, **kwds)
214        #self.SetupScrolling()
215        self.parent = parent
216        self.base = base
217        self.path = path
218        self.font = wx.SystemSettings_GetFont(wx.SYS_SYSTEM_FONT)
219        self.font.SetPointSize(10)
220        self.reader = None
221        self.name = 'untitled'
222        self.overwrite_name = True
223        self.is_2d = False
224        self.fname = None
225        self.param_strings = ''
226        self.function_strings = ''
227        self._notes = ""
228        self.warning = ""
229        self._description = "New Custom Model"
230        #self._default_save_location = os.getcwd()
231        self._do_layout()
232        #self.bt_apply.Disable()
233
234             
235    def _define_structure(self):
236        """
237        define initial sizer
238        """
239        #w, h = self.parent.GetSize()
240        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
241        self.name_sizer = wx.BoxSizer(wx.VERTICAL)
242        self.name_hsizer = wx.BoxSizer(wx.HORIZONTAL)
243        self.desc_sizer = wx.BoxSizer(wx.VERTICAL)
244        self.param_sizer = wx.BoxSizer(wx.VERTICAL)
245        self.function_sizer = wx.BoxSizer(wx.VERTICAL)
246        self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
247       
248    def _layout_name(self):
249        """
250        Do the layout for file/function name related widgets
251        """
252        #title name [string]
253        name_txt = wx.StaticText(self, -1, 'Function Name : ') 
254        overwrite_cb = wx.CheckBox(self, -1, "Overwrite?", (10, 10))
255        overwrite_cb.SetValue(True)
256        overwrite_cb.SetToolTipString("Overwrite it if already exists?")
257        wx.EVT_CHECKBOX(self, overwrite_cb.GetId(), self.on_over_cb)
258        self.name_tcl = wx.TextCtrl(self, -1, size=(PANEL_WIDTH*3/5, -1)) 
259        self.name_tcl.Bind(wx.EVT_TEXT_ENTER, self.on_change_name)
260        self.name_tcl.SetValue('MyFunction')
261        self.name_tcl.SetFont(self.font)
262        hint_name = "Unique Model Function Name."
263        self.name_tcl.SetToolTipString(hint_name)
264        self.name_hsizer.AddMany([(self.name_tcl, 0, wx.LEFT|wx.TOP, 0),
265                                       (overwrite_cb, 0, wx.LEFT, 20)])
266        self.name_sizer.AddMany([(name_txt, 0, wx.LEFT|wx.TOP, 10),
267                                       (self.name_hsizer, 0, 
268                                        wx.LEFT|wx.TOP|wx.BOTTOM, 10)])
269       
270       
271    def _layout_description(self):
272        """
273        Do the layout for description related widgets
274        """
275        #title name [string]
276        desc_txt = wx.StaticText(self, -1, 'Description (optional) : ') 
277        self.desc_tcl = wx.TextCtrl(self, -1, size=(PANEL_WIDTH*3/5, -1)) 
278        self.desc_tcl.SetValue('')
279        #self.name_tcl.SetFont(self.font)
280        hint_desc = "Write a short description of the model function."
281        self.desc_tcl.SetToolTipString(hint_desc)
282        self.desc_sizer.AddMany([(desc_txt, 0, wx.LEFT|wx.TOP, 10),
283                                       (self.desc_tcl, 0, 
284                                        wx.LEFT|wx.TOP|wx.BOTTOM, 10)])     
285    def _layout_param(self):
286        """
287        Do the layout for parameter related widgets
288        """
289        param_txt = wx.StaticText(self, -1, 'Fit Parameters (if any): ') 
290        param_tip = "#Set the parameters and initial values.\n"
291        param_tip += "#Example:\n"
292        param_tip += "A = 1\nB = 1"
293        #param_txt.SetToolTipString(param_tip)
294        id  = wx.NewId() 
295        self.param_tcl = EditWindow(self, id, wx.DefaultPosition, 
296                            wx.DefaultSize, wx.CLIP_CHILDREN|wx.SUNKEN_BORDER)
297        self.param_tcl.setDisplayLineNumbers(True)
298        self.param_tcl.SetToolTipString(param_tip)
299        self.param_sizer.AddMany([(param_txt, 0, wx.LEFT, 10),
300                        (self.param_tcl, 1, wx.EXPAND|wx.ALL, 10)])
301
302   
303    def _layout_function(self):
304        """
305        Do the layout for function related widgets
306        """
307        function_txt = wx.StaticText(self, -1, 'Function(x) : ') 
308        hint_function = "#Example:\n"
309        hint_function += "if x <= 0:\n"
310        hint_function += "    y = A + B\n"
311        hint_function += "else:\n"
312        hint_function += "    y = A + B * cos(2 * pi * x)\n"
313        hint_function += "return y\n"
314        id  = wx.NewId() 
315        self.function_tcl = EditWindow(self, id, wx.DefaultPosition, 
316                            wx.DefaultSize, wx.CLIP_CHILDREN|wx.SUNKEN_BORDER)
317        self.function_tcl.setDisplayLineNumbers(True)
318        self.function_tcl.SetToolTipString(hint_function)
319        self.function_sizer.Add(function_txt, 0, wx.LEFT, 10)
320        self.function_sizer.Add( self.function_tcl, 1, wx.EXPAND|wx.ALL, 10)
321
322                   
323    def _layout_button(self): 
324        """
325        Do the layout for the button widgets
326        """         
327        self.bt_apply = wx.Button(self, -1, "Apply", size=(_BOX_WIDTH, -1))
328        self.bt_apply.SetToolTipString("Save changes into the imported data.")
329        self.bt_apply.Bind(wx.EVT_BUTTON, self.on_click_apply)
330       
331        self.bt_close = wx.Button(self, -1, 'Close', size=(_BOX_WIDTH, -1))
332        self.bt_close.Bind(wx.EVT_BUTTON, self.on_close)
333        self.bt_close.SetToolTipString("Close this panel.")
334       
335        self.button_sizer.AddMany([(self.bt_apply, 0, 
336                                    wx.LEFT, EDITOR_WIDTH * 0.8),
337                                   (self.bt_close, 0, 
338                                    wx.LEFT|wx.BOTTOM, 15)])
339       
340    def _do_layout(self):
341        """
342        Draw the current panel
343        """
344        self._define_structure()
345        self._layout_name()
346        self._layout_description()
347        self._layout_param()
348        self._layout_function()
349        self._layout_button()
350        self.main_sizer.AddMany([(self.name_sizer, 0, 
351                                        wx.EXPAND|wx.ALL, 5),
352                                 (wx.StaticLine(self), 0, 
353                                       wx.ALL|wx.EXPAND, 5),
354                                 (self.desc_sizer, 0, 
355                                        wx.EXPAND|wx.ALL, 5),
356                                 (wx.StaticLine(self), 0, 
357                                       wx.ALL|wx.EXPAND, 5),
358                                (self.param_sizer, 0,
359                                         wx.EXPAND|wx.ALL, 5),
360                                 (wx.StaticLine(self), 0, 
361                                       wx.ALL|wx.EXPAND, 5),
362                                (self.function_sizer, 1,
363                                         wx.EXPAND|wx.ALL, 5),
364                                 (wx.StaticLine(self), 0, 
365                                       wx.ALL|wx.EXPAND, 5),
366                                (self.button_sizer, 0,
367                                         wx.EXPAND|wx.ALL, 5)])
368        self.SetSizer(self.main_sizer)
369        self.SetAutoLayout(True)
370   
371    def get_notes(self):
372        """
373        return notes
374        """
375        return self._notes
376                 
377    def on_change_name(self, event=None):
378        """
379        Change name
380        """
381        if event is not None:
382            event.Skip()
383        self.name_tcl.SetBackgroundColour('white')
384        self.Refresh()
385   
386    def check_name(self):
387        """
388        Check name if exist already
389        """
390        self.on_change_name(None)
391        plugin_dir = self.path
392        list_fnames = os.listdir(plugin_dir)
393        # function/file name
394        title = self.name_tcl.GetValue().lstrip().rstrip()
395        self.name = title
396        t_fname = title + '.py'
397        if not self.overwrite_name:
398            if t_fname in list_fnames:
399                self.name_tcl.SetBackgroundColour('pink')
400                return False
401        self.fname = os.path.join(plugin_dir, t_fname)
402        self._notes += "Model function name set "
403        self._notes += "to %s. \n" % str(title)
404        return True
405   
406    def on_over_cb(self, event):
407        """
408        Set overwrite name flag on cb event
409        """
410        if event is not None:
411            event.Skip()
412        cb = event.GetEventObject()
413        self.overwrite_name = cb.GetValue()
414       
415    def on_click_apply(self, event):
416        """   
417        Changes are saved in data object imported to edit
418        """
419        #must post event here
420        event.Skip()
421        info = 'Info'
422        # Sort out the errors if occur
423        if self.check_name():
424            name = self.name_tcl.GetValue().lstrip().rstrip()
425            description = self.desc_tcl.GetValue()
426            param_str = self.param_tcl.GetText()
427            func_str = self.function_tcl.GetText()
428            # No input for the model function
429            if func_str.lstrip().rstrip():
430                self.write_file(self.fname, description, param_str, func_str)
431                tr_msg = _compileFile(self.fname)
432                msg = tr_msg.__str__()
433                # Compile error
434                if msg:
435                    _deleteFile(self.fname)
436                    msg +=  "\nCompile Failed"
437                else:
438                    msg = ''
439            else:
440                msg = 'Error: Function is not defined.'
441        else:
442            msg = "Name exists already."
443        # Prepare for the messagebox
444        if not msg:
445            if self.base != None:
446                self.base.update_custom_combo()
447            msg = "Successful!!!"
448            info = 'Info'
449        else:
450            info = 'Error'
451            wx.MessageBox(msg, info) 
452        # Send msg to the top window 
453        if self.base != None:
454                from sans.guiframe.events import StatusEvent
455                wx.PostEvent(self.base.parent, StatusEvent(status = msg, 
456                                                      info=info))
457        self.warning = msg
458
459               
460    def write_file(self, fname, desc_str, param_str, func_str): 
461        """
462        Write content in file
463       
464        :param fname: full file path
465        :param desc_str: content of the description strings
466        :param param_str: content of params; Strings 
467        :param func_str: content of func; Strings
468        """ 
469        try:
470            out_f =  open(fname,'w')
471        except :
472            raise
473        # Prepare the content of the function
474        lines = CUSTOM_TEMPLATE.split('\n')
475
476        has_scipy = func_str.count("scipy.")
477        self.is_2d = func_str.count("#self.ndim = 2")
478        line_2d = ''
479        if self.is_2d:
480            line_2d = CUSTOM_2D_TEMP.split('\n')
481        line_test = TEST_TEMPLATE.split('\n')
482        local_params = ''
483        spaces = '        '#8spaces
484        # write function here
485        for line in lines:
486            # The location where to put the strings is
487            # hard-coded in the template as shown below.
488            if line.count("#self.params here"):
489                for param_line in param_str.split('\n'):
490                    p_line = param_line.lstrip().rstrip()
491                    if p_line:
492                        p0_line = self.set_param_helper(p_line)
493                        local_params += self.set_function_helper(p_line)
494                        out_f.write(p0_line)
495            elif line.count("#local params here"):
496                if local_params:
497                    out_f.write(local_params)
498            elif line.count("self.description = "):
499                des0 = self.name + "\\n"
500                desc = str(desc_str.lstrip().rstrip().replace('\"', ''))
501                out_f.write(line% (des0 + desc) + "\n")
502            elif line.count("def function(self, x=0.0%s):"):
503                if self.is_2d:
504                    y_str = ', y=0.0'
505                    out_f.write(line% y_str + "\n")
506                else:
507                    out_f.write(line% '' + "\n")
508            elif line.count("#function here"):
509                for func_line in func_str.split('\n'):
510                    f_line = func_line.rstrip()
511                    if f_line:
512                        out_f.write(spaces + f_line + "\n")
513                if not func_str:
514                    dep_var = 'y'
515                    if self.is_2d:
516                        dep_var = 'z'
517                    out_f.write(spaces + 'return %s'% dep_var + "\n")
518            elif line.count("#import scipy?"):
519                if has_scipy:
520                    out_f.write("import scipy" + "\n")
521            elif line.count("name = "):
522                out_f.write(line % self.name + "\n")
523            elif line:
524                out_f.write(line + "\n")
525        # run string for 2d
526        if line_2d:
527            for line in line_2d:
528                out_f.write(line + "\n")
529        # Test strins
530        for line in line_test:
531            out_f.write(line + "\n")
532   
533        out_f.close() 
534   
535    def set_param_helper(self, line):   
536        """
537        Get string in line to define the params dictionary
538       
539        :param line: one line of string got from the param_str
540        """
541        flag = True
542        params_str = ''
543        spaces = '        '#8spaces
544        items = line.split(";")
545        for item in items:
546            name = item.split("=")[0].lstrip().rstrip()
547            try:
548                value = item.split("=")[1].lstrip().rstrip()
549                float(value)
550            except:
551                value = 1.0 # default
552            params_str += spaces + "self.params['%s'] = %s\n"% (name, value)
553           
554        return params_str
555
556    def set_function_helper(self, line):   
557        """
558        Get string in line to define the local params
559       
560        :param line: one line of string got from the param_str
561        """
562        flag = True
563        params_str = ''
564        spaces = '        '#8spaces
565        items = line.split(";")
566        for item in items:
567            name = item.split("=")[0].lstrip().rstrip()
568            params_str += spaces + "%s = self.params['%s']\n"% (name, name)
569        return params_str
570   
571    def get_warning(self):
572        """
573        Get the warning msg
574        """
575        return self.warning
576       
577    def on_close(self, event):
578        """
579        leave data as it is and close
580        """
581        self.parent.Close()
582        event.Skip()
583       
584class EditorWindow(wx.Frame):
585    def __init__(self, parent, base, path, title, 
586                 size=(EDITOR_WIDTH, EDITOR_HEIGTH), *args, **kwds):
587        kwds["title"] = title
588        kwds["size"] = size
589        wx.Frame.__init__(self, parent=None, *args, **kwds)
590        self.parent = parent
591        self.panel = EditorPanel(parent=self, base=parent, 
592                                 path=path, title=title)
593        self.Show(True)
594        wx.EVT_CLOSE(self, self.OnClose)
595   
596    def OnClose(self, event): 
597        """
598        On close event
599        """
600        if self.parent != None:
601            self.parent.new_model_frame = None
602        self.Destroy() 
603
604## Templates for custom models
605CUSTOM_TEMPLATE = """
606from sans.models.pluginmodel import Model1DPlugin
607from math import *
608import numpy
609#import scipy?
610class Model(Model1DPlugin):
611    name = "%s"                             
612    def __init__(self):
613        Model1DPlugin.__init__(self, name=self.name)                                                     
614        #self.params here
615        self.description = "%s"
616        self.set_details()
617    def function(self, x=0.0%s):
618        #local params here
619        #function here
620"""
621CUSTOM_2D_TEMP = """
622    def run(self, x=0.0, y=0.0):
623        if x.__class__.__name__ == 'list':
624            x_val = x[0]
625            y_val = y[0]*0.0
626            return self.function(x_val, y_val)
627        elif x.__class__.__name__ == 'tuple':
628            msg = "Tuples are not allowed as input to BaseComponent models"
629            raise ValueError, msg
630        else:
631            return self.function(x, 0.0)
632    def runXY(self, x=0.0, y=0.0):
633        if x.__class__.__name__ == 'list':
634            return self.function(x, y)
635        elif x.__class__.__name__ == 'tuple':
636            msg = "Tuples are not allowed as input to BaseComponent models"
637            raise ValueError, msg
638        else:
639            return self.function(x, y)
640    def evalDistribution(self, qdist):
641        if qdist.__class__.__name__ == 'list':
642            msg = "evalDistribution expects a list of 2 ndarrays"
643            if len(qdist)!=2:
644                raise RuntimeError, msg
645            if qdist[0].__class__.__name__ != 'ndarray':
646                raise RuntimeError, msg
647            if qdist[1].__class__.__name__ != 'ndarray':
648                raise RuntimeError, msg
649            v_model = numpy.vectorize(self.runXY, otypes=[float])
650            iq_array = v_model(qdist[0], qdist[1])
651            return iq_array
652        elif qdist.__class__.__name__ == 'ndarray':
653            v_model = numpy.vectorize(self.runXY, otypes=[float])
654            iq_array = v_model(qdist)
655            return iq_array
656"""
657TEST_TEMPLATE = """
658######################################################################
659## THIS IS FOR TEST. DO NOT MODIFY THE FOLLOWING LINES!!!!!!!!!!!!!!!!       
660if __name__ == "__main__":
661    m= Model()
662    out1 = m.runXY(0.0)
663    out2 = m.runXY(0.01)
664    isfine1 = numpy.isfinite(out1)
665    isfine2 = numpy.isfinite(out2)
666    print "Testing the value at Q = 0.0:"
667    print out1, " : finite? ", isfine1
668    print "Testing the value at Q = 0.01:"
669    print out2, " : finite? ", isfine2
670    if isfine1 and isfine2:
671        print "===> Simple Test: Passed!"
672    else:
673        print "===> Simple Test: Failed!"
674"""
675SUM_TEMPLATE = """
676# A sample of an experimental model function for Sum(Pmodel1,Pmodel2)
677import copy
678from sans.models.pluginmodel import Model1DPlugin
679# User can change the name of the model (only with single functional model)
680#P1_model:
681from sans.models.%s import %s as P1
682
683#P2_model:
684from sans.models.%s import %s as P2
685
686class Model(Model1DPlugin):
687    name = ""
688    def __init__(self):
689        Model1DPlugin.__init__(self, name='')
690        p_model1 = P1()
691        p_model2 = P2()
692        ## Setting  model name model description
693        self.description=""
694        self.name = self._get_name(p_model1.name, p_model2.name)
695        self.description = p_model1.name
696        self.description += p_model2.name
697        self.fill_description(p_model1, p_model2)
698
699        ## Define parameters
700        self.params = {}
701
702        ## Parameter details [units, min, max]
703        self.details = {}
704       
705        # non-fittable parameters
706        self.non_fittable = p_model1.non_fittable 
707        self.non_fittable += p_model2.non_fittable 
708           
709        ##models
710        self.p_model1= p_model1
711        self.p_model2= p_model2
712       
713       
714        ## dispersion
715        self._set_dispersion()
716        ## Define parameters
717        self._set_params()
718        ## New parameter:Scaling factor
719        self.params['scale_factor'] = 1
720       
721        ## Parameter details [units, min, max]
722        self._set_details()
723        self.details['scale_factor'] = ['', None, None]
724
725       
726        #list of parameter that can be fitted
727        self._set_fixed_params() 
728        ## parameters with orientation
729        for item in self.p_model1.orientation_params:
730            new_item = "p1_" + item
731            if not new_item in self.orientation_params:
732                self.orientation_params.append(new_item)
733           
734        for item in self.p_model2.orientation_params:
735            new_item = "p2_" + item
736            if not new_item in self.orientation_params:
737                self.orientation_params.append(new_item)
738        # get multiplicity if model provide it, else 1.
739        try:
740            multiplicity1 = p_model1.multiplicity
741            try:
742                multiplicity2 = p_model2.multiplicity
743            except:
744                multiplicity2 = 1
745        except:
746            multiplicity1 = 1
747            multiplicity2 = 1
748        ## functional multiplicity of the model
749        self.multiplicity1 = multiplicity1 
750        self.multiplicity2 = multiplicity2   
751        self.multiplicity_info = []   
752       
753    def _clone(self, obj):
754        obj.params     = copy.deepcopy(self.params)
755        obj.description     = copy.deepcopy(self.description)
756        obj.details    = copy.deepcopy(self.details)
757        obj.dispersion = copy.deepcopy(self.dispersion)
758        obj.p_model1  = self.p_model1.clone()
759        obj.p_model2  = self.p_model2.clone()
760        #obj = copy.deepcopy(self)
761        return obj
762   
763    def _get_name(self, name1, name2):
764        p1_name = self._get_upper_name(name1)
765        if not p1_name:
766            p1_name = name1
767        name = p1_name
768        name += "+"
769        p2_name = self._get_upper_name(name2)
770        if not p2_name:
771            p2_name = name2
772        name += p2_name
773        return name
774   
775    def _get_upper_name(self, name=None):
776        if name == None:
777            return ""
778        upper_name = ""
779        str_name = str(name)
780        for index in range(len(str_name)):
781            if str_name[index].isupper():
782                upper_name += str_name[index]
783        return upper_name
784       
785    def _set_dispersion(self):
786        ##set dispersion only from p_model
787        for name , value in self.p_model1.dispersion.iteritems():
788            #if name.lower() not in self.p_model1.orientation_params:
789            new_name = "p1_" + name
790            self.dispersion[new_name]= value
791        for name , value in self.p_model2.dispersion.iteritems():
792            #if name.lower() not in self.p_model2.orientation_params:
793            new_name = "p2_" + name
794            self.dispersion[new_name]= value
795           
796    def function(self, x=0.0):
797        return 0
798                               
799    def getProfile(self):
800        try:
801            x,y = self.p_model1.getProfile()
802        except:
803            x = None
804            y = None
805           
806        return x, y
807   
808    def _set_params(self):
809        for name , value in self.p_model1.params.iteritems():
810            # No 2D-supported
811            #if name not in self.p_model1.orientation_params:
812            new_name = "p1_" + name
813            self.params[new_name]= value
814           
815        for name , value in self.p_model2.params.iteritems():
816            # No 2D-supported
817            #if name not in self.p_model2.orientation_params:
818            new_name = "p2_" + name
819            self.params[new_name]= value
820               
821        # Set "scale" as initializing
822        self._set_scale_factor()
823     
824           
825    def _set_details(self):
826        for name ,detail in self.p_model1.details.iteritems():
827            new_name = "p1_" + name
828            #if new_name not in self.orientation_params:
829            self.details[new_name]= detail
830           
831        for name ,detail in self.p_model2.details.iteritems():
832            new_name = "p2_" + name
833            #if new_name not in self.orientation_params:
834            self.details[new_name]= detail
835   
836    def _set_scale_factor(self):
837        pass
838       
839               
840    def setParam(self, name, value):
841        # set param to p1+p2 model
842        self._setParamHelper(name, value)
843       
844        ## setParam to p model
845        model_pre = name.split('_', 1)[0]
846        new_name = name.split('_', 1)[1]
847        if model_pre == "p1":
848            if new_name in self.p_model1.getParamList():
849                self.p_model1.setParam(new_name, value)
850        elif model_pre == "p2":
851             if new_name in self.p_model2.getParamList():
852                self.p_model2.setParam(new_name, value)
853        elif name.lower() == 'scale_factor':
854            self.params['scale_factor'] = value
855        else:
856            raise ValueError, "Model does not contain parameter %s" % name
857           
858    def getParam(self, name):
859        # Look for dispersion parameters
860        toks = name.split('.')
861        if len(toks)==2:
862            for item in self.dispersion.keys():
863                # 2D not supported
864                if item.lower()==toks[0].lower():
865                    for par in self.dispersion[item]:
866                        if par.lower() == toks[1].lower():
867                            return self.dispersion[item][par]
868        else:
869            # Look for standard parameter
870            for item in self.params.keys():
871                if item.lower()==name.lower():
872                    return self.params[item]
873        return 
874        #raise ValueError, "Model does not contain parameter %s" % name
875       
876    def _setParamHelper(self, name, value):
877        # Look for dispersion parameters
878        toks = name.split('.')
879        if len(toks)== 2:
880            for item in self.dispersion.keys():
881                if item.lower()== toks[0].lower():
882                    for par in self.dispersion[item]:
883                        if par.lower() == toks[1].lower():
884                            self.dispersion[item][par] = value
885                            return
886        else:
887            # Look for standard parameter
888            for item in self.params.keys():
889                if item.lower()== name.lower():
890                    self.params[item] = value
891                    return
892           
893        raise ValueError, "Model does not contain parameter %s" % name
894             
895   
896    def _set_fixed_params(self):
897        for item in self.p_model1.fixed:
898            new_item = "p1" + item
899            self.fixed.append(new_item)
900        for item in self.p_model2.fixed:
901            new_item = "p2" + item
902            self.fixed.append(new_item)
903
904        self.fixed.sort()
905               
906                   
907    def run(self, x = 0.0):
908        self._set_scale_factor()
909        return self.params['scale_factor'] * \
910(self.p_model1.run(x) + self.p_model2.run(x))
911   
912    def runXY(self, x = 0.0):
913        self._set_scale_factor()
914        return self.params['scale_factor'] * \
915(self.p_model1.runXY(x) + self.p_model2.runXY(x))
916   
917    ## Now (May27,10) directly uses the model eval function
918    ## instead of the for-loop in Base Component.
919    def evalDistribution(self, x = []):
920        self._set_scale_factor()
921        return self.params['scale_factor'] * \
922(self.p_model1.evalDistribution(x) + \
923self.p_model2.evalDistribution(x))
924
925    def set_dispersion(self, parameter, dispersion):
926        value= None
927        new_pre = parameter.split("_", 1)[0]
928        new_parameter = parameter.split("_", 1)[1]
929        try:
930            if new_pre == 'p1' and \
931new_parameter in self.p_model1.dispersion.keys():
932                value= self.p_model1.set_dispersion(new_parameter, dispersion)
933            if new_pre == 'p2' and \
934new_parameter in self.p_model2.dispersion.keys():
935                value= self.p_model2.set_dispersion(new_parameter, dispersion)
936            self._set_dispersion()
937            return value
938        except:
939            raise
940
941    def fill_description(self, p_model1, p_model2):
942        description = ""
943        description +="This model gives the summation of  %s and %s. "% \
944( p_model1.name, p_model2.name )
945        self.description += description
946       
947if __name__ == "__main__":
948    m1= Model()
949    #m1.setParam("p1_scale", 25) 
950    #m1.setParam("p1_length", 1000)
951    #m1.setParam("p2_scale", 100)
952    #m1.setParam("p2_rg", 100)
953    out1 = m1.runXY(0.01)
954
955    m2= Model()
956    #m2.p_model1.setParam("scale", 25)
957    #m2.p_model1.setParam("length", 1000)
958    #m2.p_model2.setParam("scale", 100)
959    #m2.p_model2.setParam("rg", 100)
960    out2 = m2.p_model1.runXY(0.01) + m2.p_model2.runXY(0.01)
961    print out1, " = ", out2
962    if out1 == out2:
963        print "===> Simple Test: Passed!"
964    else:
965        print "===> Simple Test: Failed!"
966"""
967     
968#if __name__ == "__main__":
969#    app = wx.PySimpleApp()
970#    frame = TextDialog(id=1, model_list=["SphereModel", "CylinderModel"])   
971#    frame.Show(True)
972#    app.MainLoop()             
973
974if __name__ == "__main__":
975    from sans.perspectives.fitting import models
976    dir_path = models.find_plugins_dir()
977    app  = wx.App()
978    window = EditorWindow(parent=None, base=None, path=dir_path, title="Editor")
979    app.MainLoop()         
Note: See TracBrowser for help on using the repository browser.