source: trunk/GSASIIexprGUI.py @ 1619

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

add columnsorter for hetrogeneous seq ref; reorg to start moving widgets out of g2grid

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