Source code for GSASIIexprGUI

# -*- coding: utf-8 -*-
#GSASIIexprGUI - Expression Definition and Evaluation
########### SVN repository information ###################
# $Date: 2020-12-14 18:58:19 +0000 (Mon, 14 Dec 2020) $
# $Author: vondreele $
# $Revision: 4672 $
# $URL: https://subversion.xray.aps.anl.gov/pyGSAS/trunk/GSASIIexprGUI.py $
# $Id: GSASIIexprGUI.py 4672 2020-12-14 18:58:19Z vondreele $
########### SVN repository information ###################
'''
*GSASIIexprGUI: Expression Handling*
-------------------------------------

This module defines a class for defining an expression in terms of values
in a parameter dictionary via a wx.Dialog. The dialog creates a :class:`GSASII.ExpressionObj`
which is used to evaluate the expression against a supplied parameter dictionary.

The expression is parsed to find variables used in the expression and then
the user is asked to assign parameters from the dictionary to each variable.

Default expressions are read from file DefaultExpressions.txt using
:func:`GSASIIpath.LoadConfigFile`.

'''
from __future__ import division, print_function
import re
import platform
import wx
import wx.lib.scrolledpanel as wxscroll
import numpy as np
import GSASIIpath
GSASIIpath.SetVersionNumber("$Revision: 4672 $")
import GSASIIctrlGUI as G2G
import GSASIIpy3 as G2py3
import GSASIIobj as G2obj
import GSASIImath as G2mth

# Define a short name for convenience
WACV = wx.ALIGN_CENTER_VERTICAL

[docs]def IndexParmDict(parmDict,wildcard): '''Separate the parameters in parmDict into list of keys by parameter type. :param dict parmDict: a dict with GSAS-II parameters :param bool wildcard: True if wildcard versions of parameters should be generated and added to the lists :returns: a dict of lists where key 1 is a list of phase parameters, 2 is histogram/phase parms, 3 is histogram parms and 4 are global parameters ''' prex = re.compile('[0-9]+::.*') hrex = re.compile(':[0-9]+:.*') parmLists = {} for i in (1,2,3,4): parmLists[i] = [] for i in sorted(parmDict.keys()): if i.startswith("::") or i.find(':') == -1: # globals parmLists[4].append(i) elif prex.match(i): parmLists[1].append(i) elif hrex.match(i): parmLists[3].append(i) else: parmLists[2].append(i) if wildcard: for i in (1,2,3,4): parmLists[i] += G2obj.GenWildCard(parmLists[i]) # generate wildcard versions for i in (1,2,3,4): parmLists[i].sort() return parmLists
#========================================================================== defaultExpressions = None
[docs]def LoadDefaultExpressions(): '''Read a configuration file with default expressions from all files named DefaultExpressions.txt found in the path. Duplicates are removed and expressions are sorted alphabetically ''' global defaultExpressions if defaultExpressions is not None: return # run this routine only once defaultExpressions = sorted(list(set(GSASIIpath.LoadConfigFile('DefaultExpressions.txt'))))
#==========================================================================
[docs]class ExpressionDialog(wx.Dialog): '''A wx.Dialog that allows a user to input an arbitrary expression to be evaluated and possibly minimized. To do this, the user assigns a new (free) or existing GSAS-II parameter to each parameter label used in the expression. The free parameters can optionally be designated to be refined. For example, is an expression is used such as:: 'A*np.exp(-B/C)' then A, B and C can each be assigned as Free parameter with a user-selected value or to any existing GSAS-II variable in the parameter dictionary. As the expression is entered it is checked for validity. After the :class:`ExpressionDialog` object is created, use :meth:`Show` to run it and obtain a :class:`GSASIIobj.ExpressionObj` object with the user input. :param wx.Frame parent: The parent of the Dialog. Can be None, but better is to provide the name of the Frame where the dialog is called. :param dict parmDict: a dict with defined parameters and their values. Each value may be a list with parameter values and a refine flag or may just contain the parameter value (non-float/int values in dict are ignored) :param exprObj: a :class:`GSASIIobj.ExpressionObj` object with an expression and label assignments or None (default) :param str wintitle: String placed on title bar of dialog; defaults to "Expression Editor" :param str header: String placed at top of dialog to tell the user what they will do here; default is "Enter restraint expression here" :param bool fit: determines if the expression will be used in fitting (default=True). If set to False, refinement flags are not shown and Free parameters are not offered as an assignment option. :param str VarLabel: an optional variable label to include before the expression input. Ignored if None (default) :param list depVarDict: a dict of choices for the dependent variable to be fitted to the expression and their values. Ignored if None (default). :param list ExtraButton: a list with two terms that define [0]: the label for an extra button and [1] the callback routine to be used when the button is pressed. The button will only be enabled when the OK button can be used (meaning the equation/expression is valid). The default is None, meaning this will not be used. :param list usedVars: defines a list of previously used variable names. These names will not be reused as defaults for new free variables. (The default is an empty list). :param bool wildCard: If True (default), histogram names are converted to wildcard values, as is appropriate for the sequential refinement table ''' def __init__(self, parent, parmDict, exprObj=None, header='Enter restraint expression here', wintitle='Expression Editor', fit=True,VarLabel=None,depVarDict=None, ExtraButton=None,usedVars=[], wildCard=True): self.fit = fit self.depVarDict = depVarDict 'dict for dependent variables' self.parmDict = {} '''A copy of the G2 parameter dict (parmDict) except only numerical values are included and only the value (not the vary flag, if present) is included. ''' self.exprVarLst = [] '''A list containing the variables utilized in the current expression. Placed into a :class:`GSASIIobj.ExpressionObj` object when the dialog is closed with "OK", saving any changes. ''' self.varSelect = {} '''A dict that shows the variable type for each label found in the expression. * If the value is None or is not defined, the value is not assigned. * If the value is 0, then the varible is "free" -- a new refineable parameter. * Values above 1 determine what variables will be shown when the option is selected. ''' self.varName = {} 'Name assigned to each variable' self.varValue = {} 'Value for a variable (Free parameters only)' self.varRefflag = {} 'Refinement flag for a variable (Free parameters only)' self.expr = '' 'Expression as a text string' self.dependentVar = None 'name for dependent variable selection, when depVarDict is specified' self.usedVars = usedVars 'variable names that have been used and should not be reused by default' self.wildCard = wildCard defSize = (620,340) # seems like a good size 'Starting size for dialog' # process dictionary of values and create an index for key in parmDict: try: # deal with values that are in lists val = parmDict[key][0] except KeyError: continue # there were dicts in parmDict (should be gone now) except (TypeError,IndexError): val = parmDict[key] if '2' in platform.python_version_tuple()[0]: basestr = basestring else: basestr = str if isinstance(val, basestr): continue try: self.parmDict[key] = float(val) except: pass # separate the variables by type self.parmLists = IndexParmDict(self.parmDict,self.fit) self.timer = wx.Timer() self.timer.Bind(wx.EVT_TIMER,self.OnValidate) LoadDefaultExpressions() style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER wx.Dialog.__init__(self, parent, wx.ID_ANY, wintitle, style=style, size=defSize) self.mainsizer = wx.BoxSizer(wx.VERTICAL) label = wx.StaticText(self, wx.ID_ANY, header) self.mainsizer.Add(label, 0, wx.ALIGN_CENTRE|wx.ALL, 5) self.exsizer = wx.BoxSizer(wx.HORIZONTAL) if VarLabel: label = wx.StaticText(self, wx.ID_ANY, VarLabel + ' = ') self.exsizer.Add(label, 0, wx.ALL|wx.EXPAND, 0) elif depVarDict: self.depParmLists = IndexParmDict(self.depVarDict,False) choices = ['','Phase','Hist./Phase','Hist.','Global'] for i in range(1,len(choices)): # remove empty menus from choice list if not len(self.depParmLists[i]): choices[i] = '' self.depChoices = [i for i in range(len(choices)) if choices[i]] choice = wx.Choice( self, wx.ID_ANY, choices = [choices[i] for i in self.depChoices] ) choice.SetSelection(wx.NOT_FOUND) choice.Bind(wx.EVT_CHOICE,self.OnDepChoice) self.exsizer.Add(choice, 0, wx.ALL|wx.EXPAND, 0) self.exsizer.Add((5,5)) self.depLabel = wx.StaticText(self, wx.ID_ANY, ' ') self.exsizer.Add(self.depLabel, 0, wx.ALL|wx.EXPAND, 0) label = wx.StaticText(self, wx.ID_ANY, ' = ') self.exsizer.Add(label, 0, wx.ALL|wx.EXPAND, 0) #self.exCtrl = wx.TextCtrl(self, wx.ID_ANY, size=(150,-1),style=wx.TE_PROCESS_ENTER) self.exCtrl = wx.ComboBox(self, wx.ID_ANY, "", (90, 50), (160, -1), defaultExpressions,wx.CB_DROPDOWN| wx.TE_PROCESS_ENTER) self.exCtrl.Bind(wx.EVT_CHAR, self.OnChar) self.exCtrl.Bind(wx.EVT_COMBOBOX, self.OnValidate) self.exCtrl.Bind(wx.EVT_TEXT_ENTER, self.OnValidate) self.exsizer.Add(self.exCtrl, 1, wx.ALL|wx.EXPAND, 0) #self.mainsizer.Add(self.exCtrl, 0, wx.ALL|wx.EXPAND, 5) self.mainsizer.Add(self.exsizer, 0, wx.ALL|wx.EXPAND, 5) self.mainsizer.Add((-1,5),0,wx.EXPAND,1) evalSizer = wx.BoxSizer(wx.HORIZONTAL) self.mainsizer.Add(evalSizer,0,wx.ALL|wx.EXPAND,0) btn = wx.Button(self, wx.ID_ANY,"Validate") btn.Bind(wx.EVT_BUTTON,self.OnValidate) evalSizer.Add(btn,0,wx.LEFT|wx.RIGHT,5) self.result = wx.StaticText(self, wx.ID_ANY, '') evalSizer.Add(self.result, 0, wx.ALIGN_CENTRE|wx.ALL, 5) self.varSizer = wx.BoxSizer(wx.HORIZONTAL) self.mainsizer.Add(self.varSizer,1,wx.ALL|wx.EXPAND,1) self.mainsizer.Add((-1,5),0,wx.EXPAND,1) bSizer = wx.BoxSizer(wx.HORIZONTAL) btnsizer = wx.StdDialogButtonSizer() if ExtraButton: self.ExtraBtn = wx.Button(self, wx.ID_ANY, ExtraButton[0]) self.ExtraBtn.Bind(wx.EVT_BUTTON,self.OnExtra) self.ExtraCallBack = ExtraButton[1] self.ExtraBtn.Disable() bSizer.Add(self.ExtraBtn, 0, wx.ALL|WACV, 2) else: self.ExtraBtn = None bSizer.Add((1,1), 1, wx.ALL|wx.EXPAND, 0) self.OKbtn = wx.Button(self, wx.ID_OK) self.OKbtn.SetDefault() self.OKbtn.Disable() btnsizer.AddButton(self.OKbtn) btn = wx.Button(self, wx.ID_CANCEL) btnsizer.AddButton(btn) btnsizer.Realize() bSizer.Add(btnsizer, 0, WACV|wx.ALL, 5) self.mainsizer.Add(bSizer, 0, wx.ALL|wx.EXPAND, 5) self.SetSizer(self.mainsizer) self.CenterOnParent() if exprObj: self.expr = exprObj.EditExpression( self.exprVarLst, self.varSelect, self.varName, self.varValue, self.varRefflag, ) # set the initial value for the dependent value if self.depVarDict: var = exprObj.GetDepVar() if var in self.depVarDict: self.depLabel.SetLabel(var) self.dependentVar = var self.exCtrl.SetValue(self.expr) self.OnValidate(None) self.SetMinSize(defSize) #self.errbox.SetAutoLayout(1) #self.errbox.SetupScrolling() #self.varbox.SetAutoLayout(1) #self.varbox.SetupScrolling() #self.mainsizer.Fit(self) def OnExtra(self,event): exprObj = G2obj.ExpressionObj() exprObj.LoadExpression( self.expr, self.exprVarLst, self.varSelect, self.varName, self.varValue, self.varRefflag, ) if self.depVarDict: exprObj.SetDepVar(self.dependentVar) self.ExtraCallBack(exprObj) # put results back into displayed dialog resDict = dict(exprObj.GetVariedVarVal()) for v,var in self.varName.items(): varname = "::" + var.lstrip(':').replace(' ','_').replace(':',';') val = resDict.get(varname) if val: self.varValue[v] = val wx.CallLater(100,self.Repaint,exprObj)
[docs] def Show(self,mode=True): '''Call to use the dialog after it is created. :returns: None (On Cancel) or a new :class:`~GSASIIobj.ExpressionObj` ''' self.Layout() #self.mainsizer.Fit(self) self.SendSizeEvent() # force repaint if self.ShowModal() == wx.ID_OK: # store the edit results in the object and return it exprObj = G2obj.ExpressionObj() exprObj.LoadExpression( self.expr, self.exprVarLst, self.varSelect, self.varName, self.varValue, self.varRefflag, ) if self.depVarDict: exprObj.SetDepVar(self.dependentVar) return exprObj else: return None
[docs] def setEvalResult(self,msg): 'Show a string in the expression result area' self.result.SetLabel(msg)
[docs] def RestartTimer(self): '''Cancels any running timer and starts a new one. The timer causes a check of syntax after 2 seconds unless there is further input. Disables the OK button until a validity check is complete. ''' if self.timer.IsRunning(): self.timer.Stop() self.timer.Start(2000,oneShot=True)
[docs] def OnChar(self,event): '''Called as each character is entered. Cancels any running timer and starts a new one. The timer causes a check of syntax after 2 seconds without input. Disables the OK button until a validity check is complete. ''' self.RestartTimer() self.OKbtn.Disable() if self.ExtraBtn: self.ExtraBtn.Disable() event.Skip() return
[docs] def CheckVars(self): '''Check that appropriate variables are defined for each symbol used in :data:`self.expr` :returns: a text error message or None if all needed input is present ''' invalid = 0 for v in self.exprVarLst: if self.varSelect.get(v) is None: invalid += 1 if invalid==1: return '(a variable is not assigned)' elif invalid: return '('+str(invalid)+' variables are not assigned)' msg = '' for v in self.exprVarLst: varname = self.varName.get(v) if not varname: invalid += 1 if msg: msg += "; " msg += 'No variable for '+str(v) elif self.varSelect.get(v,0) > 0: if varname in self.parmDict: pass elif '*' in varname: l = G2obj.LookupWildCard(varname,list(self.parmDict.keys())) if len(l) == 0: invalid += 1 if msg: msg += "; " msg += 'No variables match '+str(varname) elif varname not in self.parmDict: invalid += 1 if msg: msg += "; " msg += 'No variables match '+str(varname) else: # value assignment: this check is likely unneeded val = self.varValue.get(v) try: float(val) except (ValueError,TypeError): invalid += 1 if msg: msg += "; " if val is None: msg += 'No value for '+str(v) else: msg += 'Value '+str(val)+' invalid for '+str(v) if invalid: return '('+msg+')' return
[docs] def OnDepChoice(self,event): '''Respond to a selection of a variable type for a label in an expression ''' if event: event.Skip() sel = self.depChoices[event.GetEventObject().GetSelection()] var = self.SelectG2var(sel,'Dependent variable',self.depParmLists[sel]) if var is None: self.dependentVar = None self.OnValidate(None) event.GetEventObject().SetSelection(wx.NOT_FOUND) return self.dependentVar = var self.depLabel.SetLabel(var) self.OnValidate(None) self.Layout()
[docs] def GetDepVar(self): '''Returns the name of the dependent variable, when depVarDict is used. ''' return self.dependentVar
[docs] def OnChoice(self,event): '''Respond to a selection of a variable type for a label in an expression ''' if event: event.Skip() v = event.GetEventObject().label sel = self.AllowedChoices[event.GetEventObject().GetSelection()] if sel == 0: sv = G2obj.MakeUniqueLabel(v,self.usedVars) self.varSelect[v] = sel self.varName[v] = sv self.varValue[v] = self.varValue.get(v,0.0) else: var = self.SelectG2var(sel,v,self.parmLists[sel]) if var is None: self.OnValidate(None) return self.varSelect[v] = sel self.varName[v] = var self.OnValidate(None)
[docs] def SelectG2var(self,sel,var,parmList): '''Offer a selection of a GSAS-II variable. :param int sel: Determines the type of variable to be selected. where 1 is used for Phase variables, and 2 for Histogram/Phase vars, 3 for Histogram vars and 4 for Global vars. :returns: a variable name or None (if Cancel is pressed) ''' def wildHist(var): '''Replace a histogram number with a wild card ''' slst = var.split(':') if len(slst) > 2 and slst[1]: slst[1] = '*' return ':'.join(slst) return var if not parmList: return None l2 = l1 = 1 if self.wildCard: wildList = [wildHist(i) for i in parmList] else: wildList = parmList for i in wildList: l1 = max(l1,len(i)) loc,desc = G2obj.VarDescr(i) l2 = max(l2,len(loc)) fmt = u"{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}" varListlbl = [fmt.format(i,*G2obj.VarDescr(i)) for i in wildList] dlg = G2G.G2SingleChoiceDialog( self,'Select GSAS-II variable for '+str(var)+':', 'Select variable', varListlbl,monoFont=True) dlg.SetSize((625,250)) dlg.CenterOnParent() var = None if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() var = wildList[i] dlg.Destroy() return var
[docs] def showError(self,msg1,msg2='',msg3=''): '''Show an error message of 1 to 3 sections. The second section is shown in an equally-spaced font. :param str msg1: msg1 is shown in a the standard font :param str msg2: msg2 is shown in a equally-spaced (wx.MODERN) font :param str msg3: msg3 is shown in a the standard font ''' self.varSizer.Clear(True) self.OKbtn.Disable() if self.ExtraBtn: self.ExtraBtn.Disable() self.varSizer.Clear(True) self.errbox = wxscroll.ScrolledPanel(self,style=wx.HSCROLL) self.errbox.SetMinSize((200,130)) self.varSizer.Add(self.errbox,1,wx.ALL|wx.EXPAND,1) Siz = wx.BoxSizer(wx.VERTICAL) errMsg1 = wx.StaticText(self.errbox, wx.ID_ANY,"") Siz.Add(errMsg1, 0, wx.LEFT|wx.EXPAND, 5) errMsg2 = wx.StaticText(self.errbox, wx.ID_ANY,"\n\n") font1 = wx.Font(errMsg2.GetFont().GetPointSize(), wx.MODERN, wx.NORMAL, wx.NORMAL, False) errMsg2.SetFont(font1) Siz.Add(errMsg2, 0, wx.LEFT|wx.EXPAND, 5) errMsg3 = wx.StaticText(self.errbox, wx.ID_ANY,"") Siz.Add(errMsg3, 0, wx.LEFT|wx.EXPAND, 5) self.errbox.SetSizer(Siz,True) Siz.Fit(self.errbox) errMsg1.SetLabel(msg1) errMsg2.SetLabel(" "+msg2) errMsg2.Wrap(-1) errMsg3.SetLabel(msg3) self.Layout()
[docs] def OnValidate(self,event): '''Respond to a press of the Validate button or when a variable is associated with a label (in :meth:`OnChoice`) ''' if event: event.Skip() self.setEvalResult('(expression cannot be evaluated)') self.timer.Stop() self.expr = self.exCtrl.GetValue().strip() if not self.expr: wx.CallAfter(self.showError, "Invalid Expression:",""," (an expression must be entered)") return exprObj = G2obj.ExpressionObj() ret = exprObj.ParseExpression(self.expr) if not ret: wx.CallAfter(self.showError,*exprObj.lastError) return self.exprVarLst,pkgdict = ret wx.CallLater(100,self.Repaint,exprObj)
[docs] def Repaint(self,exprObj): 'Redisplay the variables and continue the validation' # create widgets to associate vars with labels and/or show messages self.varSizer.Clear(True) self.errbox = wxscroll.ScrolledPanel(self,style=wx.HSCROLL) self.errbox.SetMinSize((100,130)) self.varSizer.Add(self.errbox,0,wx.ALL|wx.EXPAND,1) self.varbox = wxscroll.ScrolledPanel(self,style=wx.HSCROLL) self.varSizer.Add(self.varbox,1,wx.ALL|wx.EXPAND,1) Siz = wx.BoxSizer(wx.VERTICAL) Siz.Add( wx.StaticText(self.varbox,wx.ID_ANY, 'Assignment of variables to labels:'), 0,wx.EXPAND,0) GridSiz = wx.FlexGridSizer(0,5,2,2) GridSiz.Add( wx.StaticText(self.varbox,wx.ID_ANY,'label',style=wx.CENTER), 0,wx.ALIGN_CENTER) lbls = ('varib. type\nselection','variable\nname','value') choices = ['Free','Phase','Hist./Phase','Hist.','Global'] if self.fit: lbls += ('refine\nflag',) else: lbls += ('',) choices[0] = '' for i in range(1,len(choices)): # remove empty menus from choice list if not len(self.parmLists[i]): choices[i] = '' self.AllowedChoices = [i for i in range(len(choices)) if choices[i]] for lbl in lbls: w = wx.StaticText(self.varbox,wx.ID_ANY,lbl,style=wx.CENTER) w.SetMinSize((80,-1)) GridSiz.Add(w,0,wx.ALIGN_CENTER) # show input for each var in expression. for v in self.exprVarLst: # label GridSiz.Add(wx.StaticText(self.varbox,wx.ID_ANY,v),0,wx.ALIGN_CENTER,0) # assignment type ch = wx.Choice( self.varbox, wx.ID_ANY, choices = [choices[i] for i in self.AllowedChoices] ) GridSiz.Add(ch,0,wx.ALIGN_LEFT,0) if v in self.varSelect and self.varSelect.get(v) in self.AllowedChoices: i = self.AllowedChoices.index(self.varSelect[v]) ch.SetSelection(i) else: ch.SetSelection(wx.NOT_FOUND) ch.label = v ch.Bind(wx.EVT_CHOICE,self.OnChoice) # var name/var assignment if self.varSelect.get(v) is None: GridSiz.Add((-1,-1),0,wx.EXPAND,0) elif self.varSelect.get(v) == 0: wid = G2G.ValidatedTxtCtrl(self.varbox,self.varName,v,size=(50,-1)) GridSiz.Add(wid,0,wx.EXPAND,0) else: wid = wx.StaticText(self.varbox,wx.ID_ANY,self.varName[v]) GridSiz.Add(wid,0,wx.ALIGN_LEFT,0) # value if self.varSelect.get(v) is None: GridSiz.Add((-1,-1),0,wx.EXPAND,0) elif self.varSelect.get(v) == 0: wid = G2G.ValidatedTxtCtrl(self.varbox,self.varValue,v,size=(75,-1)) GridSiz.Add(wid,0,wx.EXPAND,0) wid.Bind(wx.EVT_CHAR,self.OnChar) else: var = self.varName[v] if var in self.parmDict: val = self.parmDict[var] s = G2py3.FormatSigFigs(val).rstrip('0') elif '*' in var: vs = G2obj.LookupWildCard(var,self.parmDict.keys()) s = '('+str(len(vs))+' values)' else: s = '?' wid = wx.StaticText(self.varbox,wx.ID_ANY,s) GridSiz.Add(wid,0,wx.ALIGN_LEFT,0) # show a refine flag for Free Vars only if self.varSelect.get(v) == 0 and self.fit: self.varRefflag[v] = self.varRefflag.get(v,True) wid = G2G.G2CheckBox(self.varbox,'',self.varRefflag,v) GridSiz.Add(wid,0,wx.EXPAND,0) else: wid = (-1,-1) GridSiz.Add(wid,0,wx.EXPAND,0) Siz.Add(GridSiz) self.varbox.SetSizer(Siz,True) # evaluate the expression, displaying errors or the expression value try: msg = self.CheckVars() if msg: self.setEvalResult(msg) return exprObj.LoadExpression( self.expr, self.exprVarLst, self.varSelect, self.varName, self.varValue, self.varRefflag, ) try: calcobj = G2obj.ExpressionCalcObj(exprObj) calcobj.SetupCalc(self.parmDict) val = calcobj.EvalExpression() except Exception as msg: self.setEvalResult("Error in evaluation: "+str(msg)) return if not np.isfinite(val): self.setEvalResult("Expression value is infinite or out-of-bounds") return s = G2py3.FormatSigFigs(val).rstrip('0') depVal = "" if self.depVarDict: if not self.dependentVar: self.setEvalResult("A dependent variable must be selected.") return depVal = '; Variable "' + self.dependentVar + '" = ' + str( self.depVarDict.get(self.dependentVar,'?') ) self.setEvalResult("Expression evaluates to: "+str(s)+depVal) self.OKbtn.Enable() if self.ExtraBtn: self.ExtraBtn.Enable() finally: xwid,yhgt = Siz.Fit(self.varbox) self.varbox.SetMinSize((xwid,130)) self.varbox.SetAutoLayout(1) self.varbox.SetupScrolling() self.varbox.Refresh() self.Layout() self.SendSizeEvent() # force repaint
#==========================================================================
[docs]class BondDialog(wx.Dialog): '''A wx.Dialog that allows a user to select a bond length to be evaluated. What needs to be done here? Need phase info for atoms 0. Select phase 1. Select 1st atom from dialog 2. Find neighbors & select one from dialog 3. Set up distance equation & save it - has to look like result from Show in above ExpressionDialog Use existing bond & esd calculate routines ''' def __init__(self, parent, Phases, parmDict, exprObj=None, header='Enter restraint expression here', wintitle='Expression Editor', VarLabel=None,depVarDict=None, ExtraButton=None,usedVars=[]): wx.Dialog.__init__(self,parent,wx.ID_ANY,wintitle, pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE) self.panel = wx.Panel(self) #just a dummy - gets destroyed in Draw! self.Phases = Phases self.parmDict = parmDict self.header = header self.pName = list(Phases.keys())[0] DisAglCtls = {} dlg = G2G.DisAglDialog(self.panel,DisAglCtls,self.Phases[self.pName]['General'],Reset=False) if dlg.ShowModal() == wx.ID_OK: Phases[self.pName]['General']['DisAglCtls'] = dlg.GetData() dlg.Destroy() self.Oatom = '' self.Tatom = '' self.Draw() def Draw(self): def OnPhase(event): Obj = event.GetEventObject() self.pName = Obj.GetValue() self.Oatom = '' DisAglCtls = {} dlg = G2G.DisAglDialog(self.panel,DisAglCtls,self.Phases[self.pName]['General'],Reset=False) if dlg.ShowModal() == wx.ID_OK: self.Phases[self.pName]['General']['DisAglCtls'] = dlg.GetData() dlg.Destroy() wx.CallAfter(self.Draw) def OnOrigAtom(event): Obj = event.GetEventObject() self.Oatom = Obj.GetValue() wx.CallAfter(self.Draw) def OnTargAtom(event): Obj = event.GetEventObject() self.Tatom = Obj.GetValue() wx.CallAfter(self.Draw) self.panel.Destroy() self.panel = wx.Panel(self) mainSizer = wx.BoxSizer(wx.VERTICAL) mainSizer.Add(wx.StaticText(self.panel,label=self.header),0) pNames = list(self.Phases.keys()) phaseSizer = wx.BoxSizer(wx.HORIZONTAL) phaseSizer.Add(wx.StaticText(self.panel,label=' Select phase: '),0,WACV) phase = wx.ComboBox(self.panel,value=self.pName,choices=pNames, style=wx.CB_READONLY|wx.CB_DROPDOWN) phase.Bind(wx.EVT_COMBOBOX,OnPhase) phaseSizer.Add(phase,0,WACV) mainSizer.Add(phaseSizer) Phase = self.Phases[self.pName] cx,ct = Phase['General']['AtomPtrs'][:2] Atoms = Phase['Atoms'] aNames = [atom[ct-1] for atom in Atoms] atomSizer = wx.BoxSizer(wx.HORIZONTAL) atomSizer.Add(wx.StaticText(self.panel,label=' Origin atom: '),0,WACV) origAtom = wx.ComboBox(self.panel,value=self.Oatom,choices=aNames, style=wx.CB_READONLY|wx.CB_DROPDOWN) origAtom.Bind(wx.EVT_COMBOBOX,OnOrigAtom) atomSizer.Add(origAtom,0,WACV) atomSizer.Add(wx.StaticText(self.panel,label=' distance to: '),0,WACV) neigh = [] if self.Oatom: neigh = G2mth.FindAllNeighbors(Phase,self.Oatom,aNames) bNames = ['',] if neigh: bNames = [item[0]+' d=%.3f'%(item[2]) for item in neigh[0]] targAtom = wx.ComboBox(self.panel,value=self.Tatom,choices=bNames, style=wx.CB_READONLY|wx.CB_DROPDOWN) targAtom.Bind(wx.EVT_COMBOBOX,OnTargAtom) atomSizer.Add(targAtom,0,WACV) mainSizer.Add(atomSizer) OkBtn = wx.Button(self.panel,-1,"Ok") OkBtn.Bind(wx.EVT_BUTTON, self.OnOk) cancelBtn = wx.Button(self.panel,-1,"Cancel") cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel) btnSizer = wx.BoxSizer(wx.HORIZONTAL) btnSizer.Add((20,20),1) btnSizer.Add(OkBtn) btnSizer.Add((20,20),1) btnSizer.Add(cancelBtn) btnSizer.Add((20,20),1) mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10) self.panel.SetSizer(mainSizer) self.panel.Fit() self.Fit() def GetSelection(self): return self.pName,self.Oatom,self.Tatom def OnOk(self,event): parent = self.GetParent() parent.Raise() self.EndModal(wx.ID_OK) def OnCancel(self,event): parent = self.GetParent() parent.Raise() self.EndModal(wx.ID_CANCEL)
#==========================================================================
[docs]class AngleDialog(wx.Dialog): '''A wx.Dialog that allows a user to select a bond angle to be evaluated. What needs to be done here? Need phase info for atom 0. Select phase 1. Select 1st atom from dialog 2. Find neighbors & select two from dialog 3. Set up angle equation & save it - has to look like result from Show in above ExpressionDialog Use existing angle & esd calculate routines ''' def __init__(self, parent, Phases, parmDict, exprObj=None, header='Enter restraint expression here', wintitle='Expression Editor', VarLabel=None,depVarDict=None, ExtraButton=None,usedVars=[]): wx.Dialog.__init__(self,parent,wx.ID_ANY,wintitle, pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE) self.panel = wx.Panel(self) #just a dummy - gets destroyed in Draw! self.Phases = Phases self.parmDict = parmDict self.header = header self.pName = list(Phases.keys())[0] DisAglCtls = {} dlg = G2G.DisAglDialog(self.panel,DisAglCtls,self.Phases[self.pName]['General'],Reset=False) if dlg.ShowModal() == wx.ID_OK: Phases[self.pName]['General']['DisAglCtls'] = dlg.GetData() dlg.Destroy() self.Oatom = '' self.Tatoms = '' self.Draw() def Draw(self): def OnPhase(event): Obj = event.GetEventObject() self.pName = Obj.GetValue() self.Oatom = '' DisAglCtls = {} dlg = G2G.DisAglDialog(self.panel,DisAglCtls,self.Phases[self.pName]['General'],Reset=False) if dlg.ShowModal() == wx.ID_OK: self.Phases[self.pName]['General']['DisAglCtls'] = dlg.GetData() dlg.Destroy() wx.CallAfter(self.Draw) def OnOrigAtom(event): Obj = event.GetEventObject() self.Oatom = Obj.GetValue() wx.CallAfter(self.Draw) def OnTargAtoms(event): Obj = event.GetEventObject() self.Tatoms = Obj.GetValue() wx.CallAfter(self.Draw) self.panel.Destroy() self.panel = wx.Panel(self) mainSizer = wx.BoxSizer(wx.VERTICAL) mainSizer.Add(wx.StaticText(self.panel,label=self.header),0,WACV) pNames = list(self.Phases.keys()) phaseSizer = wx.BoxSizer(wx.HORIZONTAL) phaseSizer.Add(wx.StaticText(self.panel,label=' Select phase: '),0,WACV) phase = wx.ComboBox(self.panel,value=self.pName,choices=pNames, style=wx.CB_READONLY|wx.CB_DROPDOWN) phase.Bind(wx.EVT_COMBOBOX,OnPhase) phaseSizer.Add(phase,0,WACV) mainSizer.Add(phaseSizer) Phase = self.Phases[self.pName] cx,ct = Phase['General']['AtomPtrs'][:2] Atoms = Phase['Atoms'] aNames = [atom[ct-1] for atom in Atoms] atomSizer = wx.BoxSizer(wx.HORIZONTAL) atomSizer.Add(wx.StaticText(self.panel,label=' Origin atom (O in A-O-B): '),0,WACV) origAtom = wx.ComboBox(self.panel,value=self.Oatom,choices=aNames, style=wx.CB_READONLY|wx.CB_DROPDOWN) origAtom.Bind(wx.EVT_COMBOBOX,OnOrigAtom) atomSizer.Add(origAtom,0,WACV) mainSizer.Add(atomSizer) neigh = [] if self.Oatom: neigh = G2mth.FindAllNeighbors(Phase,self.Oatom,aNames)[0] mainSizer.Add(wx.StaticText(self.panel,label=' A-O-B angle for A,B: '),0) bNames = ['',] if neigh: for iA,aName in enumerate(neigh): for cName in neigh[iA+1:]: bNames.append('%s;%s'%(aName[0].replace(' ',''),cName[0].replace(' ',''))) targAtoms = wx.ComboBox(self.panel,value=self.Tatoms,choices=bNames, style=wx.CB_READONLY|wx.CB_DROPDOWN) targAtoms.Bind(wx.EVT_COMBOBOX,OnTargAtoms) mainSizer.Add(targAtoms,0) OkBtn = wx.Button(self.panel,-1,"Ok") OkBtn.Bind(wx.EVT_BUTTON, self.OnOk) cancelBtn = wx.Button(self.panel,-1,"Cancel") cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel) btnSizer = wx.BoxSizer(wx.HORIZONTAL) btnSizer.Add((20,20),1) btnSizer.Add(OkBtn) btnSizer.Add((20,20),1) btnSizer.Add(cancelBtn) btnSizer.Add((20,20),1) mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10) self.panel.SetSizer(mainSizer) self.panel.Fit() self.Fit() def GetSelection(self): return self.pName,self.Oatom,self.Tatoms def OnOk(self,event): parent = self.GetParent() parent.Raise() self.EndModal(wx.ID_OK) def OnCancel(self,event): parent = self.GetParent() parent.Raise() self.EndModal(wx.ID_CANCEL)
if __name__ == "__main__": app = wx.PySimpleApp() # create the App frm = wx.Frame(None) frm.Show() PSvarDict = {'::a':1.0,'::b':1.1,'0::c':1.2,'::AlongVaraiableName':1000.} #PSvars = PSvarDict.keys() indepvarDict = {'Temperature':1.0,'Pressure':1.1,'Phase of Moon':1.2,'1:1:HAP':1.3} dlg = ExpressionDialog(frm,indepvarDict, header="Edit the PseudoVar expression", fit=False, depVarDict=PSvarDict, #VarLabel="New PseudoVar", ) newobj = dlg.Show(True) dlg = ExpressionDialog(frm,indepvarDict,newobj, header="Edit the PseudoVar expression", fit=False, depVarDict=PSvarDict, #VarLabel="New PseudoVar", ) newobj = dlg.Show(True) print (dlg.GetDepVar()) dlg = ExpressionDialog(frm,PSvarDict, header="Edit the PseudoVar expression", fit=True) newobj = dlg.Show(True) print (dlg.GetDepVar()) #app.MainLoop() if '2' in platform.python_version_tuple()[0]: import cPickle else: import pickle as cPickle def showEQ(calcobj): print print (calcobj.eObj.expression) for v in sorted(calcobj.eObj.freeVars.keys()+calcobj.eObj.assgnVars.keys()): print (" ",v,'=',calcobj.exprDict[v]) print (calcobj.EvalExpression()) print ("starting test") obj = G2obj.ExpressionObj() obj.expression = "A*np.exp(B)" obj.assgnVars = {'B': '0::Afrac:*'} obj.freeVars = {'A': [u'A', 0.5, True]} obj.CheckVars() parmDict2 = {'0::Afrac:0':1.0, '0::Afrac:1': 1.0} calcobj = G2obj.ExpressionCalcObj(obj) calcobj.SetupCalc(parmDict2) showEQ(calcobj) fp = open('/tmp/obj.pickle','w') cPickle.dump(obj,fp) fp.close() obj.expression = "A*np.exp(-2/B)" obj.assgnVars = {'A': '0::Afrac:0', 'B': '0::Afrac:1'} obj.freeVars = {} parmDict1 = {'0::Afrac:0':1.0, '0::Afrac:1': -2.0} calcobj = G2obj.ExpressionCalcObj(obj) calcobj.SetupCalc(parmDict1) showEQ(calcobj) fp = open('/tmp/obj.pickle','r') obj = cPickle.load(fp) fp.close() parmDict2 = {'0::Afrac:0':0.0, '0::Afrac:1': 1.0} calcobj = G2obj.ExpressionCalcObj(obj) calcobj.SetupCalc(parmDict2) showEQ(calcobj) parmDict2 = {'0::Afrac:0':1.0, '0::Afrac:1': 1.0} calcobj = G2obj.ExpressionCalcObj(obj) calcobj.SetupCalc(parmDict2) showEQ(calcobj)