source: trunk/GSASIIexprGUI.py @ 1413

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

Add config support; default expressions

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