source: sasview/src/sas/perspectives/calculator/model_editor.py @ 7b1f4e3

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 7b1f4e3 was 7b1f4e3, checked in by butler, 5 years ago

Major Refactoring and clean up of the TextDiaolg? in model_editor.py.
This fixes the completely broken custom composite model editor (ticket
#401), significantly simplifies and improves readability of the code and
adds a lot of documentation in the form of both doc strings and in line
comments. Still could use more cleanup.

  • Property mode set to 100644
File size: 53.4 KB
Line 
1'''
2This module provides three model editor classes: the composite model editor,
3the easy editor which provides a simple interface with tooltip help to enter
4the parameters of the model and their default value and a panel to input a
5function of y (usually the intensity).  It also provides a drop down of
6standard available math functions.  Finally a full python editor panel for
7complete customizatin is provided.
8
9:TODO the writiong of the file and name checking (and maybe some other
10funtions?) should be moved to a computational module which could be called
11fropm a python script.  Basically one just needs to pass the name,
12description text and function text (or in the case of the composite editor
13the names of the first and second model and the operator to be used).
14'''
15
16################################################################################
17#This software was developed by the University of Tennessee as part of the
18#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
19#project funded by the US National Science Foundation.
20#
21#See the license text in license.txt
22#
23#copyright 2009, University of Tennessee
24################################################################################
25import wx
26import sys
27import os
28import math
29import re
30from wx.py.editwindow import EditWindow
31
32if sys.platform.count("win32") > 0:
33    FONT_VARIANT = 0
34    PNL_WIDTH = 450
35    PNL_HITE = 320
36else:
37    FONT_VARIANT = 1
38    PNL_WIDTH = 590
39    PNL_HITE = 350
40M_NAME = 'Model'
41EDITOR_WIDTH = 800
42EDITOR_HEIGTH = 735
43PANEL_WIDTH = 500
44_BOX_WIDTH = 55
45
46
47def _compileFile(path):
48    """
49    Compile the file in the path
50    """
51    try:
52        import py_compile
53        py_compile.compile(file=path, doraise=True)
54        return ''
55    except:
56        _, value, _ = sys.exc_info()
57        return value
58
59def _deleteFile(path):
60    """
61    Delete file in the path
62    """
63    try:
64        os.remove(path)
65    except:
66        raise
67
68
69class TextDialog(wx.Dialog):
70    """
71    Dialog for easy custom composite models.  Provides a wx.Dialog panel
72    to choose two existing models (including pre-existing custom models which
73    may themselves be sum models) as well as an operation on those models (add
74    or multiply) the resulting model will add a scale parameter for summed
75    models and a background parameter for a multiplied model.
76   
77    The user also gives a brief help for the model in a description box and
78    must provide a unique name which is verified as unique before the new
79    model is saved.
80   
81    This Dialog pops up for the user when they press 'Sum|Multi(p1,p2)' under
82    'Edit Custom Model' under 'Fitting' menu.  This is currently called as
83    a Modal Dialog.
84   
85    :TODO the build in compiler currently balks at when it tries to import
86    a model whose name contains spaces or symbols (such as + ... underscore
87    should be fine) need to either alter how the model file is written on add
88    checking method to look for non-approved characters.  - PDB:April 5, 2015
89    """
90    def __init__(self, parent=None, base=None, id=None, title='',
91                 model_list=[], plugin_dir=None):
92        """
93        This class is run when instatiated.  The __init__ intializes and
94        calls the internal methods necessary.  On exiting the wx.Dialog
95        window should be destroyed.
96
97        """
98        wx.Dialog.__init__(self, parent=parent, id=id,
99                           title=title, size=(PNL_WIDTH, PNL_HITE))
100        self.parent = base
101        #Font
102        self.SetWindowVariant(variant=FONT_VARIANT)
103        # default
104        self.overwrite_name = False
105        self.plugin_dir = plugin_dir
106        self.model_list = model_list
107        self.model1_string = "SphereModel"
108        self.model2_string = "CylinderModel"
109        self.name = 'Sum' + M_NAME
110        self.factor = 'scale_factor'
111        self._notes = ''
112        self._operator = '+'
113        self._operator_choice = None
114        self.explanation = ''
115        self.explanationctr = None
116        self.type = None
117        self.name_sizer = None
118        self.name_tcl = None
119        self.desc_sizer = None
120        self.desc_tcl = None
121        self._selection_box= None
122        self.model1 = None
123        self.model2 = None
124        self.static_line_1 = None
125        self.okButton = None
126        self.closeButton = None
127        self._msg_box = None
128        self.msg_sizer = None
129        self.fname = None
130        self.cm_list = None
131        self.is_p1_custom = False
132        self.is_p2_custom = False
133        self._build_sizer()
134        self.model1_name = str(self.model1.GetValue())
135        self.model2_name = str(self.model2.GetValue())
136        self.good_name = True
137        self.fill_oprator_combox()
138
139    def _layout_name(self):
140        """
141        Do the layout for file/function name related widgets
142        """
143        #container for new model name input
144        self.name_sizer = wx.BoxSizer(wx.HORIZONTAL)
145
146        #set up label and input box with tool tip and event handling
147        self.name_txt = wx.StaticText(self, -1, 'Function Name : ')
148        self.name_tcl = wx.TextCtrl(self, -1, value = 'MySumFunction')
149        self.name_tcl.Bind(wx.EVT_TEXT_ENTER, self.on_change_name)
150        hint_name = "Unique Sum/Multiply Model Function Name."
151        self.name_tcl.SetToolTipString(hint_name)
152
153        self.name_sizer.AddMany([(self.name_txt, 0, wx.LEFT | wx.TOP, 10),
154                            (self.name_tcl, -1,
155                             wx.EXPAND | wx.RIGHT | wx.TOP | wx.BOTTOM, 10)])
156
157
158    def _layout_description(self):
159        """
160        Do the layout for description related widgets
161        """
162        #container for new model description input
163        self.desc_sizer = wx.BoxSizer(wx.HORIZONTAL)
164
165        #set up description label and input box with tool tip and event handling
166        desc_txt = wx.StaticText(self, -1, 'Description (optional) : ')
167        self.desc_tcl = wx.TextCtrl(self, -1)
168        hint_desc = "Write a short description of this model function."
169        self.desc_tcl.SetToolTipString(hint_desc)
170
171        self.desc_sizer.AddMany([(desc_txt, 0, wx.LEFT | wx.TOP, 10),
172                                (self.desc_tcl, -1,
173                                wx.EXPAND | wx.RIGHT | wx.TOP | wx.BOTTOM, 10)])
174
175
176    def _layout_model_selection(self):
177        """
178        Do the layout for model selection related widgets
179        """
180        box_width = 195 # combobox width
181
182        #First set up main sizer for the selection
183        selection_box_title = wx.StaticBox(self, -1, 'Select',
184                                       size=(PNL_WIDTH - 30, 70))
185        self._selection_box = wx.StaticBoxSizer(selection_box_title, wx.VERTICAL)
186
187        #Next create the help labels for the model selection
188        select_help_box = wx.BoxSizer(wx.HORIZONTAL)
189        model_string = " Model%s (p%s):"
190        select_help_box.Add(wx.StaticText(self, -1, model_string % (1, 1)), 0, 0)
191        select_help_box.Add((box_width - 25, 10),0,0)
192        select_help_box.Add(wx.StaticText(self, -1, model_string % (2, 2)), 0, 0)
193        self._selection_box.Add(select_help_box, 0, 0)
194
195        #Next create the actual selection box with 3 combo boxes
196        selection_box_choose = wx.BoxSizer(wx.HORIZONTAL)
197
198        self.model1 = wx.ComboBox(self, -1, style=wx.CB_READONLY)
199        wx.EVT_COMBOBOX(self.model1, -1, self.on_model1)
200        self.model1.SetMinSize((box_width * 5 / 6, -1))
201        self.model1.SetToolTipString("model1")
202
203        self._operator_choice = wx.ComboBox(self, -1, size=(50, -1), style=wx.CB_READONLY)
204        wx.EVT_COMBOBOX(self._operator_choice, -1, self.on_select_operator)
205        operation_tip = "Add: +, Multiply: * "
206        self._operator_choice.SetToolTipString(operation_tip)
207
208        self.model2 = wx.ComboBox(self, -1, style=wx.CB_READONLY)
209        wx.EVT_COMBOBOX(self.model2, -1, self.on_model2)
210        self.model2.SetMinSize((box_width * 5 / 6, -1))
211        self.model2.SetToolTipString("model2")
212        self._set_model_list()
213
214        selection_box_choose.Add(self.model1, 0, 0)
215        selection_box_choose.Add((15, 10))
216        selection_box_choose.Add(self._operator_choice, 0, 0)
217        selection_box_choose.Add((15, 10))
218        selection_box_choose.Add(self.model2, 0, 0)
219        self._selection_box.Add((20,5), 0, 0) # add some space between labels and selection
220        self._selection_box.Add(selection_box_choose, 0, 0)
221
222    def _build_sizer(self):
223        """
224        Build GUI with calls to _layout_name, _layout Description
225        and _layout_model_selection which each build a their portion of the
226        GUI.
227        """
228        mainsizer = wx.BoxSizer(wx.VERTICAL) # create main sizer for dialog
229
230        # build fromm top by calling _layout_name and _layout_description
231        # and adding to main sizer
232        self._layout_name()
233        mainsizer.Add(self.name_sizer,0, wx.EXPAND)
234        self._layout_description()
235        mainsizer.Add(self.desc_sizer,0, wx.EXPAND)
236
237        # Add an explanation of dialog (short help)
238        self.explanationctr = wx.StaticText(self, -1, self.explanation)
239        self.fill_explanation_helpstring(self._operator)
240        mainsizer.Add(self.explanationctr,0, wx.LEFT | wx.EXPAND, 15)
241
242        # Add the selection box stuff with border and labels built
243        # by _layout_model_selection
244        self._layout_model_selection()
245        mainsizer.Add(self._selection_box, 0, wx.LEFT, 15)
246
247        # Add a space and horizontal line before the notification
248        #messages and the buttons at the bottom
249        mainsizer.Add((10, 10))
250        self.static_line_1 = wx.StaticLine(self, -1)
251        mainsizer.Add(self.static_line_1, 0, wx.EXPAND, 10)
252
253        # Add action status notification line (null at startup)
254        self._msg_box = wx.StaticText(self, -1, self._notes)
255        self.msg_sizer = wx.BoxSizer(wx.HORIZONTAL)
256        self.msg_sizer.Add(self._msg_box, 0, wx.LEFT, 0)
257        mainsizer.Add(self.msg_sizer, 0,
258                 wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE | wx.BOTTOM, 10)
259
260        # Finally add the buttons (apply and close) on the bottom
261        # TODO: need help added here
262        self.okButton = wx.Button(self, wx.ID_OK, 'Apply')
263        self.okButton.Bind(wx.EVT_BUTTON, self.check_name)
264        self.closeButton = wx.Button(self, wx.ID_CANCEL, 'Close')
265        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
266        sizer_button.AddMany([((20, 20), 1, 0),
267                              (self.okButton, 0, 0),
268                              (self.closeButton, 0, wx.LEFT | wx.RIGHT, 10)])       
269        mainsizer.Add(sizer_button, 0, wx.EXPAND | wx.BOTTOM | wx.TOP, 10)
270
271        self.SetSizer(mainsizer)
272        self.Centre()
273
274    def on_change_name(self, event=None):
275        """
276        Change name
277        """
278        if event is not None:
279            event.Skip()
280        self.name_tcl.SetBackgroundColour('white')
281        self.Refresh()
282
283    def check_name(self, event=None):
284        """
285        Check name if exist already
286        """
287        mname = M_NAME
288        self.on_change_name(None)
289        list_fnames = os.listdir(self.plugin_dir)
290        # fake existing regular model name list
291        m_list = [model + ".py" for model in self.model_list]
292        list_fnames.append(m_list)
293        # function/file name
294        title = self.name_tcl.GetValue().lstrip().rstrip()
295        if title == '':
296            text = self._operator
297            if text.count('+') > 0:
298                mname = 'Sum'
299            else:
300                mname = 'Multi'
301            mname += M_NAME
302            title = mname
303        self.name = title
304        t_fname = title + '.py'
305        if not self.overwrite_name:
306            if t_fname in list_fnames and title != mname:
307                self.name_tcl.SetBackgroundColour('pink')
308                self.good_name = False
309                info = 'Error'
310                msg = "Name exists already."
311                wx.MessageBox(msg, info)
312                self._notes = msg
313                color = 'red'
314                self._msg_box.SetLabel(msg)
315                self._msg_box.SetForegroundColour(color)
316                return self.good_name
317        self.fname = os.path.join(self.plugin_dir, t_fname)
318        s_title = title
319        if len(title) > 20:
320            s_title = title[0:19] + '...'
321        self._notes = "Model function (%s) has been set! \n" % str(s_title)
322        self.good_name = True
323        self.on_apply(self.fname)
324        return self.good_name
325
326    def on_apply(self, path):
327        """
328        On Apply
329        """
330        self.name_tcl.SetBackgroundColour('white')
331        try:
332            label = self.getText()
333            fname = path
334            name1 = label[0]
335            name2 = label[1]
336            self.write_string(fname, name1, name2)
337            self.compile_file(fname)
338            self.parent.update_custom_combo()
339            msg = self._notes
340            info = 'Info'
341            color = 'blue'
342        except:
343            msg = "Easy Custom Sum/Multipy: Error occurred..."
344            info = 'Error'
345            color = 'red'
346        self._msg_box.SetLabel(msg)
347        self._msg_box.SetForegroundColour(color)
348        if self.parent.parent != None:
349            from sas.guiframe.events import StatusEvent
350            wx.PostEvent(self.parent.parent, StatusEvent(status=msg,
351                                                      info=info))
352        else:
353            raise
354
355    def _set_model_list(self):
356        """
357        Set the list of models
358        """
359        # list of model names
360        cm_list = []
361        # models
362        list = self.model_list
363        # custom models
364        al_list = os.listdir(self.plugin_dir)
365        for c_name in al_list:
366            if c_name.split('.')[-1] == 'py' and \
367                    c_name.split('.')[0] != '__init__':
368                name = str(c_name.split('.')[0])
369                cm_list.append(name)
370                if name not in list:
371                    list.append(name)
372        self.cm_list = cm_list
373        if len(list) > 1:
374            list.sort()
375        for idx in range(len(list)):
376            self.model1.Append(str(list[idx]), idx)
377            self.model2.Append(str(list[idx]), idx)
378        self.model1.SetStringSelection(self.model1_string)
379        self.model2.SetStringSelection(self.model2_string)
380
381    def update_cm_list(self):
382        """
383        Update custom model list
384        """
385        cm_list = []
386        al_list = os.listdir(self.plugin_dir)
387        for c_name in al_list:
388            if c_name.split('.')[-1] == 'py' and \
389                    c_name.split('.')[0] != '__init__':
390                name = str(c_name.split('.')[0])
391                cm_list.append(name)
392        self.cm_list = cm_list
393
394    def on_model1(self, event):
395        """
396        Set model1
397        """
398        event.Skip()
399        self.update_cm_list()
400        self.model1_name = str(self.model1.GetValue())
401        self.model1_string = self.model1_name
402        if self.model1_name in self.cm_list:
403            self.is_p1_custom = True
404        else:
405            self.is_p1_custom = False
406
407    def on_model2(self, event):
408        """
409        Set model2
410        """
411        event.Skip()
412        self.update_cm_list()
413        self.model2_name = str(self.model2.GetValue())
414        self.model2_string = self.model2_name
415        if self.model2_name in self.cm_list:
416            self.is_p2_custom = True
417        else:
418            self.is_p2_custom = False
419
420    def on_select_operator(self, event=None):
421        """
422        On Select an Operator
423        """
424        # For Mac
425        if event != None:
426            event.Skip()
427        item = event.GetEventObject()
428        text = item.GetValue()
429        self.fill_explanation_helpstring(text)
430
431    def fill_explanation_helpstring(self, type):
432        """
433        Choose the equation to use depending on whether we now have
434        a sum or multiply model then create the appropriate string
435        """
436
437        self._type = type
438        name = ''
439
440        if self._type == '*':
441            name = 'Multi'
442            factor = 'BackGround'
443            f_oper = '+'
444        else:
445            name = 'Sum'
446            factor = 'scale_factor'
447            f_oper = '*'
448
449        self.factor = factor
450        self._operator = self._type
451        self.explanation = "  Custom Model = %s %s (model1 %s model2)\n" % \
452                    (self.factor, f_oper, self._operator)
453        self.explanationctr.SetLabel(self.explanation)
454        self.name = name + M_NAME
455
456
457    def fill_oprator_combox(self):
458        """
459        fill the current combobox with the operator
460        """
461        operator_list = [' +', ' *']
462        for oper in operator_list:
463            pos = self._operator_choice.Append(str(oper))
464            self._operator_choice.SetClientData(pos, str(oper.strip()))
465        self._operator_choice.SetSelection(0)
466
467    def getText(self):
468        """
469        Returns model name string as list
470        """
471        return [self.model1_name, self.model2_name]
472
473    def write_string(self, fname, name1, name2):
474        """
475        Write and Save file
476        """
477        self.fname = fname
478        description = self.desc_tcl.GetValue().lstrip().rstrip()
479        if description == '':
480            description = name1 + self._operator + name2
481        name = self.name_tcl.GetValue().lstrip().rstrip()
482        text = self._operator_choice.GetValue()
483        if text.count('+') > 0:
484            factor = 'scale_factor'
485            f_oper = '*'
486            default_val = '1.0'
487        else:
488            factor = 'BackGround'
489            f_oper = '+'
490            default_val = '0.0'
491        path = self.fname
492        try:
493            out_f = open(path, 'w')
494        except :
495            raise
496        lines = SUM_TEMPLATE.split('\n')
497        for line in lines:
498            try:
499                if line.count("scale_factor"):
500                    line = line.replace('scale_factor', factor)
501                    #print "scale_factor", line
502                if line.count("= %s"):
503                    out_f.write(line % (default_val) + "\n")
504                elif line.count("import Model as P1"):
505                    if self.is_p1_custom:
506                        line = line.replace('#', '')
507                        out_f.write(line % name1 + "\n")
508                    else:
509                        out_f.write(line + "\n")
510                elif line.count("import %s as P1"):
511                    if not self.is_p1_custom:
512                        line = line.replace('#', '')
513                        out_f.write(line % (name1, name1) + "\n")
514                    else:
515                        out_f.write(line + "\n")
516                elif line.count("import Model as P2"):
517                    if self.is_p2_custom:
518                        line = line.replace('#', '')
519                        out_f.write(line % name2 + "\n")
520                    else:
521                        out_f.write(line + "\n")
522                elif line.count("import %s as P2"):
523                    if not self.is_p2_custom:
524                        line = line.replace('#', '')
525                        out_f.write(line % (name2, name2) + "\n")
526                    else:
527                        out_f.write(line + "\n")
528                elif line.count("self.description = '%s'"):
529                    out_f.write(line % description + "\n")
530                #elif line.count("run") and line.count("%s"):
531                #    out_f.write(line % self._operator + "\n")
532                #elif line.count("evalDistribution") and line.count("%s"):
533                #    out_f.write(line % self._operator + "\n")
534                elif line.count("return") and line.count("%s") == 2:
535                    #print "line return", line
536                    out_f.write(line % (f_oper, self._operator) + "\n")
537                elif line.count("out2")and line.count("%s"):
538                    out_f.write(line % self._operator + "\n")
539                else:
540                    out_f.write(line + "\n")
541            except:
542                raise
543        out_f.close()
544        #else:
545        #    msg = "Name exists already."
546
547    def compile_file(self, path):
548        """
549        Compile the file in the path
550        """
551        path = self.fname
552        _compileFile(path)
553
554    def delete_file(self, path):
555        """
556        Delete file in the path
557        """
558        _deleteFile(path)
559
560
561class EditorPanel(wx.ScrolledWindow):
562    """
563    Custom model function editor
564    """
565    def __init__(self, parent, base, path, title, *args, **kwds):
566        kwds['name'] = title
567        kwds["size"] = (EDITOR_WIDTH, EDITOR_HEIGTH)
568        kwds["style"] = wx.FULL_REPAINT_ON_RESIZE
569        wx.ScrolledWindow.__init__(self, parent, *args, **kwds)
570        #self.SetupScrolling()
571        self.parent = parent
572        self.base = base
573        self.path = path
574        self.font = wx.SystemSettings_GetFont(wx.SYS_SYSTEM_FONT)
575        self.font.SetPointSize(10)
576        self.reader = None
577        self.name = 'untitled'
578        self.overwrite_name = False
579        self.is_2d = False
580        self.fname = None
581        self.main_sizer = None
582        self.name_sizer = None
583        self.name_hsizer = None
584        self.desc_sizer = None
585        self.param_sizer = None
586        self.function_sizer = None
587        self.func_horizon_sizer = None
588        self.button_sizer = None
589        self.param_strings = ''
590        self.function_strings = ''
591        self._notes = ""
592        self._msg_box = None
593        self.msg_sizer = None
594        self.warning = ""
595        self._description = "New Custom Model"
596        self.function_tcl = None
597        #self._default_save_location = os.getcwd()
598        self._do_layout()
599        #self.bt_apply.Disable()
600
601
602    def _define_structure(self):
603        """
604        define initial sizer
605        """
606        #w, h = self.parent.GetSize()
607        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
608        self.name_sizer = wx.BoxSizer(wx.VERTICAL)
609        self.name_hsizer = wx.BoxSizer(wx.HORIZONTAL)
610        self.desc_sizer = wx.BoxSizer(wx.VERTICAL)
611        self.param_sizer = wx.BoxSizer(wx.VERTICAL)
612        self.function_sizer = wx.BoxSizer(wx.VERTICAL)
613        self.func_horizon_sizer = wx.BoxSizer(wx.HORIZONTAL)
614        self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
615        self.msg_sizer = wx.BoxSizer(wx.HORIZONTAL)
616
617    def _layout_name(self):
618        """
619        Do the layout for file/function name related widgets
620        """
621        #title name [string]
622        name_txt = wx.StaticText(self, -1, 'Function Name : ')
623        overwrite_cb = wx.CheckBox(self, -1, "Overwrite?", (10, 10))
624        overwrite_cb.SetValue(False)
625        overwrite_cb.SetToolTipString("Overwrite it if already exists?")
626        wx.EVT_CHECKBOX(self, overwrite_cb.GetId(), self.on_over_cb)
627        #overwrite_cb.Show(False)
628        self.name_tcl = wx.TextCtrl(self, -1, size=(PANEL_WIDTH * 3 / 5, -1))
629        self.name_tcl.Bind(wx.EVT_TEXT_ENTER, self.on_change_name)
630        self.name_tcl.SetValue('MyFunction')
631        self.name_tcl.SetFont(self.font)
632        hint_name = "Unique Model Function Name."
633        self.name_tcl.SetToolTipString(hint_name)
634        self.name_hsizer.AddMany([(self.name_tcl, 0, wx.LEFT | wx.TOP, 0),
635                                       (overwrite_cb, 0, wx.LEFT, 20)])
636        self.name_sizer.AddMany([(name_txt, 0, wx.LEFT | wx.TOP, 10),
637                                       (self.name_hsizer, 0,
638                                        wx.LEFT | wx.TOP | wx.BOTTOM, 10)])
639
640
641    def _layout_description(self):
642        """
643        Do the layout for description related widgets
644        """
645        #title name [string]
646        desc_txt = wx.StaticText(self, -1, 'Description (optional) : ')
647        self.desc_tcl = wx.TextCtrl(self, -1, size=(PANEL_WIDTH * 3 / 5, -1))
648        self.desc_tcl.SetValue('')
649        #self.name_tcl.SetFont(self.font)
650        hint_desc = "Write a short description of the model function."
651        self.desc_tcl.SetToolTipString(hint_desc)
652        self.desc_sizer.AddMany([(desc_txt, 0, wx.LEFT | wx.TOP, 10),
653                                       (self.desc_tcl, 0,
654                                        wx.LEFT | wx.TOP | wx.BOTTOM, 10)])
655    def _layout_param(self):
656        """
657        Do the layout for parameter related widgets
658        """
659        param_txt = wx.StaticText(self, -1, 'Fit Parameters (if any): ')
660
661        param_tip = "#Set the parameters and initial values.\n"
662        param_tip += "#Example:\n"
663        param_tip += "A = 1\nB = 1"
664        #param_txt.SetToolTipString(param_tip)
665        id = wx.NewId()
666        self.param_tcl = EditWindow(self, id, wx.DefaultPosition,
667                            wx.DefaultSize, wx.CLIP_CHILDREN | wx.SUNKEN_BORDER)
668        self.param_tcl.setDisplayLineNumbers(True)
669        self.param_tcl.SetToolTipString(param_tip)
670
671        self.param_sizer.AddMany([(param_txt, 0, wx.LEFT, 10),
672                        (self.param_tcl, 1, wx.EXPAND | wx.ALL, 10)])
673
674    def _layout_function(self):
675        """
676        Do the layout for function related widgets
677        """
678        function_txt = wx.StaticText(self, -1, 'Function(x) : ')
679        hint_function = "#Example:\n"
680        hint_function += "if x <= 0:\n"
681        hint_function += "    y = A + B\n"
682        hint_function += "else:\n"
683        hint_function += "    y = A + B * cos(2 * pi * x)\n"
684        hint_function += "return y\n"
685        math_txt = wx.StaticText(self, -1, '*Useful math functions: ')
686        math_combo = self._fill_math_combo()
687
688        id = wx.NewId()
689        self.function_tcl = EditWindow(self, id, wx.DefaultPosition,
690                            wx.DefaultSize, wx.CLIP_CHILDREN | wx.SUNKEN_BORDER)
691        self.function_tcl.setDisplayLineNumbers(True)
692        self.function_tcl.SetToolTipString(hint_function)
693
694        self.func_horizon_sizer.Add(function_txt)
695        self.func_horizon_sizer.Add(math_txt, 0, wx.LEFT, 250)
696        self.func_horizon_sizer.Add(math_combo, 0, wx.LEFT, 10)
697
698        self.function_sizer.Add(self.func_horizon_sizer, 0, wx.LEFT, 10)
699        self.function_sizer.Add(self.function_tcl, 1, wx.EXPAND | wx.ALL, 10)
700
701    def _layout_msg(self):
702        """
703        Layout msg
704        """
705        self._msg_box = wx.StaticText(self, -1, self._notes,
706                                      size=(PANEL_WIDTH, -1))
707        self.msg_sizer.Add(self._msg_box, 0, wx.LEFT, 10)
708
709    def _layout_button(self):
710        """
711        Do the layout for the button widgets
712        """
713        self.bt_apply = wx.Button(self, -1, "Apply", size=(_BOX_WIDTH, -1))
714        self.bt_apply.SetToolTipString("Save changes into the imported data.")
715        self.bt_apply.Bind(wx.EVT_BUTTON, self.on_click_apply)
716
717        self.bt_close = wx.Button(self, -1, 'Close', size=(_BOX_WIDTH, -1))
718        self.bt_close.Bind(wx.EVT_BUTTON, self.on_close)
719        self.bt_close.SetToolTipString("Close this panel.")
720
721        self.button_sizer.AddMany([(self.bt_apply, 0,
722                                    wx.LEFT, EDITOR_WIDTH * 0.8),
723                                   (self.bt_close, 0,
724                                    wx.LEFT | wx.BOTTOM, 15)])
725
726    def _do_layout(self):
727        """
728        Draw the current panel
729        """
730        self._define_structure()
731        self._layout_name()
732        self._layout_description()
733        self._layout_param()
734        self._layout_function()
735        self._layout_msg()
736        self._layout_button()
737        self.main_sizer.AddMany([(self.name_sizer, 0,
738                                        wx.EXPAND | wx.ALL, 5),
739                                 (wx.StaticLine(self), 0,
740                                       wx.ALL | wx.EXPAND, 5),
741                                 (self.desc_sizer, 0,
742                                        wx.EXPAND | wx.ALL, 5),
743                                 (wx.StaticLine(self), 0,
744                                       wx.ALL | wx.EXPAND, 5),
745                                (self.param_sizer, 1,
746                                         wx.EXPAND | wx.ALL, 5),
747                                 (wx.StaticLine(self), 0,
748                                       wx.ALL | wx.EXPAND, 5),
749                                (self.function_sizer, 2,
750                                         wx.EXPAND | wx.ALL, 5),
751                                 (wx.StaticLine(self), 0,
752                                       wx.ALL | wx.EXPAND, 5),
753                                 (self.msg_sizer, 0,
754                                        wx.EXPAND | wx.ALL, 5),
755                                (self.button_sizer, 0,
756                                         wx.EXPAND | wx.ALL, 5)])
757        self.SetSizer(self.main_sizer)
758        self.SetAutoLayout(True)
759
760    def _fill_math_combo(self):
761        """
762        Fill up the math combo box
763        """
764        self.math_combo = wx.ComboBox(self, -1, size=(100, -1),
765                                      style=wx.CB_READONLY)
766        for item in dir(math):
767            if item.count("_") < 1:
768                try:
769                    exec "float(math.%s)" % item
770                    self.math_combo.Append(str(item))
771                except:
772                    self.math_combo.Append(str(item) + "()")
773        self.math_combo.Bind(wx.EVT_COMBOBOX, self._on_math_select)
774        self.math_combo.SetSelection(0)
775        return self.math_combo
776
777    def _on_math_select(self, event):
778        """
779        On math selection on ComboBox
780        """
781        event.Skip()
782        label = self.math_combo.GetLabel()
783        self.function_tcl.SetFocus()
784        # Put the text at the cursor position
785        pos = self.function_tcl.GetCurrentPos()
786        self.function_tcl.InsertText(pos, label)
787        # Put the cursor at appropriate postion
788        length = len(label)
789        if label[-1] == ')':
790            length -= 1
791        f_pos = pos + length
792        self.function_tcl.GotoPos(f_pos)
793
794    def get_notes(self):
795        """
796        return notes
797        """
798        return self._notes
799
800    def on_change_name(self, event=None):
801        """
802        Change name
803        """
804        if event is not None:
805            event.Skip()
806        self.name_tcl.SetBackgroundColour('white')
807        self.Refresh()
808
809    def check_name(self):
810        """
811        Check name if exist already
812        """
813        self._notes = ''
814        self.on_change_name(None)
815        plugin_dir = self.path
816        list_fnames = os.listdir(plugin_dir)
817        # function/file name
818        title = self.name_tcl.GetValue().lstrip().rstrip()
819        self.name = title
820        t_fname = title + '.py'
821        if not self.overwrite_name:
822            if t_fname in list_fnames:
823                self.name_tcl.SetBackgroundColour('pink')
824                return False
825        self.fname = os.path.join(plugin_dir, t_fname)
826        s_title = title
827        if len(title) > 20:
828            s_title = title[0:19] + '...'
829        self._notes += "Model function name is set "
830        self._notes += "to %s. \n" % str(s_title)
831        return True
832
833    def on_over_cb(self, event):
834        """
835        Set overwrite name flag on cb event
836        """
837        if event is not None:
838            event.Skip()
839        cb = event.GetEventObject()
840        self.overwrite_name = cb.GetValue()
841
842    def on_click_apply(self, event):
843        """
844        Changes are saved in data object imported to edit
845        """
846        #must post event here
847        event.Skip()
848        info = 'Info'
849        msg = ''
850        # Sort out the errors if occur
851        if self.check_name():
852            name = self.name_tcl.GetValue().lstrip().rstrip()
853            description = self.desc_tcl.GetValue()
854            param_str = self.param_tcl.GetText()
855            func_str = self.function_tcl.GetText()
856            # No input for the model function
857            if func_str.lstrip().rstrip():
858                if func_str.count('return') > 0:
859                    self.write_file(self.fname, description, param_str,
860                                                                    func_str)
861                    tr_msg = _compileFile(self.fname)
862                    msg = str(tr_msg.__str__())
863                    # Compile error
864                    if msg:
865                        msg.replace("  ", "\n")
866                        msg += "\nCompiling Failed"
867                else:
868                    msg = "Error: The func(x) must 'return' a value at least.\n"
869                    msg += "For example: \n\nreturn 2*x"
870            else:
871                msg = 'Error: Function is not defined.'
872        else:
873            msg = "Name exists already."
874        # Prepare for the messagebox
875        if self.base != None and not msg:
876            self.base.update_custom_combo()
877            Model = None
878            exec "from %s import Model" % name
879            try:
880                Model().run(0.01)
881            except:
882                msg = "Error "
883                _, value, _ = sys.exc_info()
884                msg += "in %s:\n%s\n" % (name, value)
885        if msg:
886            info = 'Error'
887            color = 'red'
888            try:
889                # try to remove pyc file if exists
890                _deleteFile(self.fname)
891                _deleteFile(self.fname + "c")
892            except:
893                pass
894        else:
895            msg = "Successful! "
896            msg += "  " + self._notes
897            msg += " Please look for it in the Customized Models."
898            info = 'Info'
899            color = 'blue'
900        # Not to display long error msg
901        if info == 'Error':
902            mss = info
903        else:
904            mss = msg
905        self._msg_box.SetLabel(mss)
906        self._msg_box.SetForegroundColour(color)
907        # Send msg to the top window 
908        if self.base != None:
909                from sas.guiframe.events import StatusEvent
910                wx.PostEvent(self.base.parent, StatusEvent(status=msg,
911                                                      info=info))
912        self.warning = msg
913
914
915    def write_file(self, fname, desc_str, param_str, func_str):
916        """
917        Write content in file
918
919        :param fname: full file path
920        :param desc_str: content of the description strings
921        :param param_str: content of params; Strings
922        :param func_str: content of func; Strings
923        """
924        try:
925            out_f = open(fname, 'w')
926        except :
927            raise
928        # Prepare the content of the function
929        lines = CUSTOM_TEMPLATE.split('\n')
930
931        has_scipy = func_str.count("scipy.")
932        self.is_2d = func_str.count("#self.ndim = 2")
933        line_2d = ''
934        if self.is_2d:
935            line_2d = CUSTOM_2D_TEMP.split('\n')
936        line_test = TEST_TEMPLATE.split('\n')
937        local_params = ''
938        spaces = '        '#8spaces
939        # write function here
940        for line in lines:
941            # The location where to put the strings is
942            # hard-coded in the template as shown below.
943            if line.count("#self.params here"):
944                for param_line in param_str.split('\n'):
945                    p_line = param_line.lstrip().rstrip()
946                    if p_line:
947                        p0_line = self.set_param_helper(p_line)
948                        local_params += self.set_function_helper(p_line)
949                        out_f.write(p0_line)
950            elif line.count("#local params here"):
951                if local_params:
952                    out_f.write(local_params)
953            elif line.count("self.description = "):
954                des0 = self.name + "\\n"
955                desc = str(desc_str.lstrip().rstrip().replace('\"', ''))
956                out_f.write(line % (des0 + desc) + "\n")
957            elif line.count("def function(self, x=0.0%s):"):
958                if self.is_2d:
959                    y_str = ', y=0.0'
960                    out_f.write(line % y_str + "\n")
961                else:
962                    out_f.write(line % '' + "\n")
963            elif line.count("#function here"):
964                for func_line in func_str.split('\n'):
965                    f_line = func_line.rstrip()
966                    if f_line:
967                        out_f.write(spaces + f_line + "\n")
968                if not func_str:
969                    dep_var = 'y'
970                    if self.is_2d:
971                        dep_var = 'z'
972                    out_f.write(spaces + 'return %s' % dep_var + "\n")
973            elif line.count("#import scipy?"):
974                if has_scipy:
975                    out_f.write("import scipy" + "\n")
976            #elif line.count("name = "):
977            #    out_f.write(line % self.name + "\n")
978            elif line:
979                out_f.write(line + "\n")
980        # run string for 2d
981        if line_2d:
982            for line in line_2d:
983                out_f.write(line + "\n")
984        # Test strins
985        for line in line_test:
986            out_f.write(line + "\n")
987
988        out_f.close()
989
990    def set_param_helper(self, line):
991        """
992        Get string in line to define the params dictionary
993
994        :param line: one line of string got from the param_str
995        """
996        flag = True
997        params_str = ''
998        spaces = '        '#8spaces
999        items = line.split(";")
1000        for item in items:
1001            name = item.split("=")[0].lstrip().rstrip()
1002            try:
1003                value = item.split("=")[1].lstrip().rstrip()
1004                float(value)
1005            except:
1006                value = 1.0 # default
1007            params_str += spaces + "self.params['%s'] = %s\n" % (name, value)
1008
1009        return params_str
1010
1011    def set_function_helper(self, line):
1012        """
1013        Get string in line to define the local params
1014
1015        :param line: one line of string got from the param_str
1016        """
1017        flag = True
1018        params_str = ''
1019        spaces = '        '#8spaces
1020        items = line.split(";")
1021        for item in items:
1022            name = item.split("=")[0].lstrip().rstrip()
1023            params_str += spaces + "%s = self.params['%s']\n" % (name, name)
1024        return params_str
1025
1026    def get_warning(self):
1027        """
1028        Get the warning msg
1029        """
1030        return self.warning
1031
1032    def on_close(self, event):
1033        """
1034        leave data as it is and close
1035        """
1036        self.parent.Show(False)#Close()
1037        event.Skip()
1038
1039class EditorWindow(wx.Frame):
1040    """
1041    Editor Window
1042    """
1043    def __init__(self, parent, base, path, title,
1044                 size=(EDITOR_WIDTH, EDITOR_HEIGTH), *args, **kwds):
1045        """
1046        Init
1047        """
1048        kwds["title"] = title
1049        kwds["size"] = size
1050        wx.Frame.__init__(self, parent=None, *args, **kwds)
1051        self.parent = parent
1052        self.panel = EditorPanel(parent=self, base=parent,
1053                                 path=path, title=title)
1054        self.Show(True)
1055        wx.EVT_CLOSE(self, self.OnClose)
1056
1057    def OnClose(self, event):
1058        """
1059        On close event
1060        """
1061        self.Show(False)
1062        #if self.parent != None:
1063        #    self.parent.new_model_frame = None
1064        #self.Destroy() 
1065
1066## Templates for custom models
1067CUSTOM_TEMPLATE = """
1068from sas.models.pluginmodel import Model1DPlugin
1069from math import *
1070import os
1071import sys
1072import numpy
1073#import scipy?
1074class Model(Model1DPlugin):
1075    name = ""
1076    def __init__(self):
1077        Model1DPlugin.__init__(self, name=self.name)
1078        #set name same as file name
1079        self.name = self.get_fname()
1080        #self.params here
1081        self.description = "%s"
1082        self.set_details()
1083    def function(self, x=0.0%s):
1084        #local params here
1085        #function here
1086"""
1087CUSTOM_2D_TEMP = """
1088    def run(self, x=0.0, y=0.0):
1089        if x.__class__.__name__ == 'list':
1090            x_val = x[0]
1091            y_val = y[0]*0.0
1092            return self.function(x_val, y_val)
1093        elif x.__class__.__name__ == 'tuple':
1094            msg = "Tuples are not allowed as input to BaseComponent models"
1095            raise ValueError, msg
1096        else:
1097            return self.function(x, 0.0)
1098    def runXY(self, x=0.0, y=0.0):
1099        if x.__class__.__name__ == 'list':
1100            return self.function(x, y)
1101        elif x.__class__.__name__ == 'tuple':
1102            msg = "Tuples are not allowed as input to BaseComponent models"
1103            raise ValueError, msg
1104        else:
1105            return self.function(x, y)
1106    def evalDistribution(self, qdist):
1107        if qdist.__class__.__name__ == 'list':
1108            msg = "evalDistribution expects a list of 2 ndarrays"
1109            if len(qdist)!=2:
1110                raise RuntimeError, msg
1111            if qdist[0].__class__.__name__ != 'ndarray':
1112                raise RuntimeError, msg
1113            if qdist[1].__class__.__name__ != 'ndarray':
1114                raise RuntimeError, msg
1115            v_model = numpy.vectorize(self.runXY, otypes=[float])
1116            iq_array = v_model(qdist[0], qdist[1])
1117            return iq_array
1118        elif qdist.__class__.__name__ == 'ndarray':
1119            v_model = numpy.vectorize(self.runXY, otypes=[float])
1120            iq_array = v_model(qdist)
1121            return iq_array
1122"""
1123TEST_TEMPLATE = """
1124    def get_fname(self):
1125        path = sys._getframe().f_code.co_filename
1126        basename  = os.path.basename(path)
1127        name, _ = os.path.splitext(basename)
1128        return name
1129######################################################################
1130## THIS IS FOR TEST. DO NOT MODIFY THE FOLLOWING LINES!!!!!!!!!!!!!!!!
1131if __name__ == "__main__":
1132    m= Model()
1133    out1 = m.runXY(0.0)
1134    out2 = m.runXY(0.01)
1135    isfine1 = numpy.isfinite(out1)
1136    isfine2 = numpy.isfinite(out2)
1137    print "Testing the value at Q = 0.0:"
1138    print out1, " : finite? ", isfine1
1139    print "Testing the value at Q = 0.01:"
1140    print out2, " : finite? ", isfine2
1141    if isfine1 and isfine2:
1142        print "===> Simple Test: Passed!"
1143    else:
1144        print "===> Simple Test: Failed!"
1145"""
1146SUM_TEMPLATE = """
1147# A sample of an experimental model function for Sum/Multiply(Pmodel1,Pmodel2)
1148import copy
1149from sas.models.pluginmodel import Model1DPlugin
1150# User can change the name of the model (only with single functional model)
1151#P1_model:
1152#from sas.models.%s import %s as P1
1153#from %s import Model as P1
1154
1155#P2_model:
1156#from sas.models.%s import %s as P2
1157#from %s import Model as P2
1158import os
1159import sys
1160
1161class Model(Model1DPlugin):
1162    name = ""
1163    def __init__(self):
1164        Model1DPlugin.__init__(self, name='')
1165        p_model1 = P1()
1166        p_model2 = P2()
1167        ## Setting  model name model description
1168        self.description = '%s'
1169        self.name = self.get_fname()
1170        if self.name.rstrip().lstrip() == '':
1171            self.name = self._get_name(p_model1.name, p_model2.name)
1172        if self.description.rstrip().lstrip() == '':
1173            self.description = p_model1.name
1174            self.description += p_model2.name
1175            self.fill_description(p_model1, p_model2)
1176
1177        ## Define parameters
1178        self.params = {}
1179
1180        ## Parameter details [units, min, max]
1181        self.details = {}
1182        ## Magnetic Panrameters
1183        self.magnetic_params = []
1184        # non-fittable parameters
1185        self.non_fittable = p_model1.non_fittable
1186        self.non_fittable += p_model2.non_fittable
1187
1188        ##models
1189        self.p_model1= p_model1
1190        self.p_model2= p_model2
1191
1192
1193        ## dispersion
1194        self._set_dispersion()
1195        ## Define parameters
1196        self._set_params()
1197        ## New parameter:scaling_factor
1198        self.params['scale_factor'] = %s
1199
1200        ## Parameter details [units, min, max]
1201        self._set_details()
1202        self.details['scale_factor'] = ['', None, None]
1203
1204
1205        #list of parameter that can be fitted
1206        self._set_fixed_params()
1207        ## parameters with orientation
1208        for item in self.p_model1.orientation_params:
1209            new_item = "p1_" + item
1210            if not new_item in self.orientation_params:
1211                self.orientation_params.append(new_item)
1212
1213        for item in self.p_model2.orientation_params:
1214            new_item = "p2_" + item
1215            if not new_item in self.orientation_params:
1216                self.orientation_params.append(new_item)
1217        ## magnetic params
1218        for item in self.p_model1.magnetic_params:
1219            new_item = "p1_" + item
1220            if not new_item in self.magnetic_params:
1221                self.magnetic_params.append(new_item)
1222
1223        for item in self.p_model2.magnetic_params:
1224            new_item = "p2_" + item
1225            if not new_item in self.magnetic_params:
1226                self.magnetic_params.append(new_item)
1227        # get multiplicity if model provide it, else 1.
1228        try:
1229            multiplicity1 = p_model1.multiplicity
1230            try:
1231                multiplicity2 = p_model2.multiplicity
1232            except:
1233                multiplicity2 = 1
1234        except:
1235            multiplicity1 = 1
1236            multiplicity2 = 1
1237        ## functional multiplicity of the model
1238        self.multiplicity1 = multiplicity1
1239        self.multiplicity2 = multiplicity2
1240        self.multiplicity_info = []
1241
1242    def _clone(self, obj):
1243        obj.params     = copy.deepcopy(self.params)
1244        obj.description     = copy.deepcopy(self.description)
1245        obj.details    = copy.deepcopy(self.details)
1246        obj.dispersion = copy.deepcopy(self.dispersion)
1247        obj.p_model1  = self.p_model1.clone()
1248        obj.p_model2  = self.p_model2.clone()
1249        #obj = copy.deepcopy(self)
1250        return obj
1251
1252    def _get_name(self, name1, name2):
1253        p1_name = self._get_upper_name(name1)
1254        if not p1_name:
1255            p1_name = name1
1256        name = p1_name
1257        name += "_and_"
1258        p2_name = self._get_upper_name(name2)
1259        if not p2_name:
1260            p2_name = name2
1261        name += p2_name
1262        return name
1263
1264    def _get_upper_name(self, name=None):
1265        if name == None:
1266            return ""
1267        upper_name = ""
1268        str_name = str(name)
1269        for index in range(len(str_name)):
1270            if str_name[index].isupper():
1271                upper_name += str_name[index]
1272        return upper_name
1273
1274    def _set_dispersion(self):
1275        ##set dispersion only from p_model
1276        for name , value in self.p_model1.dispersion.iteritems():
1277            #if name.lower() not in self.p_model1.orientation_params:
1278            new_name = "p1_" + name
1279            self.dispersion[new_name]= value
1280        for name , value in self.p_model2.dispersion.iteritems():
1281            #if name.lower() not in self.p_model2.orientation_params:
1282            new_name = "p2_" + name
1283            self.dispersion[new_name]= value
1284
1285    def function(self, x=0.0):
1286        return 0
1287
1288    def getProfile(self):
1289        try:
1290            x,y = self.p_model1.getProfile()
1291        except:
1292            x = None
1293            y = None
1294
1295        return x, y
1296
1297    def _set_params(self):
1298        for name , value in self.p_model1.params.iteritems():
1299            # No 2D-supported
1300            #if name not in self.p_model1.orientation_params:
1301            new_name = "p1_" + name
1302            self.params[new_name]= value
1303
1304        for name , value in self.p_model2.params.iteritems():
1305            # No 2D-supported
1306            #if name not in self.p_model2.orientation_params:
1307            new_name = "p2_" + name
1308            self.params[new_name]= value
1309
1310        # Set "scale" as initializing
1311        self._set_scale_factor()
1312
1313
1314    def _set_details(self):
1315        for name ,detail in self.p_model1.details.iteritems():
1316            new_name = "p1_" + name
1317            #if new_name not in self.orientation_params:
1318            self.details[new_name]= detail
1319
1320        for name ,detail in self.p_model2.details.iteritems():
1321            new_name = "p2_" + name
1322            #if new_name not in self.orientation_params:
1323            self.details[new_name]= detail
1324
1325    def _set_scale_factor(self):
1326        pass
1327
1328
1329    def setParam(self, name, value):
1330        # set param to this (p1, p2) model
1331        self._setParamHelper(name, value)
1332
1333        ## setParam to p model
1334        model_pre = ''
1335        new_name = ''
1336        name_split = name.split('_', 1)
1337        if len(name_split) == 2:
1338            model_pre = name.split('_', 1)[0]
1339            new_name = name.split('_', 1)[1]
1340        if model_pre == "p1":
1341            if new_name in self.p_model1.getParamList():
1342                self.p_model1.setParam(new_name, value)
1343        elif model_pre == "p2":
1344             if new_name in self.p_model2.getParamList():
1345                self.p_model2.setParam(new_name, value)
1346        elif name == 'scale_factor':
1347            self.params['scale_factor'] = value
1348        else:
1349            raise ValueError, "Model does not contain parameter %s" % name
1350
1351    def getParam(self, name):
1352        # Look for dispersion parameters
1353        toks = name.split('.')
1354        if len(toks)==2:
1355            for item in self.dispersion.keys():
1356                # 2D not supported
1357                if item.lower()==toks[0].lower():
1358                    for par in self.dispersion[item]:
1359                        if par.lower() == toks[1].lower():
1360                            return self.dispersion[item][par]
1361        else:
1362            # Look for standard parameter
1363            for item in self.params.keys():
1364                if item.lower()==name.lower():
1365                    return self.params[item]
1366        return
1367        #raise ValueError, "Model does not contain parameter %s" % name
1368
1369    def _setParamHelper(self, name, value):
1370        # Look for dispersion parameters
1371        toks = name.split('.')
1372        if len(toks)== 2:
1373            for item in self.dispersion.keys():
1374                if item.lower()== toks[0].lower():
1375                    for par in self.dispersion[item]:
1376                        if par.lower() == toks[1].lower():
1377                            self.dispersion[item][par] = value
1378                            return
1379        else:
1380            # Look for standard parameter
1381            for item in self.params.keys():
1382                if item.lower()== name.lower():
1383                    self.params[item] = value
1384                    return
1385
1386        raise ValueError, "Model does not contain parameter %s" % name
1387
1388
1389    def _set_fixed_params(self):
1390        for item in self.p_model1.fixed:
1391            new_item = "p1" + item
1392            self.fixed.append(new_item)
1393        for item in self.p_model2.fixed:
1394            new_item = "p2" + item
1395            self.fixed.append(new_item)
1396
1397        self.fixed.sort()
1398
1399
1400    def run(self, x = 0.0):
1401        self._set_scale_factor()
1402        return self.params['scale_factor'] %s \
1403(self.p_model1.run(x) %s self.p_model2.run(x))
1404
1405    def runXY(self, x = 0.0):
1406        self._set_scale_factor()
1407        return self.params['scale_factor'] %s \
1408(self.p_model1.runXY(x) %s self.p_model2.runXY(x))
1409
1410    ## Now (May27,10) directly uses the model eval function
1411    ## instead of the for-loop in Base Component.
1412    def evalDistribution(self, x = []):
1413        self._set_scale_factor()
1414        return self.params['scale_factor'] %s \
1415(self.p_model1.evalDistribution(x) %s \
1416self.p_model2.evalDistribution(x))
1417
1418    def set_dispersion(self, parameter, dispersion):
1419        value= None
1420        new_pre = parameter.split("_", 1)[0]
1421        new_parameter = parameter.split("_", 1)[1]
1422        try:
1423            if new_pre == 'p1' and \
1424new_parameter in self.p_model1.dispersion.keys():
1425                value= self.p_model1.set_dispersion(new_parameter, dispersion)
1426            if new_pre == 'p2' and \
1427new_parameter in self.p_model2.dispersion.keys():
1428                value= self.p_model2.set_dispersion(new_parameter, dispersion)
1429            self._set_dispersion()
1430            return value
1431        except:
1432            raise
1433
1434    def fill_description(self, p_model1, p_model2):
1435        description = ""
1436        description += "This model gives the summation or multiplication of"
1437        description += "%s and %s. "% ( p_model1.name, p_model2.name )
1438        self.description += description
1439
1440    def get_fname(self):
1441        path = sys._getframe().f_code.co_filename
1442        basename  = os.path.basename(path)
1443        name, _ = os.path.splitext(basename)
1444        return name
1445
1446if __name__ == "__main__":
1447    m1= Model()
1448    #m1.setParam("p1_scale", 25)
1449    #m1.setParam("p1_length", 1000)
1450    #m1.setParam("p2_scale", 100)
1451    #m1.setParam("p2_rg", 100)
1452    out1 = m1.runXY(0.01)
1453
1454    m2= Model()
1455    #m2.p_model1.setParam("scale", 25)
1456    #m2.p_model1.setParam("length", 1000)
1457    #m2.p_model2.setParam("scale", 100)
1458    #m2.p_model2.setParam("rg", 100)
1459    out2 = m2.p_model1.runXY(0.01) %s m2.p_model2.runXY(0.01)\n
1460    print "My name is %s."% m1.name
1461    print out1, " = ", out2
1462    if out1 == out2:
1463        print "===> Simple Test: Passed!"
1464    else:
1465        print "===> Simple Test: Failed!"
1466"""
1467
1468if __name__ == "__main__": 
1469#    app = wx.PySimpleApp()
1470    app = wx.App()
1471    frame = TextDialog(id=1, model_list=["SphereModel", "CylinderModel"],plugin_dir='../fitting/plugin_models')   
1472#    frame = wx.Dialog()
1473    frame.ShowModal()
1474#    frame.Show(True)
1475    app.MainLoop()             
1476
1477#if __name__ == "__main__":
1478#    from sas.perspectives.fitting import models
1479#    dir_path = models.find_plugins_dir()
1480#    app = wx.App()
1481#    window = EditorWindow(parent=None, base=None, path=dir_path, title="Editor")
1482#    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.