source: sasview/src/sas/perspectives/calculator/model_editor.py @ 49ab5d7

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 49ab5d7 was 49ab5d7, checked in by Doucet, Mathieu <doucetm@…>, 9 years ago

Take care of white spaces (pylint)

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