source: trunk/GSASIIexprGUI.py @ 2236

Last change on this file since 2236 was 1770, checked in by vondreele, 10 years ago

moved MultipleChoicesDialog? and SingleChoiceDialog? from G2grid to G2ctrls

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 30.0 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIexprGUI - Expression Definition and Evaluation
3########### SVN repository information ###################
4# $Date: 2015-03-30 18:39:46 +0000 (Mon, 30 Mar 2015) $
5# $Author: vondreele $
6# $Revision: 1770 $
7# $URL: trunk/GSASIIexprGUI.py $
8# $Id: GSASIIexprGUI.py 1770 2015-03-30 18:39:46Z vondreele $
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: 1770 $")
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 KeyError:
176                continue # there were dicts in parmDict (should be gone now)
177            except (TypeError,IndexError):
178                val = parmDict[key]
179            if isinstance(val, basestring): continue
180            try:
181                self.parmDict[key] = float(val)
182            except:
183                pass
184        # separate the variables by type
185        self.parmLists = IndexParmDict(self.parmDict,self.fit)
186        self.timer = wx.Timer()
187        self.timer.Bind(wx.EVT_TIMER,self.OnValidate)
188        LoadDefaultExpressions()
189        style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
190        wx.Dialog.__init__(self, parent, wx.ID_ANY, wintitle, style=style, size=defSize)
191        self.mainsizer = wx.BoxSizer(wx.VERTICAL)
192        label = wx.StaticText(self,  wx.ID_ANY, header)
193        self.mainsizer.Add(label, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
194
195        self.exsizer = wx.BoxSizer(wx.HORIZONTAL)
196        if VarLabel:
197            label = wx.StaticText(self,  wx.ID_ANY, VarLabel + ' = ')
198            self.exsizer.Add(label, 0, wx.ALL|wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 0)
199        elif depVarDict:
200            self.depParmLists = IndexParmDict(self.depVarDict,False)
201            choices = ['','Phase','Hist./Phase','Hist.','Global']
202            for i in range(1,len(choices)): # remove empty menus from choice list
203                if not len(self.depParmLists[i]): choices[i] = ''
204            self.depChoices = [i for i in range(len(choices)) if choices[i]]
205            choice = wx.Choice(
206                self, wx.ID_ANY,
207                choices = [choices[i] for i in self.depChoices]
208                )
209            choice.SetSelection(wx.NOT_FOUND)
210            choice.Bind(wx.EVT_CHOICE,self.OnDepChoice)
211            self.exsizer.Add(choice, 0, wx.ALL|wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 0)
212            self.exsizer.Add((5,5))
213            self.depLabel = wx.StaticText(self,  wx.ID_ANY, ' ')
214            self.exsizer.Add(self.depLabel, 0, wx.ALL|wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 0)
215            label = wx.StaticText(self,  wx.ID_ANY, ' = ')
216            self.exsizer.Add(label, 0, wx.ALL|wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 0)
217
218        #self.exCtrl = wx.TextCtrl(self,  wx.ID_ANY, size=(150,-1),style=wx.TE_PROCESS_ENTER)
219        self.exCtrl = wx.ComboBox(self, wx.ID_ANY, "", (90, 50), (160, -1),
220                                  defaultExpressions,
221                                  wx.CB_DROPDOWN| wx.TE_PROCESS_ENTER
222                         )
223        self.exCtrl.Bind(wx.EVT_CHAR, self.OnChar)
224        self.exCtrl.Bind(wx.EVT_COMBOBOX, self.OnValidate)
225        self.exCtrl.Bind(wx.EVT_TEXT_ENTER, self.OnValidate)
226        self.exsizer.Add(self.exCtrl, 1, wx.ALL|wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 0)
227        #self.mainsizer.Add(self.exCtrl, 0, wx.ALL|wx.EXPAND, 5)
228        self.mainsizer.Add(self.exsizer, 0, wx.ALL|wx.EXPAND, 5)
229        self.mainsizer.Add((-1,5),0,wx.EXPAND,1)
230
231        evalSizer = wx.BoxSizer(wx.HORIZONTAL)
232        self.mainsizer.Add(evalSizer,0,wx.ALL|wx.EXPAND,0)
233        btn = wx.Button(self, wx.ID_ANY,"Validate")
234        btn.Bind(wx.EVT_BUTTON,self.OnValidate)
235        evalSizer.Add(btn,0,wx.LEFT|wx.RIGHT,5)
236        self.result = wx.StaticText(self,  wx.ID_ANY, '')
237        evalSizer.Add(self.result, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
238
239        self.varSizer = wx.BoxSizer(wx.HORIZONTAL)
240        self.mainsizer.Add(self.varSizer,1,wx.ALL|wx.EXPAND,1)
241        self.mainsizer.Add((-1,5),0,wx.EXPAND,1)
242
243        bSizer = wx.BoxSizer(wx.HORIZONTAL)
244        btnsizer = wx.StdDialogButtonSizer()
245        if ExtraButton:
246            self.ExtraBtn = wx.Button(self, wx.ID_ANY, ExtraButton[0])
247            self.ExtraBtn.Bind(wx.EVT_BUTTON,self.OnExtra)
248            self.ExtraCallBack = ExtraButton[1]
249            self.ExtraBtn.Disable()
250            bSizer.Add(self.ExtraBtn, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 2)
251        else:
252            self.ExtraBtn = None
253        bSizer.Add((1,1), 1, wx.ALIGN_CENTER|wx.ALL|wx.EXPAND, 0)
254        self.OKbtn = wx.Button(self, wx.ID_OK)
255        self.OKbtn.SetDefault()
256        self.OKbtn.Disable()
257        btnsizer.AddButton(self.OKbtn)
258        btn = wx.Button(self, wx.ID_CANCEL)
259        btnsizer.AddButton(btn)
260        btnsizer.Realize()
261        bSizer.Add(btnsizer, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
262        self.mainsizer.Add(bSizer, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, 5)
263        self.SetSizer(self.mainsizer)
264        self.CenterOnParent()
265        if exprObj:
266            self.expr = exprObj.EditExpression(
267                self.exprVarLst,
268                self.varSelect,
269                self.varName,
270                self.varValue,
271                self.varRefflag,
272                )
273            # set the initial value for the dependent value
274            if self.depVarDict:
275                var = exprObj.GetDepVar()
276                if var in self.depVarDict:
277                    self.depLabel.SetLabel(var)
278                    self.dependentVar = var
279                   
280        self.exCtrl.SetValue(self.expr)
281        self.OnValidate(None)
282        self.SetMinSize(defSize) 
283        #self.errbox.SetAutoLayout(1)
284        #self.errbox.SetupScrolling()
285        #self.varbox.SetAutoLayout(1)
286        #self.varbox.SetupScrolling()
287        #self.mainsizer.Fit(self)
288
289    def OnExtra(self,event):
290        exprObj = G2obj.ExpressionObj()
291        exprObj.LoadExpression(
292            self.expr,
293            self.exprVarLst,
294            self.varSelect,
295            self.varName,
296            self.varValue,
297            self.varRefflag,
298            )
299        if self.depVarDict:
300            exprObj.SetDepVar(self.dependentVar)
301        self.ExtraCallBack(exprObj)
302        # put results back into displayed dialog
303        resDict = dict(exprObj.GetVariedVarVal())
304        for v,var in self.varName.items():
305            varname = "::" + var.lstrip(':').replace(' ','_').replace(':',';')
306            val =  resDict.get(varname)
307            if val:
308                self.varValue[v] = val
309        wx.CallAfter(self.ShowVars)
310
311    def Show(self,mode=True):
312        '''Call to use the dialog after it is created.
313
314        :returns: None (On Cancel) or a new :class:`~GSASIIobj.ExpressionObj`
315        '''
316        self.Layout()
317        #self.mainsizer.Fit(self)
318        self.SendSizeEvent() # force repaint
319        if self.ShowModal() == wx.ID_OK:
320            # store the edit results in the object and return it
321            exprObj = G2obj.ExpressionObj()
322            exprObj.LoadExpression(
323                self.expr,
324                self.exprVarLst,
325                self.varSelect,
326                self.varName,
327                self.varValue,
328                self.varRefflag,
329                )
330            if self.depVarDict:
331                exprObj.SetDepVar(self.dependentVar)
332            return exprObj
333        else:
334            return None
335       
336    def setEvalResult(self,msg):
337        'Show a string in the expression result area'
338        self.result.SetLabel(msg)
339
340    def RestartTimer(self):
341        '''Cancels any running timer and starts a new one.
342        The timer causes a check of syntax after 2 seconds unless there is further input.
343        Disables the OK button until a validity check is complete.
344        '''
345        if self.timer.IsRunning():
346            self.timer.Stop()
347        self.timer.Start(2000,oneShot=True)
348       
349    def OnChar(self,event):
350        '''Called as each character is entered. Cancels any running timer
351        and starts a new one. The timer causes a check of syntax after 2 seconds
352        without input.
353        Disables the OK button until a validity check is complete.
354        '''
355        self.RestartTimer()
356        self.OKbtn.Disable()
357        if self.ExtraBtn: self.ExtraBtn.Disable()
358        event.Skip()
359        return
360   
361    def CheckVars(self):
362        '''Check that appropriate variables are defined for each
363        symbol used in :data:`self.expr`
364
365        :returns: a text error message or None if all needed input is present       
366        '''
367        invalid = 0
368        for v in self.exprVarLst:
369            if self.varSelect.get(v) is None:
370                invalid += 1
371        if invalid==1:
372            return '(a variable is not assigned)'
373        elif invalid:
374            return '('+str(invalid)+' variables are not assigned)'
375        msg = ''
376        for v in self.exprVarLst:
377            varname = self.varName.get(v)
378            if not varname:
379                invalid += 1
380                if msg: msg += "; "
381                msg += 'No variable for '+str(v)
382            elif self.varSelect.get(v) > 0:
383               if '*' in varname:
384                   l = G2obj.LookupWildCard(varname,self.parmDict.keys())
385                   if len(l) == 0:
386                       invalid += 1
387                       if msg: msg += "; "
388                       msg += 'No variables match '+str(varname)
389               elif varname not in self.parmDict.keys():
390                   invalid += 1
391                   if msg: msg += "; "
392                   msg += 'No variables match '+str(varname)
393            else:
394                # value assignment: this check is likely unneeded
395                val = self.varValue.get(v)
396                try:
397                    float(val)
398                except ValueError,TypeError:
399                    invalid += 1
400                    if msg: msg += "; "
401                    if val is None:
402                        msg += 'No value for '+str(v)
403                    else:
404                        msg += 'Value '+str(val)+' invalid for '+str(v)
405        if invalid:
406            return '('+msg+')'       
407        return
408
409    def ShowVars(self):
410        # create widgets to associate vars with labels and/or show messages
411        self.varSizer.Clear(True)
412        self.errbox = wxscroll.ScrolledPanel(self,style=wx.HSCROLL)
413        self.errbox.SetMinSize((100,130))
414        self.varSizer.Add(self.errbox,0,wx.ALL|wx.EXPAND,1)
415        self.varbox = wxscroll.ScrolledPanel(self,style=wx.HSCROLL)
416        self.varSizer.Add(self.varbox,1,wx.ALL|wx.EXPAND,1)
417        Siz = wx.BoxSizer(wx.VERTICAL)
418        Siz.Add(
419            wx.StaticText(self.varbox,wx.ID_ANY,
420                          'Assignment of variables to labels:'),
421            0,wx.EXPAND|wx.ALIGN_CENTER,0)
422        GridSiz = wx.FlexGridSizer(0,5,2,2)
423        GridSiz.Add(
424            wx.StaticText(self.varbox,wx.ID_ANY,'label',style=wx.CENTER),
425            0,wx.ALIGN_CENTER)
426        lbls = ('varib. type\nselection','variable\nname','value')
427        choices = ['Free','Phase','Hist./Phase','Hist.','Global']
428        if self.fit:
429            lbls += ('refine\nflag',)
430        else:
431            lbls += ('',)
432            choices[0] = ''
433        for i in range(1,len(choices)): # remove empty menus from choice list
434            if not len(self.parmLists[i]): choices[i] = ''
435        self.AllowedChoices = [i for i in range(len(choices)) if choices[i]]
436        for lbl in lbls:
437            w = wx.StaticText(self.varbox,wx.ID_ANY,lbl,style=wx.CENTER)
438            w.SetMinSize((80,-1))
439            GridSiz.Add(w,0,wx.ALIGN_CENTER)
440
441        # show input for each var in expression.
442        for v in self.exprVarLst:
443            # label
444            GridSiz.Add(wx.StaticText(self.varbox,wx.ID_ANY,v),0,wx.ALIGN_CENTER,0)
445            # assignment type
446            ch = wx.Choice(
447                self.varbox, wx.ID_ANY,
448                choices = [choices[i] for i in self.AllowedChoices]
449                )
450            GridSiz.Add(ch,0,wx.ALIGN_LEFT,0)
451            if v in self.varSelect and self.varSelect.get(v) in self.AllowedChoices:
452                i = self.AllowedChoices.index(self.varSelect[v])
453                ch.SetSelection(i)
454            else:
455                ch.SetSelection(wx.NOT_FOUND)
456            ch.label = v
457            ch.Bind(wx.EVT_CHOICE,self.OnChoice)
458
459            # var name/var assignment
460            if self.varSelect.get(v) is None:
461                GridSiz.Add((-1,-1),0,wx.ALIGN_LEFT|wx.EXPAND,0)
462            elif self.varSelect.get(v) == 0:
463                wid = G2G.ValidatedTxtCtrl(self.varbox,self.varName,v,
464                                            #OnLeave=self.OnTxtLeave,
465                                            size=(50,-1))
466                GridSiz.Add(wid,0,wx.ALIGN_LEFT|wx.EXPAND,0)
467            else:
468                wid = wx.StaticText(self.varbox,wx.ID_ANY,self.varName[v])
469                GridSiz.Add(wid,0,wx.ALIGN_LEFT,0)
470
471            # value
472            if self.varSelect.get(v) is None:
473                GridSiz.Add((-1,-1),0,wx.ALIGN_RIGHT|wx.EXPAND,0)
474            elif self.varSelect.get(v) == 0:
475                wid = G2G.ValidatedTxtCtrl(self.varbox,self.varValue,v,
476                                            #OnLeave=self.OnTxtLeave,
477                                            size=(75,-1))
478                GridSiz.Add(wid,0,wx.ALIGN_LEFT|wx.EXPAND,0)
479                wid.Bind(wx.EVT_CHAR,self.OnChar)
480            else:
481                var = self.varName[v]
482                if '*' in var:
483                    #[self.parmDict[v] for v in LookupWildCard(var,self.parmDict.keys())]
484                    #print self.varValue[v]
485                    vs = G2obj.LookupWildCard(var,self.parmDict.keys())
486                    s = '('+str(len(vs))+' values)'
487                elif var in self.parmDict:
488                    val = self.parmDict[var]
489                    s = G2py3.FormatSigFigs(val).rstrip('0')
490                else:
491                    s = '?'
492                wid = wx.StaticText(self.varbox,wx.ID_ANY,s)
493                GridSiz.Add(wid,0,wx.ALIGN_LEFT,0)
494
495            # show a refine flag for Free Vars only
496            if self.varSelect.get(v) == 0 and self.fit:
497                self.varRefflag[v] = self.varRefflag.get(v,True)
498                wid = G2G.G2CheckBox(self.varbox,'',self.varRefflag,v)
499                GridSiz.Add(wid,0,wx.ALIGN_LEFT|wx.EXPAND,0)
500            else:
501                wid = (-1,-1)
502                GridSiz.Add(wid,0,wx.ALIGN_LEFT|wx.EXPAND,0)
503
504        Siz.Add(GridSiz)
505        self.varbox.SetSizer(Siz,True)
506        xwid,yhgt = Siz.Fit(self.varbox)
507        self.varbox.SetMinSize((xwid,130))
508        self.varbox.SetAutoLayout(1)
509        self.varbox.SetupScrolling()
510        self.varbox.Refresh()
511        self.Layout()
512        #self.mainsizer.Fit(self)
513        self.SendSizeEvent() # force repaint
514        return
515
516    def OnDepChoice(self,event):
517        '''Respond to a selection of a variable type for a label in
518        an expression
519        '''
520        sel = self.depChoices[event.GetEventObject().GetSelection()]
521        var = self.SelectG2var(sel,'Dependent variable',self.depParmLists[sel])
522        if var is None:
523            self.dependentVar = None
524            self.OnValidate(None)
525            event.GetEventObject().SetSelection(wx.NOT_FOUND)
526            return
527        self.dependentVar = var
528        self.depLabel.SetLabel(var)
529        self.OnValidate(None)
530        self.Layout()
531
532    def GetDepVar(self):
533        '''Returns the name of the dependent variable, when depVarDict is used.
534        '''
535        return self.dependentVar
536       
537    def OnChoice(self,event):
538        '''Respond to a selection of a variable type for a label in
539        an expression
540        '''
541        v = event.GetEventObject().label
542        sel = self.AllowedChoices[event.GetEventObject().GetSelection()]
543        if sel == 0:
544            sv = G2obj.MakeUniqueLabel(v,self.usedVars)
545            self.varSelect[v] = sel
546            self.varName[v] = sv
547            self.varValue[v] = self.varValue.get(v,0.0)
548        else:
549            var = self.SelectG2var(sel,v,self.parmLists[sel])
550            if var is None:
551                self.OnValidate(None)
552                return
553            self.varSelect[v] = sel
554            self.varName[v] = var
555        self.OnValidate(None)
556
557    def SelectG2var(self,sel,var,parmList):
558        '''Offer a selection of a GSAS-II variable.
559
560        :param int sel: Determines the type of variable to be selected.
561          where 1 is used for Phase variables, and 2 for Histogram/Phase vars,
562          3 for Histogram vars and 4 for Global vars.
563        :returns: a variable name or None (if Cancel is pressed)
564        '''
565        if not parmList:
566            return None
567        l2 = l1 = 1
568        for i in parmList:
569            l1 = max(l1,len(i))
570            loc,desc = G2obj.VarDescr(i)
571            l2 = max(l2,len(loc))
572        fmt = u"{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}"
573        varListlbl = [fmt.format(i,*G2obj.VarDescr(i)) for i in parmList]
574
575        dlg = G2G.G2SingleChoiceDialog(
576            self,'Select GSAS-II variable for '+str(var)+':',
577            'Select variable',
578            varListlbl,monoFont=True)
579        dlg.SetSize((625,250))
580        dlg.CenterOnParent()
581        var = None
582        if dlg.ShowModal() == wx.ID_OK:
583            i = dlg.GetSelection()
584            var = parmList[i]
585        dlg.Destroy()
586        return var
587
588    def showError(self,msg1,msg2='',msg3=''):
589        '''Show an error message of 1 to 3 sections. The second
590        section is shown in an equally-spaced font.
591       
592        :param str msg1: msg1 is shown in a the standard font
593        :param str msg2: msg2 is shown in a equally-spaced (wx.MODERN) font
594        :param str msg3: msg3 is shown in a the standard font
595        '''
596        self.OKbtn.Disable()
597        if self.ExtraBtn: self.ExtraBtn.Disable()
598        self.varSizer.Clear(True)
599        self.errbox = wxscroll.ScrolledPanel(self,style=wx.HSCROLL)
600        self.errbox.SetMinSize((200,130))
601        self.varSizer.Add(self.errbox,1,wx.ALL|wx.EXPAND,1)
602        Siz = wx.BoxSizer(wx.VERTICAL)
603        errMsg1 = wx.StaticText(self.errbox, wx.ID_ANY,"")
604        Siz.Add(errMsg1, 0, wx.ALIGN_LEFT|wx.LEFT|wx.EXPAND, 5)
605        errMsg2 = wx.StaticText(self.errbox, wx.ID_ANY,"\n\n")
606        font1 = wx.Font(errMsg2.GetFont().GetPointSize(),
607                        wx.MODERN, wx.NORMAL, wx.NORMAL, False)
608        errMsg2.SetFont(font1)
609        Siz.Add(errMsg2, 0, wx.ALIGN_LEFT|wx.LEFT|wx.EXPAND, 5)
610        errMsg3 = wx.StaticText(self.errbox, wx.ID_ANY,"")
611        Siz.Add(errMsg3, 0, wx.ALIGN_LEFT|wx.LEFT|wx.EXPAND, 5)
612        self.errbox.SetSizer(Siz,True)
613        Siz.Fit(self.errbox)
614        errMsg1.SetLabel(msg1)
615        errMsg2.SetLabel("  "+msg2)
616        errMsg2.Wrap(-1)
617        errMsg3.SetLabel(msg3)
618        self.Layout()
619
620    def OnValidate(self,event):
621        '''Respond to a press of the Validate button or when a variable
622        is associated with a label (in :meth:`OnChoice`)
623        '''
624        self.setEvalResult('(expression cannot be evaluated)')
625        self.timer.Stop()
626        self.expr = self.exCtrl.GetValue().strip()
627        self.varSizer.Clear(True)
628        if not self.expr: 
629            self.showError(
630                "Invalid Expression:","",
631                "(an expression must be entered)")
632            return
633        exprObj = G2obj.ExpressionObj()
634        ret = exprObj.ParseExpression(self.expr)
635        if not ret:
636            self.showError(*exprObj.lastError)
637            return
638        self.exprVarLst,pkgdict = ret
639        wx.CallAfter(self.Repaint,exprObj)
640           
641    def Repaint(self,exprObj):
642        'Redisplay the variables and continue the validation'
643        self.ShowVars() # show widgets to set vars
644        msg = self.CheckVars() 
645        if msg:
646            self.setEvalResult(msg)
647            return
648        exprObj.LoadExpression(
649            self.expr,
650            self.exprVarLst,
651            self.varSelect,
652            self.varName,
653            self.varValue,
654            self.varRefflag,
655            )
656        try:
657            calcobj = G2obj.ExpressionCalcObj(exprObj)
658            calcobj.SetupCalc(self.parmDict)
659            val = calcobj.EvalExpression()
660        except Exception as msg:
661            self.setEvalResult("Error in evaluation: "+str(msg))
662            return
663        if not np.isfinite(val):
664            self.setEvalResult("Expression value is infinite or out-of-bounds")
665            return
666        s = G2py3.FormatSigFigs(val).rstrip('0')
667        depVal = ""
668        if self.depVarDict:
669            if not self.dependentVar:
670                self.setEvalResult("A dependent variable must be selected.")
671                return
672            depVal = '; Variable "' + self.dependentVar + '" = ' + str(
673                self.depVarDict.get(self.dependentVar,'?')
674                )
675        self.setEvalResult("Expression evaluates to: "+str(s)+depVal)
676        self.OKbtn.Enable()
677        if self.ExtraBtn: self.ExtraBtn.Enable()
678       
679if __name__ == "__main__":
680    app = wx.PySimpleApp() # create the App
681    frm = wx.Frame(None)
682    frm.Show()
683    PSvarDict = {'::a':1.0,'::b':1.1,'0::c':1.2,'::AlongVaraiableName':1000.}
684    #PSvars = PSvarDict.keys()
685    indepvarDict = {'Temperature':1.0,'Pressure':1.1,'Phase of Moon':1.2,'1:1:HAP':1.3}
686    dlg = ExpressionDialog(frm,indepvarDict,
687                           header="Edit the PseudoVar expression",
688                           fit=False,
689                           depVarDict=PSvarDict,
690                           #VarLabel="New PseudoVar",                           
691                           )
692    newobj = dlg.Show(True)
693    dlg = ExpressionDialog(frm,indepvarDict,newobj,
694                           header="Edit the PseudoVar expression",
695                           fit=False,
696                           depVarDict=PSvarDict,
697                           #VarLabel="New PseudoVar",                           
698                           )
699    newobj = dlg.Show(True)
700    print dlg.GetDepVar()
701    dlg = ExpressionDialog(frm,PSvarDict,
702                           header="Edit the PseudoVar expression",
703                           fit=True)
704    newobj = dlg.Show(True)
705    print dlg.GetDepVar()
706    import sys
707    #sys.exit()
708
709    #app.MainLoop()
710
711
712    import cPickle
713    def showEQ(calcobj):
714        print
715        print calcobj.eObj.expression
716        for v in sorted(calcobj.eObj.freeVars.keys()+calcobj.eObj.assgnVars.keys()):
717            print "  ",v,'=',calcobj.exprDict[v]
718        print calcobj.EvalExpression()
719    print "starting test"
720    obj = G2obj.ExpressionObj()
721    obj.expression = "A*np.exp(B)"
722    obj.assgnVars =  {'B': '0::Afrac:*'}
723    obj.freeVars =  {'A': [u'A', 0.5, True]}
724    obj.CheckVars()
725    parmDict2 = {'0::Afrac:0':1.0, '0::Afrac:1': 1.0}
726    calcobj = G2obj.ExpressionCalcObj(obj)
727    calcobj.SetupCalc(parmDict2)
728    showEQ(calcobj)
729    fp = open('/tmp/obj.pickle','w')
730    cPickle.dump(obj,fp)
731    fp.close()
732   
733    obj.expression = "A*np.exp(-2/B)"
734    obj.assgnVars =  {'A': '0::Afrac:0', 'B': '0::Afrac:1'}
735    obj.freeVars =  {}
736    parmDict1 = {'0::Afrac:0':1.0, '0::Afrac:1': -2.0}
737    calcobj = G2obj.ExpressionCalcObj(obj)
738    calcobj.SetupCalc(parmDict1)
739    showEQ(calcobj)
740
741    fp = open('/tmp/obj.pickle','r')
742    obj = cPickle.load(fp)
743    fp.close()
744    parmDict2 = {'0::Afrac:0':0.0, '0::Afrac:1': 1.0}
745    calcobj = G2obj.ExpressionCalcObj(obj)
746    calcobj.SetupCalc(parmDict2)
747    showEQ(calcobj)
748
749    parmDict2 = {'0::Afrac:0':1.0, '0::Afrac:1': 1.0}
750    calcobj = G2obj.ExpressionCalcObj(obj)
751    calcobj.SetupCalc(parmDict2)
752    showEQ(calcobj)
753   
Note: See TracBrowser for help on using the repository browser.