source: trunk/GSASIIexprGUI.py @ 1378

Last change on this file since 1378 was 1378, checked in by toby, 8 years ago

wxPython 2.9 fixes: FlexGridSizer?, wx.Colour, MacOpenFile?; move bind to prevent Rigid body crash

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 28.6 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIexprGUI - Expression Definition and Evaluation
3########### SVN repository information ###################
4# $Date: 2014-06-11 20:38:24 +0000 (Wed, 11 Jun 2014) $
5# $Author: toby $
6# $Revision: 1378 $
7# $URL: trunk/GSASIIexprGUI.py $
8# $Id: GSASIIexprGUI.py 1378 2014-06-11 20:38:24Z toby $
9########### SVN repository information ###################
10'''
11*GSASIIexprGUI: Expression Handling*
12-------------------------------------
13
14This module defines a class for defining an expression in terms of values
15in a parameter dictionary via a wx.Dialog. The dialog creates a :class:`GSASII.ExpressionObj`
16which is used to evaluate the expression against a supplied parameter dictionary.
17
18The expression is parsed to find variables used in the expression and then
19the user is asked to assign parameters from the dictionary to each variable.
20
21'''
22import re
23import sys
24import wx
25import wx.lib.scrolledpanel as wxscroll
26import numpy as np
27import GSASIIgrid as G2gd
28import GSASIIpy3 as G2py3
29import GSASIIobj as G2obj
30
31def IndexParmDict(parmDict,wildcard):
32    '''Separate the parameters in parmDict into list of keys by parameter
33    type.
34   
35    :param dict parmDict: a dict with GSAS-II parameters
36    :param bool wildcard: True if wildcard versions of parameters should
37      be generated and added to the lists
38    :returns: a dict of lists where key 1 is a list of phase parameters,
39      2 is histogram/phase parms, 3 is histogram parms and 4 are global parameters
40    '''
41    prex = re.compile('[0-9]+::.*')
42    hrex = re.compile(':[0-9]+:.*')
43    parmLists = {}
44    for i in (1,2,3,4):
45        parmLists[i] = []
46    for i in sorted(parmDict.keys()):
47        if i.startswith("::") or i.find(':') == -1: # globals
48            parmLists[4].append(i)
49        elif prex.match(i):
50            parmLists[1].append(i)
51        elif hrex.match(i):
52            parmLists[3].append(i)
53        else:
54            parmLists[2].append(i)
55    if wildcard:
56        for i in (1,2,3,4):
57            parmLists[i] += G2obj.GenWildCard(parmLists[i]) # generate wildcard versions
58    for i in (1,2,3,4):
59        parmLists[i].sort()
60    return parmLists
61
62#==========================================================================
63class ExpressionDialog(wx.Dialog):
64    '''A wx.Dialog that allows a user to input an arbitrary expression
65    to be evaluated and possibly minimized.
66
67    To do this, the user assigns a new (free) or existing
68    GSAS-II parameter to each parameter label used in the expression.
69    The free parameters can optionally be designated to be refined.
70    For example, is an expression is used such as::
71
72    'A*np.exp(-B/C)'
73
74    then A, B and C can each be assigned as Free parameter with a user-selected
75    value or to any existing GSAS-II variable in the parameter dictionary.
76    As the expression is entered it is checked for validity.
77
78    After the :class:`ExpressionDialog` object is created, use :meth:`Show` to
79    run it and obtain a :class:`GSASIIobj.ExpressionObj` object with the user
80    input.
81
82    :param wx.Frame parent: The parent of the Dialog. Can be None,
83      but better is to provide the name of the Frame where the dialog
84      is called.
85    :param dict parmDict: a dict with defined parameters and their values. Each value
86      may be a list with parameter values and a refine flag or may just contain
87      the parameter value (non-float/int values in dict are ignored)
88    :param str exprObj: a :class:`GSASIIobj.ExpressionObj` object with an expression and
89      label assignments or None (default)
90    :param str wintitle: String placed on title bar of dialog;
91      defaults to "Expression Editor"
92    :param str header: String placed at top of dialog to tell the user
93      what they will do here; default is "Enter restraint expression here"
94    :param bool fit: determines if the expression will be used in fitting (default=True).
95      If set to False, and refinement flags are not shown
96      and Free parameters are not offered as an assignment option.
97    :param str VarLabel: an optional variable label to include before the expression
98      input. Ignored if None (default)
99    :param list depVarDict: a dict of choices for the dependent variable to be
100      fitted to the expression and their values. Ignored if None (default).
101    :param list ExtraButton: a list with two terms that define [0]: the label
102      for an extra button and [1] the callback routine to be used when the
103      button is pressed. The button will only be enabled when the OK button can be
104      used (meaning the equation/expression is valid). The default is None, meaning this
105      will not be used.
106    :param list usedVars: defines a list of previously used variable names. These names
107      will not be reused as defaults for new free variables.
108      (The default is an empty list).
109    '''
110    def __init__(self, parent, parmDict, exprObj=None,
111                 header='Enter restraint expression here',
112                 wintitle='Expression Editor',
113                 fit=True,VarLabel=None,depVarDict=None,
114                 ExtraButton=None,usedVars=[]):
115        self.fit = fit
116        self.depVarDict = depVarDict
117        'dict for dependent variables'
118        self.parmDict = {}
119        '''A copy of the G2 parameter dict (parmDict) except only numerical
120        values are included and only the value (not the vary flag, if present)
121        is included.
122        '''
123        self.exprVarLst = []
124        '''A list containing the variables utilized in the current expression.
125        Placed into a :class:`GSASIIobj.ExpressionObj` object when the dialog is closed
126        with "OK", saving any changes.
127        '''
128        self.varSelect = {}
129        '''A dict that shows the variable type for each label
130        found in the expression.
131
132        * If the value is None or is not defined, the value is not assigned.
133        * If the value is 0, then the varible is "free" -- a new refineable
134          parameter.
135        * Values above 1 determine what variables will be shown
136          when the option is selected.
137        '''
138        self.varName = {}
139        'Name assigned to each variable'
140        self.varValue = {}
141        'Value for a variable (Free parameters only)'
142        self.varRefflag = {}
143        'Refinement flag for a variable (Free parameters only)'
144        self.expr = ''
145        'Expression as a text string'
146        self.dependentVar = None
147        'name for dependent variable selection, when depVarDict is specified'
148        self.usedVars = usedVars
149        'variable names that have been used and should not be reused by default'
150
151        # process dictionary of values and create an index
152        for key in parmDict:
153            try: # deal with values that are in lists
154                val = parmDict[key][0]
155            except (TypeError,IndexError):
156                val = parmDict[key]
157            if isinstance(val, basestring): continue
158            try:
159                self.parmDict[key] = float(val)
160            except:
161                pass
162        # separate the variables by type
163        self.parmLists = IndexParmDict(self.parmDict,self.fit)
164        self.timer = wx.Timer()
165        self.timer.Bind(wx.EVT_TIMER,self.OnValidate)
166
167        style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
168        wx.Dialog.__init__(self, parent, wx.ID_ANY, wintitle, style=style)
169        self.mainsizer = wx.BoxSizer(wx.VERTICAL)
170        label = wx.StaticText(self,  wx.ID_ANY, header)
171        self.mainsizer.Add(label, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
172
173        self.exsizer = wx.BoxSizer(wx.HORIZONTAL)
174        if VarLabel:
175            label = wx.StaticText(self,  wx.ID_ANY, VarLabel + ' = ')
176            self.exsizer.Add(label, 0, wx.ALL|wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 0)
177        elif depVarDict:
178            self.depParmLists = IndexParmDict(self.depVarDict,False)
179            choices = ['','Phase','Hist./Phase','Hist.','Global']
180            for i in range(1,len(choices)): # remove empty menus from choice list
181                if not len(self.depParmLists[i]): choices[i] = ''
182            self.depChoices = [i for i in range(len(choices)) if choices[i]]
183            choice = wx.Choice(
184                self, wx.ID_ANY,
185                choices = [choices[i] for i in self.depChoices]
186                )
187            choice.SetSelection(wx.NOT_FOUND)
188            choice.Bind(wx.EVT_CHOICE,self.OnDepChoice)
189            self.exsizer.Add(choice, 0, wx.ALL|wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 0)
190            self.exsizer.Add((5,5))
191            self.depLabel = wx.StaticText(self,  wx.ID_ANY, ' ')
192            self.exsizer.Add(self.depLabel, 0, wx.ALL|wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 0)
193            label = wx.StaticText(self,  wx.ID_ANY, ' = ')
194            self.exsizer.Add(label, 0, wx.ALL|wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 0)
195
196        self.exCtrl = wx.TextCtrl(self,  wx.ID_ANY, size=(150,-1),style=wx.TE_PROCESS_ENTER)
197        self.exCtrl.Bind(wx.EVT_CHAR, self.OnChar)
198        self.exCtrl.Bind(wx.EVT_TEXT_ENTER, self.OnValidate)
199        self.exsizer.Add(self.exCtrl, 1, wx.ALL|wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 0)
200        #self.mainsizer.Add(self.exCtrl, 0, wx.ALL|wx.EXPAND, 5)
201        self.mainsizer.Add(self.exsizer, 0, wx.ALL|wx.EXPAND, 5)
202        self.mainsizer.Add((-1,5),0,wx.EXPAND,1)
203
204        evalSizer = wx.BoxSizer(wx.HORIZONTAL)
205        self.mainsizer.Add(evalSizer,0,wx.ALL|wx.EXPAND,0)
206        btn = wx.Button(self, wx.ID_ANY,"Validate")
207        btn.Bind(wx.EVT_BUTTON,self.OnValidate)
208        evalSizer.Add(btn,0,wx.LEFT|wx.RIGHT,5)
209        self.result = wx.StaticText(self,  wx.ID_ANY, '')
210        evalSizer.Add(self.result, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
211
212        self.varSizer = wx.BoxSizer(wx.HORIZONTAL)
213        self.mainsizer.Add(self.varSizer,1,wx.ALL|wx.EXPAND,1)
214        self.mainsizer.Add((-1,5),0,wx.EXPAND,1)
215
216        bSizer = wx.BoxSizer(wx.HORIZONTAL)
217        btnsizer = wx.StdDialogButtonSizer()
218        if ExtraButton:
219            self.ExtraBtn = wx.Button(self, wx.ID_ANY, ExtraButton[0])
220            self.ExtraBtn.Bind(wx.EVT_BUTTON,self.OnExtra)
221            self.ExtraCallBack = ExtraButton[1]
222            self.ExtraBtn.Disable()
223            bSizer.Add(self.ExtraBtn, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 2)
224        else:
225            self.ExtraBtn = None
226        bSizer.Add((1,1), 1, wx.ALIGN_CENTER|wx.ALL|wx.EXPAND, 0)
227        self.OKbtn = wx.Button(self, wx.ID_OK)
228        self.OKbtn.SetDefault()
229        self.OKbtn.Disable()
230        btnsizer.AddButton(self.OKbtn)
231        btn = wx.Button(self, wx.ID_CANCEL)
232        btnsizer.AddButton(btn)
233        btnsizer.Realize()
234        bSizer.Add(btnsizer, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
235        self.mainsizer.Add(bSizer, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, 5)
236        self.SetSizer(self.mainsizer)
237        self.CenterOnParent()
238        if exprObj:
239            self.expr = exprObj.EditExpression(
240                self.exprVarLst,
241                self.varSelect,
242                self.varName,
243                self.varValue,
244                self.varRefflag,
245                )
246            # set the initial value for the dependent value
247            if self.depVarDict:
248                var = exprObj.GetDepVar()
249                if var in self.depVarDict:
250                    self.depLabel.SetLabel(var)
251                    self.dependentVar = var
252                   
253        self.exCtrl.SetValue(self.expr)
254        self.OnValidate(None)
255        self.SetMinSize((620,300)) # seems like a good size
256        #self.errbox.SetAutoLayout(1)
257        #self.errbox.SetupScrolling()
258        #self.varbox.SetAutoLayout(1)
259        #self.varbox.SetupScrolling()
260        #self.mainsizer.Fit(self)
261
262    def OnExtra(self,event):
263        exprObj = G2obj.ExpressionObj()
264        exprObj.LoadExpression(
265            self.expr,
266            self.exprVarLst,
267            self.varSelect,
268            self.varName,
269            self.varValue,
270            self.varRefflag,
271            )
272        if self.depVarDict:
273            exprObj.SetDepVar(self.dependentVar)
274        self.ExtraCallBack(exprObj)
275        # put results back into displayed dialog
276        resDict = dict(exprObj.GetVariedVarVal())
277        for v,var in self.varName.items():
278            varname = "::" + var.lstrip(':').replace(' ','_').replace(':',';')
279            val =  resDict.get(varname)
280            if val:
281                self.varValue[v] = val
282        wx.CallAfter(self.ShowVars())
283
284    def Show(self,mode=True):
285        '''Call to use the dialog after it is created.
286
287        :returns: None (On Cancel) or a new :class:`~GSASIIobj.ExpressionObj`
288        '''
289        self.Layout()
290        self.mainsizer.Fit(self)
291        if self.ShowModal() == wx.ID_OK:
292            # store the edit results in the object and return it
293            exprObj = G2obj.ExpressionObj()
294            exprObj.LoadExpression(
295                self.expr,
296                self.exprVarLst,
297                self.varSelect,
298                self.varName,
299                self.varValue,
300                self.varRefflag,
301                )
302            if self.depVarDict:
303                exprObj.SetDepVar(self.dependentVar)
304            return exprObj
305        else:
306            return None
307       
308    def setEvalResult(self,msg):
309        'Show a string in the expression result area'
310        self.result.SetLabel(msg)
311
312    def RestartTimer(self):
313        '''Cancels any running timer and starts a new one.
314        The timer causes a check of syntax after 2 seconds unless there is further input.
315        Disables the OK button until a validity check is complete.
316        '''
317        if self.timer.IsRunning():
318            self.timer.Stop()
319        self.timer.Start(2000,oneShot=True)
320       
321    def OnChar(self,event):
322        '''Called as each character is entered. Cancels any running timer
323        and starts a new one. The timer causes a check of syntax after 2 seconds
324        without input.
325        Disables the OK button until a validity check is complete.
326        '''
327        self.RestartTimer()
328        self.OKbtn.Disable()
329        if self.ExtraBtn: self.ExtraBtn.Disable()
330        event.Skip()
331        return
332   
333    def CheckVars(self):
334        '''Check that appropriate variables are defined for each
335        symbol used in :data:`self.expr`
336
337        :returns: a text error message or None if all needed input is present       
338        '''
339        invalid = 0
340        for v in self.exprVarLst:
341            if self.varSelect.get(v) is None:
342                invalid += 1
343        if invalid==1:
344            return '(a variable is not assigned)'
345        elif invalid:
346            return '('+str(invalid)+' variables are not assigned)'
347        msg = ''
348        for v in self.exprVarLst:
349            varname = self.varName.get(v)
350            if not varname:
351                invalid += 1
352                if msg: msg += "; "
353                msg += 'No variable for '+str(v)
354            elif self.varSelect.get(v) > 0:
355               if '*' in varname:
356                   l = G2obj.LookupWildCard(varname,self.parmDict.keys())
357                   if len(l) == 0:
358                       invalid += 1
359                       if msg: msg += "; "
360                       msg += 'No variables match '+str(varname)
361               elif varname not in self.parmDict.keys():
362                   invalid += 1
363                   if msg: msg += "; "
364                   msg += 'No variables match '+str(varname)
365            else:
366                # value assignment: this check is likely unneeded
367                val = self.varValue.get(v)
368                try:
369                    float(val)
370                except ValueError,TypeError:
371                    invalid += 1
372                    if msg: msg += "; "
373                    if val is None:
374                        msg += 'No value for '+str(v)
375                    else:
376                        msg += 'Value '+str(val)+' invalid for '+str(v)
377        if invalid:
378            return '('+msg+')'       
379        return
380
381    def ShowVars(self):
382        # create widgets to associate vars with labels and/or show messages
383        self.varSizer.Clear(True)
384        self.errbox = wxscroll.ScrolledPanel(self,style=wx.HSCROLL)
385        self.errbox.SetMinSize((100,130))
386        self.varSizer.Add(self.errbox,0,wx.ALL|wx.EXPAND,1)
387        self.varbox = wxscroll.ScrolledPanel(self,style=wx.HSCROLL)
388        self.varSizer.Add(self.varbox,1,wx.ALL|wx.EXPAND,1)
389        Siz = wx.BoxSizer(wx.VERTICAL)
390        Siz.Add(
391            wx.StaticText(self.varbox,wx.ID_ANY,
392                          'Assignment of variables to labels:'),
393            0,wx.EXPAND|wx.ALIGN_CENTER,0)
394        GridSiz = wx.FlexGridSizer(0,5,2,2)
395        GridSiz.Add(
396            wx.StaticText(self.varbox,wx.ID_ANY,'label',style=wx.CENTER),
397            0,wx.ALIGN_CENTER)
398        lbls = ('varib. type\nselection','variable\nname','value')
399        choices = ['Free','Phase','Hist./Phase','Hist.','Global']
400        if self.fit:
401            lbls += ('refine\nflag',)
402        else:
403            lbls += ('',)
404            choices[0] = ''
405        for i in range(1,len(choices)): # remove empty menus from choice list
406            if not len(self.parmLists[i]): choices[i] = ''
407        self.AllowedChoices = [i for i in range(len(choices)) if choices[i]]
408        for lbl in lbls:
409            w = wx.StaticText(self.varbox,wx.ID_ANY,lbl,style=wx.CENTER)
410            w.SetMinSize((80,-1))
411            GridSiz.Add(w,0,wx.ALIGN_CENTER)
412
413        # show input for each var in expression.
414        for v in self.exprVarLst:
415            # label
416            GridSiz.Add(wx.StaticText(self.varbox,wx.ID_ANY,v),0,wx.ALIGN_CENTER,0)
417            # assignment type
418            ch = wx.Choice(
419                self.varbox, wx.ID_ANY,
420                choices = [choices[i] for i in self.AllowedChoices]
421                )
422            GridSiz.Add(ch,0,wx.ALIGN_LEFT,0)
423            if v in self.varSelect and self.varSelect.get(v) in self.AllowedChoices:
424                i = self.AllowedChoices.index(self.varSelect[v])
425                ch.SetSelection(i)
426            else:
427                ch.SetSelection(wx.NOT_FOUND)
428            ch.label = v
429            ch.Bind(wx.EVT_CHOICE,self.OnChoice)
430
431            # var name/var assignment
432            if self.varSelect.get(v) is None:
433                GridSiz.Add((-1,-1),0,wx.ALIGN_LEFT|wx.EXPAND,0)
434            elif self.varSelect.get(v) == 0:
435                wid = G2gd.ValidatedTxtCtrl(self.varbox,self.varName,v,
436                                            #OnLeave=self.OnTxtLeave,
437                                            size=(50,-1))
438                GridSiz.Add(wid,0,wx.ALIGN_LEFT|wx.EXPAND,0)
439            else:
440                wid = wx.StaticText(self.varbox,wx.ID_ANY,self.varName[v])
441                GridSiz.Add(wid,0,wx.ALIGN_LEFT,0)
442
443            # value
444            if self.varSelect.get(v) is None:
445                GridSiz.Add((-1,-1),0,wx.ALIGN_RIGHT|wx.EXPAND,0)
446            elif self.varSelect.get(v) == 0:
447                wid = G2gd.ValidatedTxtCtrl(self.varbox,self.varValue,v,
448                                            #OnLeave=self.OnTxtLeave,
449                                            size=(75,-1))
450                GridSiz.Add(wid,0,wx.ALIGN_LEFT|wx.EXPAND,0)
451                wid.Bind(wx.EVT_CHAR,self.OnChar)
452            else:
453                var = self.varName[v]
454                if '*' in var:
455                    #[self.parmDict[v] for v in LookupWildCard(var,self.parmDict.keys())]
456                    #print self.varValue[v]
457                    vs = G2obj.LookupWildCard(var,self.parmDict.keys())
458                    s = '('+str(len(vs))+' values)'
459                elif var in self.parmDict:
460                    val = self.parmDict[var]
461                    s = G2py3.FormatSigFigs(val).rstrip('0')
462                else:
463                    s = '?'
464                wid = wx.StaticText(self.varbox,wx.ID_ANY,s)
465                GridSiz.Add(wid,0,wx.ALIGN_LEFT,0)
466
467            # show a refine flag for Free Vars only
468            if self.varSelect.get(v) == 0 and self.fit:
469                self.varRefflag[v] = self.varRefflag.get(v,True)
470                wid = G2gd.G2CheckBox(self.varbox,'',self.varRefflag,v)
471                GridSiz.Add(wid,0,wx.ALIGN_LEFT|wx.EXPAND,0)
472            else:
473                wid = (-1,-1)
474                GridSiz.Add(wid,0,wx.ALIGN_LEFT|wx.EXPAND,0)
475
476        Siz.Add(GridSiz)
477        self.varbox.SetSizer(Siz,True)
478        xwid,yhgt = Siz.Fit(self.varbox)
479        self.varbox.SetMinSize((xwid,130))
480        self.varbox.SetAutoLayout(1)
481        self.varbox.SetupScrolling()
482        self.varbox.Refresh()
483        self.Layout()
484        return
485
486    def OnDepChoice(self,event):
487        '''Respond to a selection of a variable type for a label in
488        an expression
489        '''
490        sel = self.depChoices[event.GetEventObject().GetSelection()]
491        var = self.SelectG2var(sel,'Dependent variable',self.depParmLists[sel])
492        if not var:
493            self.dependentVar = None
494            self.OnValidate(None)
495            event.GetEventObject().SetSelection(wx.NOT_FOUND)
496            return
497        self.dependentVar = var
498        self.depLabel.SetLabel(var)
499        self.OnValidate(None)
500        self.Layout()
501
502    def GetDepVar(self):
503        '''Returns the name of the dependent variable, when depVarDict is used.
504        '''
505        return self.dependentVar
506       
507    def OnChoice(self,event):
508        '''Respond to a selection of a variable type for a label in
509        an expression
510        '''
511        v = event.GetEventObject().label
512        sel = self.AllowedChoices[event.GetEventObject().GetSelection()]
513        self.varSelect[v] = sel
514        if sel == 0:
515            sv = G2obj.MakeUniqueLabel(v,self.usedVars)
516            self.varName[v] = sv
517            self.varValue[v] = self.varValue.get(v,0.0)
518        else:
519            var = self.SelectG2var(sel,v,self.parmLists[sel])
520            if not var:
521                del self.varSelect[v]
522                self.OnValidate(None)
523                return
524            self.varName[v] = var
525        self.OnValidate(None)
526
527    def SelectG2var(self,sel,var,parmList):
528        '''Offer a selection of a GSAS-II variable.
529
530        :param int sel: Determines the type of variable to be selected.
531          where 1 is used for Phase variables, and 2 for Histogram/Phase vars,
532          3 for Histogram vars and 4 for Global vars.
533        :returns: a variable name or None (if Cancel is pressed)
534        '''
535        if not parmList:
536            return None
537        l2 = l1 = 1
538        for i in parmList:
539            l1 = max(l1,len(i))
540            loc,desc = G2obj.VarDescr(i)
541            l2 = max(l2,len(loc))
542        fmt = u"{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}"
543        varListlbl = [fmt.format(i,*G2obj.VarDescr(i)) for i in parmList]
544
545        dlg = G2gd.G2SingleChoiceDialog(
546            self,'Select GSAS-II variable for '+str(var)+':',
547            'Select variable',
548            varListlbl,monoFont=True)
549        dlg.SetSize((625,250))
550        dlg.CenterOnParent()
551        var = None
552        if dlg.ShowModal() == wx.ID_OK:
553            i = dlg.GetSelection()
554            var = parmList[i]
555        dlg.Destroy()
556        return var
557
558    def showError(self,msg1,msg2='',msg3=''):
559        '''Show an error message of 1 to 3 sections. The second
560        section is shown in an equally-spaced font.
561       
562        :param str msg1: msg1 is shown in a the standard font
563        :param str msg2: msg2 is shown in a equally-spaced (wx.MODERN) font
564        :param str msg3: msg3 is shown in a the standard font
565        '''
566        self.OKbtn.Disable()
567        if self.ExtraBtn: self.ExtraBtn.Disable()
568        self.varSizer.Clear(True)
569        self.errbox = wxscroll.ScrolledPanel(self,style=wx.HSCROLL)
570        self.errbox.SetMinSize((200,130))
571        self.varSizer.Add(self.errbox,1,wx.ALL|wx.EXPAND,1)
572        Siz = wx.BoxSizer(wx.VERTICAL)
573        errMsg1 = wx.StaticText(self.errbox, wx.ID_ANY,"")
574        Siz.Add(errMsg1, 0, wx.ALIGN_LEFT|wx.LEFT|wx.EXPAND, 5)
575        errMsg2 = wx.StaticText(self.errbox, wx.ID_ANY,"\n\n")
576        font1 = wx.Font(errMsg2.GetFont().GetPointSize(),
577                        wx.MODERN, wx.NORMAL, wx.NORMAL, False)
578        errMsg2.SetFont(font1)
579        Siz.Add(errMsg2, 0, wx.ALIGN_LEFT|wx.LEFT|wx.EXPAND, 5)
580        errMsg3 = wx.StaticText(self.errbox, wx.ID_ANY,"")
581        Siz.Add(errMsg3, 0, wx.ALIGN_LEFT|wx.LEFT|wx.EXPAND, 5)
582        self.errbox.SetSizer(Siz,True)
583        Siz.Fit(self.errbox)
584        errMsg1.SetLabel(msg1)
585        errMsg2.SetLabel("  "+msg2)
586        errMsg2.Wrap(-1)
587        errMsg3.SetLabel(msg3)
588        self.Layout()
589
590    def OnValidate(self,event):
591        '''Respond to a press of the Validate button or when a variable
592        is associated with a label (in :meth:`OnChoice`)
593        '''
594        self.setEvalResult('(expression cannot be evaluated)')
595        self.timer.Stop()
596        self.expr = self.exCtrl.GetValue().strip()
597        self.varSizer.Clear(True)
598        if not self.expr: 
599            self.showError(
600                "Invalid Expression:","",
601                "(an expression must be entered)")
602            return
603        exprObj = G2obj.ExpressionObj()
604        ret = exprObj.ParseExpression(self.expr)
605        if not ret:
606            self.showError(*exprObj.lastError)
607            return
608        self.exprVarLst,pkgdict = ret
609        wx.CallAfter(self.Repaint,exprObj)
610           
611    def Repaint(self,exprObj):
612        'Redisplay the variables and continue the validation'
613        self.ShowVars() # show widgets to set vars
614        msg = self.CheckVars() 
615        if msg:
616            self.setEvalResult(msg)
617            return
618        exprObj.LoadExpression(
619            self.expr,
620            self.exprVarLst,
621            self.varSelect,
622            self.varName,
623            self.varValue,
624            self.varRefflag,
625            )
626        try:
627            calcobj = G2obj.ExpressionCalcObj(exprObj)
628            calcobj.SetupCalc(self.parmDict)
629            val = calcobj.EvalExpression()
630        except Exception as msg:
631            self.setEvalResult("Error in evaluation: "+str(msg))
632            return
633        if not np.isfinite(val):
634            self.setEvalResult("Expression value is infinite or out-of-bounds")
635            return
636        s = G2py3.FormatSigFigs(val).rstrip('0')
637        depVal = ""
638        if self.depVarDict:
639            if not self.dependentVar:
640                self.setEvalResult("A dependent variable must be selected.")
641                return
642            depVal = '; Variable "' + self.dependentVar + '" = ' + str(
643                self.depVarDict.get(self.dependentVar,'?')
644                )
645        self.setEvalResult("Expression evaluates to: "+str(s)+depVal)
646        self.OKbtn.Enable()
647        if self.ExtraBtn: self.ExtraBtn.Enable()
648       
649if __name__ == "__main__":
650    app = wx.PySimpleApp() # create the App
651    frm = wx.Frame(None)
652    frm.Show()
653    PSvarDict = {'::a':1.0,'::b':1.1,'0::c':1.2,'::AlongVaraiableName':1000.}
654    #PSvars = PSvarDict.keys()
655    indepvarDict = {'Temperature':1.0,'Pressure':1.1,'Phase of Moon':1.2,'1:1:HAP':1.3}
656    dlg = ExpressionDialog(frm,indepvarDict,
657                           header="Edit the PseudoVar expression",
658                           fit=False,
659                           depVarDict=PSvarDict,
660                           #VarLabel="New PseudoVar",                           
661                           )
662    newobj = dlg.Show(True)
663    dlg = ExpressionDialog(frm,indepvarDict,newobj,
664                           header="Edit the PseudoVar expression",
665                           fit=False,
666                           depVarDict=PSvarDict,
667                           #VarLabel="New PseudoVar",                           
668                           )
669    newobj = dlg.Show(True)
670    print dlg.GetDepVar()
671    dlg = ExpressionDialog(frm,PSvarDict,
672                           header="Edit the PseudoVar expression",
673                           fit=True)
674    newobj = dlg.Show(True)
675    print dlg.GetDepVar()
676    import sys
677    #sys.exit()
678
679    #app.MainLoop()
680
681
682    import cPickle
683    def showEQ(calcobj):
684        print
685        print calcobj.eObj.expression
686        for v in sorted(calcobj.eObj.freeVars.keys()+calcobj.eObj.assgnVars.keys()):
687            print "  ",v,'=',calcobj.exprDict[v]
688        print calcobj.EvalExpression()
689    print "starting test"
690    obj = G2obj.ExpressionObj()
691    obj.expression = "A*np.exp(B)"
692    obj.assgnVars =  {'B': '0::Afrac:*'}
693    obj.freeVars =  {'A': [u'A', 0.5, True]}
694    obj.CheckVars()
695    parmDict2 = {'0::Afrac:0':1.0, '0::Afrac:1': 1.0}
696    calcobj = G2obj.ExpressionCalcObj(obj)
697    calcobj.SetupCalc(parmDict2)
698    showEQ(calcobj)
699    fp = open('/tmp/obj.pickle','w')
700    cPickle.dump(obj,fp)
701    fp.close()
702   
703    obj.expression = "A*np.exp(-2/B)"
704    obj.assgnVars =  {'A': '0::Afrac:0', 'B': '0::Afrac:1'}
705    obj.freeVars =  {}
706    parmDict1 = {'0::Afrac:0':1.0, '0::Afrac:1': -2.0}
707    calcobj = G2obj.ExpressionCalcObj(obj)
708    calcobj.SetupCalc(parmDict1)
709    showEQ(calcobj)
710
711    fp = open('/tmp/obj.pickle','r')
712    obj = cPickle.load(fp)
713    fp.close()
714    parmDict2 = {'0::Afrac:0':0.0, '0::Afrac:1': 1.0}
715    calcobj = G2obj.ExpressionCalcObj(obj)
716    calcobj.SetupCalc(parmDict2)
717    showEQ(calcobj)
718
719    parmDict2 = {'0::Afrac:0':1.0, '0::Afrac:1': 1.0}
720    calcobj = G2obj.ExpressionCalcObj(obj)
721    calcobj.SetupCalc(parmDict2)
722    showEQ(calcobj)
723   
Note: See TracBrowser for help on using the repository browser.