# -*- coding: utf-8 -*-
#GSASIIgrid - data display routines
########### SVN repository information ###################
# $Date: 2014-10-01 22:35:23 -0500 (Wed, 01 Oct 2014) $
# $Author: toby $
# $Revision: 1512 $
# $URL: https://subversion.xray.aps.anl.gov/pyGSAS/trunk/GSASIIgrid.py $
# $Id: GSASIIgrid.py 1512 2014-10-02 03:35:23Z toby $
########### SVN repository information ###################
'''
*GSASIIgrid: Basic GUI routines*
--------------------------------
'''
import wx
import wx.grid as wg
import wx.wizard as wz
import wx.aui
import wx.lib.scrolledpanel as wxscroll
import time
import copy
import cPickle
import sys
import os
import numpy as np
import numpy.ma as ma
import scipy.optimize as so
import wx.html # could postpone this for quicker startup
import webbrowser # could postpone this for quicker startup
import GSASIIpath
GSASIIpath.SetVersionNumber("$Revision: 1512 $")
import GSASIImath as G2mth
import GSASIIIO as G2IO
import GSASIIstrIO as G2stIO
import GSASIIlattice as G2lat
import GSASIIplot as G2plt
import GSASIIpwdGUI as G2pdG
import GSASIIimgGUI as G2imG
import GSASIIphsGUI as G2phG
import GSASIIspc as G2spc
import GSASIImapvars as G2mv
import GSASIIconstrGUI as G2cnstG
import GSASIIrestrGUI as G2restG
import GSASIIpy3 as G2py3
import GSASIIobj as G2obj
import GSASIIexprGUI as G2exG
import GSASIIlog as log
# trig functions in degrees
sind = lambda x: np.sin(x*np.pi/180.)
tand = lambda x: np.tan(x*np.pi/180.)
cosd = lambda x: np.cos(x*np.pi/180.)
# globals we will use later
__version__ = None # gets overridden in GSASII.py
path2GSAS2 = os.path.dirname(os.path.realpath(__file__)) # save location of this file
helpLocDict = {}
htmlPanel = None
htmlFrame = None
helpMode = 'browser'
#if sys.platform.lower().startswith('win'): helpMode = 'internal' # need a global control to set this
htmlFirstUse = True
WACV = wx.ALIGN_CENTER_VERTICAL
[ wxID_FOURCALC, wxID_FOURSEARCH, wxID_FOURCLEAR, wxID_PEAKSMOVE, wxID_PEAKSCLEAR,
wxID_CHARGEFLIP, wxID_PEAKSUNIQUE, wxID_PEAKSDELETE, wxID_PEAKSDA,
wxID_PEAKSDISTVP, wxID_PEAKSVIEWPT, wxID_FINDEQVPEAKS,wxID_SHOWBONDS,wxID_MULTIMCSA,
wxID_SINGLEMCSA
] = [wx.NewId() for item in range(15)]
[ wxID_PWDRADD, wxID_HKLFADD, wxID_PWDANALYSIS, wxID_PWDCOPY, wxID_DATADELETE,
] = [wx.NewId() for item in range(5)]
[ wxID_ATOMSEDITADD, wxID_ATOMSEDITINSERT, wxID_ATOMSEDITDELETE, wxID_ATOMSREFINE,
wxID_ATOMSMODIFY, wxID_ATOMSTRANSFORM, wxID_ATOMSVIEWADD, wxID_ATOMVIEWINSERT,
wxID_RELOADDRAWATOMS,wxID_ATOMSDISAGL,wxID_ATOMMOVE,
wxID_ASSIGNATMS2RB,wxID_ATOMSPDISAGL, wxID_ISODISP,
] = [wx.NewId() for item in range(14)]
[ wxID_DRAWATOMSTYLE, wxID_DRAWATOMLABEL, wxID_DRAWATOMCOLOR, wxID_DRAWATOMRESETCOLOR,
wxID_DRAWVIEWPOINT, wxID_DRAWTRANSFORM, wxID_DRAWDELETE, wxID_DRAWFILLCELL,
wxID_DRAWADDEQUIV, wxID_DRAWFILLCOORD, wxID_DRAWDISAGLTOR, wxID_DRAWPLANE,
wxID_DRAWDISTVP,
] = [wx.NewId() for item in range(13)]
[ wxID_DRAWRESTRBOND, wxID_DRAWRESTRANGLE, wxID_DRAWRESTRPLANE, wxID_DRAWRESTRCHIRAL,
] = [wx.NewId() for item in range(4)]
[ wxID_ADDMCSAATOM,wxID_ADDMCSARB,wxID_CLEARMCSARB,wxID_MOVEMCSA,wxID_MCSACLEARRESULTS,
] = [wx.NewId() for item in range(5)]
[ wxID_CLEARTEXTURE,wxID_REFINETEXTURE,
] = [wx.NewId() for item in range(2)]
[ wxID_PAWLEYLOAD, wxID_PAWLEYESTIMATE, wxID_PAWLEYUPDATE,
] = [wx.NewId() for item in range(3)]
[ wxID_IMCALIBRATE,wxID_IMRECALIBRATE,wxID_IMINTEGRATE, wxID_IMCLEARCALIB,
wxID_IMCOPYCONTROLS, wxID_INTEGRATEALL, wxID_IMSAVECONTROLS, wxID_IMLOADCONTROLS,
] = [wx.NewId() for item in range(8)]
[ wxID_MASKCOPY, wxID_MASKSAVE, wxID_MASKLOAD,wxID_NEWMASKSPOT,wxID_NEWMASKARC,wxID_NEWMASKRING,
wxID_NEWMASKFRAME, wxID_NEWMASKPOLY,
] = [wx.NewId() for item in range(8)]
[ wxID_STRSTACOPY, wxID_STRSTAFIT, wxID_STRSTASAVE, wxID_STRSTALOAD,wxID_APPENDDZERO,
wxID_STRSTAALLFIT,wxID_UPDATEDZERO,
] = [wx.NewId() for item in range(7)]
[ wxID_BACKCOPY,wxID_LIMITCOPY, wxID_SAMPLECOPY, wxID_SAMPLECOPYSOME, wxID_BACKFLAGCOPY, wxID_SAMPLEFLAGCOPY,
wxID_SAMPLESAVE, wxID_SAMPLELOAD,wxID_ADDEXCLREGION,wxID_SETSCALE,wxID_SAMPLE1VAL
] = [wx.NewId() for item in range(11)]
[ wxID_INSTPRMRESET,wxID_CHANGEWAVETYPE,wxID_INSTCOPY, wxID_INSTFLAGCOPY, wxID_INSTLOAD,
wxID_INSTSAVE, wxID_INST1VAL, wxID_INSTCALIB,
] = [wx.NewId() for item in range(8)]
[ wxID_UNDO,wxID_LSQPEAKFIT,wxID_LSQONECYCLE,wxID_RESETSIGGAM,wxID_CLEARPEAKS,wxID_AUTOSEARCH,
wxID_PEAKSCOPY, wxID_SEQPEAKFIT,
] = [wx.NewId() for item in range(8)]
[ wxID_INDXRELOAD, wxID_INDEXPEAKS, wxID_REFINECELL, wxID_COPYCELL, wxID_MAKENEWPHASE,
] = [wx.NewId() for item in range(5)]
[ wxID_CONSTRAINTADD,wxID_EQUIVADD,wxID_HOLDADD,wxID_FUNCTADD,
wxID_CONSPHASE, wxID_CONSHIST, wxID_CONSHAP, wxID_CONSGLOBAL,
] = [wx.NewId() for item in range(8)]
[ wxID_RESTRAINTADD, wxID_RESTSELPHASE,wxID_RESTDELETE, wxID_RESRCHANGEVAL,
wxID_RESTCHANGEESD,wxID_AARESTRAINTADD,wxID_AARESTRAINTPLOT,
] = [wx.NewId() for item in range(7)]
[ wxID_RIGIDBODYADD,wxID_DRAWDEFINERB,wxID_RIGIDBODYIMPORT,wxID_RESIDUETORSSEQ,
wxID_AUTOFINDRESRB,wxID_GLOBALRESREFINE,wxID_RBREMOVEALL,wxID_COPYRBPARMS,
wxID_GLOBALTHERM,
] = [wx.NewId() for item in range(9)]
[ wxID_RENAMESEQSEL,wxID_SAVESEQSEL,wxID_SAVESEQSELCSV,wxID_PLOTSEQSEL,
wxADDSEQVAR,wxDELSEQVAR,wxEDITSEQVAR,wxCOPYPARFIT,
wxADDPARFIT,wxDELPARFIT,wxEDITPARFIT,wxDOPARFIT,
] = [wx.NewId() for item in range(12)]
[ wxID_MODELCOPY,wxID_MODELFIT,wxID_MODELADD,wxID_ELEMENTADD,wxID_ELEMENTDELETE,
wxID_ADDSUBSTANCE,wxID_LOADSUBSTANCE,wxID_DELETESUBSTANCE,wxID_COPYSUBSTANCE,
wxID_MODELUNDO,wxID_MODELFITALL,wxID_MODELCOPYFLAGS,
] = [wx.NewId() for item in range(12)]
[ wxID_SELECTPHASE,wxID_PWDHKLPLOT,wxID_PWD3DHKLPLOT,
] = [wx.NewId() for item in range(3)]
[ wxID_PDFCOPYCONTROLS, wxID_PDFSAVECONTROLS, wxID_PDFLOADCONTROLS,
wxID_PDFCOMPUTE, wxID_PDFCOMPUTEALL, wxID_PDFADDELEMENT, wxID_PDFDELELEMENT,
] = [wx.NewId() for item in range(7)]
[ wxID_MCRON,wxID_MCRLIST,wxID_MCRSAVE,wxID_MCRPLAY,
] = [wx.NewId() for item in range(4)]
VERY_LIGHT_GREY = wx.Colour(235,235,235)
################################################################################
#### GSAS-II class definitions
################################################################################
[docs]class G2TreeCtrl(wx.TreeCtrl):
'''Create a wrapper around the standard TreeCtrl so we can "wrap"
various events.
This logs when a tree item is selected (in :meth:`onSelectionChanged`)
This also wraps lists and dicts pulled out of the tree to track where
they were retrieved from.
'''
def __init__(self,parent=None,*args,**kwargs):
super(self.__class__,self).__init__(parent=parent,*args,**kwargs)
self.G2frame = parent.GetParent()
self.root = self.AddRoot('Loaded Data: ')
self.SelectionChanged = None
log.LogInfo['Tree'] = self
def _getTreeItemsList(self,item):
'''Get the full tree hierarchy from a reference to a tree item.
Note that this effectively hard-codes phase and histogram names in the
returned list. We may want to make these names relative in the future.
'''
textlist = [self.GetItemText(item)]
parent = self.GetItemParent(item)
while parent:
if parent == self.root: break
textlist.insert(0,self.GetItemText(parent))
parent = self.GetItemParent(parent)
return textlist
[docs] def onSelectionChanged(self,event):
'''Log each press on a tree item here.
'''
if self.SelectionChanged:
textlist = self._getTreeItemsList(event.GetItem())
if log.LogInfo['Logging'] and event.GetItem() != self.root:
textlist[0] = self.GetRelativeHistNum(textlist[0])
if textlist[0] == "Phases" and len(textlist) > 1:
textlist[1] = self.GetRelativePhaseNum(textlist[1])
log.MakeTreeLog(textlist)
self.SelectionChanged(event)
[docs] def Bind(self,eventtype,handler,*args,**kwargs):
'''Override the Bind() function so that page change events can be trapped
'''
if eventtype == wx.EVT_TREE_SEL_CHANGED:
self.SelectionChanged = handler
wx.TreeCtrl.Bind(self,eventtype,self.onSelectionChanged)
return
wx.TreeCtrl.Bind(self,eventtype,handler,*args,**kwargs)
[docs] def GetItemPyData(self,*args,**kwargs):
'''Override the standard method to wrap the contents
so that the source can be tracked
'''
data = super(self.__class__,self).GetItemPyData(*args,**kwargs)
textlist = self._getTreeItemsList(args[0])
if type(data) is dict:
return log.dictLogged(data,textlist)
if type(data) is list:
return log.listLogged(data,textlist)
if type(data) is tuple: #N.B. tuples get converted to lists
return log.listLogged(list(data),textlist)
return data
[docs] def GetRelativeHistNum(self,histname):
'''Returns list with a histogram type and a relative number for that
histogram, or the original string if not a histogram
'''
histtype = histname.split()[0]
if histtype != histtype.upper(): # histograms (only) have a keyword all in caps
return histname
item, cookie = self.GetFirstChild(self.root)
i = 0
while item:
itemtext = self.GetItemText(item)
if itemtext == histname:
return histtype,i
elif itemtext.split()[0] == histtype:
i += 1
item, cookie = self.GetNextChild(self.root, cookie)
else:
raise Exception("Histogram not found: "+histname)
[docs] def ConvertRelativeHistNum(self,histtype,histnum):
'''Converts a histogram type and relative histogram number to a
histogram name in the current project
'''
item, cookie = self.GetFirstChild(self.root)
i = 0
while item:
itemtext = self.GetItemText(item)
if itemtext.split()[0] == histtype:
if i == histnum: return itemtext
i += 1
item, cookie = self.GetNextChild(self.root, cookie)
else:
raise Exception("Histogram #'+str(histnum)+' of type "+histtype+' not found')
[docs] def GetRelativePhaseNum(self,phasename):
'''Returns a phase number if the string matches a phase name
or else returns the original string
'''
item, cookie = self.GetFirstChild(self.root)
while item:
itemtext = self.GetItemText(item)
if itemtext == "Phases":
parent = item
item, cookie = self.GetFirstChild(parent)
i = 0
while item:
itemtext = self.GetItemText(item)
if itemtext == phasename:
return i
item, cookie = self.GetNextChild(parent, cookie)
i += 1
else:
return phasename # not a phase name
item, cookie = self.GetNextChild(self.root, cookie)
else:
raise Exception("No phases found ")
[docs] def ConvertRelativePhaseNum(self,phasenum):
'''Converts relative phase number to a phase name in
the current project
'''
item, cookie = self.GetFirstChild(self.root)
while item:
itemtext = self.GetItemText(item)
if itemtext == "Phases":
parent = item
item, cookie = self.GetFirstChild(parent)
i = 0
while item:
if i == phasenum:
return self.GetItemText(item)
item, cookie = self.GetNextChild(parent, cookie)
i += 1
else:
raise Exception("Phase "+str(phasenum)+" not found")
item, cookie = self.GetNextChild(self.root, cookie)
else:
raise Exception("No phases found ")
#===========================================================================
[docs]class ValidatedTxtCtrl(wx.TextCtrl):
'''Create a TextCtrl widget that uses a validator to prevent the
entry of inappropriate characters and changes color to highlight
when invalid input is supplied. As valid values are typed,
they are placed into the dict or list where the initial value
came from. The type of the initial value must be int,
float or str or None (see :obj:`key` and :obj:`typeHint`);
this type (or the one in :obj:`typeHint`) is preserved.
Float values can be entered in the TextCtrl as numbers or also
as algebraic expressions using operators + - / \* () and \*\*,
in addition pi, sind(), cosd(), tand(), and sqrt() can be used,
as well as appreviations s, sin, c, cos, t, tan and sq.
:param wx.Panel parent: name of panel or frame that will be
the parent to the TextCtrl. Can be None.
:param dict/list loc: the dict or list with the initial value to be
placed in the TextCtrl.
:param int/str key: the dict key or the list index for the value to be
edited by the TextCtrl. The ``loc[key]`` element must exist, but may
have value None. If None, the type for the element is taken from
:obj:`typeHint` and the value for the control is set initially
blank (and thus invalid.) This is a way to specify a field without a
default value: a user must set a valid value.
If the value is not None, it must have a base
type of int, float, str or unicode; the TextCrtl will be initialized
from this value.
:param list nDig: number of digits & places ([nDig,nPlc]) after decimal to use
for display of float. Alternately, None can be specified which causes
numbers to be displayed with approximately 5 significant figures
(Default=None).
:param bool notBlank: if True (default) blank values are invalid
for str inputs.
:param number min: minimum allowed valid value. If None (default) the
lower limit is unbounded.
:param number max: maximum allowed valid value. If None (default) the
upper limit is unbounded
:param function OKcontrol: specifies a function or method that will be
called when the input is validated. The called function is supplied
with one argument which is False if the TextCtrl contains an invalid
value and True if the value is valid.
Note that this function should check all values
in the dialog when True, since other entries might be invalid.
The default for this is None, which indicates no function should
be called.
:param function OnLeave: specifies a function or method that will be
called when the focus for the control is lost.
The called function is supplied with (at present) three keyword arguments:
* invalid: (*bool*) True if the value for the TextCtrl is invalid
* value: (*int/float/str*) the value contained in the TextCtrl
* tc: (*wx.TextCtrl*) the TextCtrl name
The number of keyword arguments may be increased in the future should needs arise,
so it is best to code these functions with a \*\*kwargs argument so they will
continue to run without errors
The default for OnLeave is None, which indicates no function should
be called.
:param type typeHint: the value of typeHint is overrides the initial value
for the dict/list element ``loc[key]``, if set to
int or float, which specifies the type for input to the TextCtrl.
Defaults as None, which is ignored.
:param bool CIFinput: for str input, indicates that only printable
ASCII characters may be entered into the TextCtrl. Forces output
to be ASCII rather than Unicode. For float and int input, allows
use of a single '?' or '.' character as valid input.
:param dict OnLeaveArgs: a dict with keyword args that are passed to
the :attr:`OnLeave` function. Defaults to ``{}``
:param (other): other optional keyword parameters for the
wx.TextCtrl widget such as size or style may be specified.
'''
def __init__(self,parent,loc,key,nDig=None,notBlank=True,min=None,max=None,
OKcontrol=None,OnLeave=None,typeHint=None,
CIFinput=False, OnLeaveArgs={}, **kw):
# save passed values needed outside __init__
self.result = loc
self.key = key
self.nDig = nDig
self.OKcontrol=OKcontrol
self.OnLeave = OnLeave
self.OnLeaveArgs = OnLeaveArgs
self.CIFinput = CIFinput
self.type = str
# initialization
self.invalid = False # indicates if the control has invalid contents
self.evaluated = False # set to True when the validator recognizes an expression
val = loc[key]
if isinstance(val,int) or typeHint is int:
self.type = int
wx.TextCtrl.__init__(
self,parent,wx.ID_ANY,
validator=NumberValidator(int,result=loc,key=key,
min=min,max=max,
OKcontrol=OKcontrol,
CIFinput=CIFinput),
**kw)
if val is not None:
self._setValue(val)
else: # no default is invalid for a number
self.invalid = True
self._IndicateValidity()
elif isinstance(val,float) or typeHint is float:
self.type = float
wx.TextCtrl.__init__(
self,parent,wx.ID_ANY,
validator=NumberValidator(float,result=loc,key=key,
min=min,max=max,
OKcontrol=OKcontrol,
CIFinput=CIFinput),
**kw)
if val is not None:
self._setValue(val)
else:
self.invalid = True
self._IndicateValidity()
elif isinstance(val,str) or isinstance(val,unicode):
if self.CIFinput:
wx.TextCtrl.__init__(
self,parent,wx.ID_ANY,val,
validator=ASCIIValidator(result=loc,key=key),
**kw)
else:
wx.TextCtrl.__init__(self,parent,wx.ID_ANY,val,**kw)
if notBlank:
self.Bind(wx.EVT_CHAR,self._onStringKey)
self.ShowStringValidity() # test if valid input
else:
self.invalid = False
self.Bind(wx.EVT_CHAR,self._GetStringValue)
elif val is None:
raise Exception,("ValidatedTxtCtrl error: value of "+str(key)+
" element is None and typeHint not defined as int or float")
else:
raise Exception,("ValidatedTxtCtrl error: Unknown element ("+str(key)+
") type: "+str(type(val)))
# When the mouse is moved away or the widget loses focus,
# display the last saved value, if an expression
#self.Bind(wx.EVT_LEAVE_WINDOW, self._onLeaveWindow)
self.Bind(wx.EVT_TEXT_ENTER, self._onLoseFocus)
self.Bind(wx.EVT_KILL_FOCUS, self._onLoseFocus)
# patch for wx 2.9 on Mac
i,j= wx.__version__.split('.')[0:2]
if int(i)+int(j)/10. > 2.8 and 'wxOSX' in wx.PlatformInfo:
self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
def SetValue(self,val):
if self.result is not None: # note that this bypasses formatting
self.result[self.key] = val
log.LogVarChange(self.result,self.key)
self._setValue(val)
def _setValue(self,val):
self.invalid = False
if self.type is int:
try:
if int(val) != val:
self.invalid = True
else:
val = int(val)
except:
if self.CIFinput and (val == '?' or val == '.'):
pass
else:
self.invalid = True
wx.TextCtrl.SetValue(self,str(val))
elif self.type is float:
try:
val = float(val) # convert strings, if needed
except:
if self.CIFinput and (val == '?' or val == '.'):
pass
else:
self.invalid = True
if self.nDig:
wx.TextCtrl.SetValue(self,str(G2py3.FormatValue(val,self.nDig)))
else:
wx.TextCtrl.SetValue(self,str(G2py3.FormatSigFigs(val)).rstrip('0'))
else:
wx.TextCtrl.SetValue(self,str(val))
self.ShowStringValidity() # test if valid input
return
self._IndicateValidity()
if self.OKcontrol:
self.OKcontrol(not self.invalid)
[docs] def OnKeyDown(self,event):
'Special callback for wx 2.9+ on Mac where backspace is not processed by validator'
key = event.GetKeyCode()
if key in [wx.WXK_BACK, wx.WXK_DELETE]:
if self.Validator: wx.CallAfter(self.Validator.TestValid,self)
if key == wx.WXK_RETURN:
self._onLoseFocus(None)
event.Skip()
def _onStringKey(self,event):
event.Skip()
if self.invalid: # check for validity after processing the keystroke
wx.CallAfter(self.ShowStringValidity,True) # was invalid
else:
wx.CallAfter(self.ShowStringValidity,False) # was valid
def _IndicateValidity(self):
'Set the control colors to show invalid input'
if self.invalid:
self.SetForegroundColour("red")
self.SetBackgroundColour("yellow")
self.SetFocus()
self.Refresh()
else: # valid input
self.SetBackgroundColour(
wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
self.SetForegroundColour("black")
self.Refresh()
[docs] def ShowStringValidity(self,previousInvalid=True):
'''Check if input is valid. Anytime the input is
invalid, call self.OKcontrol (if defined) because it is fast.
If valid, check for any other invalid entries only when
changing from invalid to valid, since that is slower.
:param bool previousInvalid: True if the TextCtrl contents were
invalid prior to the current change.
'''
val = self.GetValue().strip()
self.invalid = not val
self._IndicateValidity()
if self.invalid:
if self.OKcontrol:
self.OKcontrol(False)
elif self.OKcontrol and previousInvalid:
self.OKcontrol(True)
# always store the result
if self.CIFinput: # for CIF make results ASCII
self.result[self.key] = val.encode('ascii','replace')
else:
self.result[self.key] = val
log.LogVarChange(self.result,self.key)
def _GetStringValue(self,event):
'''Get string input and store.
'''
event.Skip() # process keystroke
wx.CallAfter(self._SaveStringValue)
def _SaveStringValue(self):
val = self.GetValue().strip()
# always store the result
if self.CIFinput: # for CIF make results ASCII
self.result[self.key] = val.encode('ascii','replace')
else:
self.result[self.key] = val
log.LogVarChange(self.result,self.key)
def _onLoseFocus(self,event):
if self.evaluated:
self.EvaluateExpression()
elif self.result is not None: # show formatted result, as Bob wants
self._setValue(self.result[self.key])
if self.OnLeave: self.OnLeave(invalid=self.invalid,
value=self.result[self.key],
tc=self,
**self.OnLeaveArgs)
if event: event.Skip()
[docs] def EvaluateExpression(self):
'''Show the computed value when an expression is entered to the TextCtrl
Make sure that the number fits by truncating decimal places and switching
to scientific notation, as needed.
Called on loss of focus, enter, etc..
'''
if self.invalid: return # don't substitute for an invalid expression
if not self.evaluated: return # true when an expression is evaluated
if self.result is not None: # retrieve the stored result
self._setValue(self.result[self.key])
self.evaluated = False # expression has been recast as value, reset flag
[docs]class NumberValidator(wx.PyValidator):
'''A validator to be used with a TextCtrl to prevent
entering characters other than digits, signs, and for float
input, a period and exponents.
The value is checked for validity after every keystroke
If an invalid number is entered, the box is highlighted.
If the number is valid, it is saved in result[key]
:param type typ: the base data type. Must be int or float.
:param bool positiveonly: If True, negative integers are not allowed
(default False). This prevents the + or - keys from being pressed.
Used with typ=int; ignored for typ=float.
:param number min: Minimum allowed value. If None (default) the
lower limit is unbounded
:param number max: Maximum allowed value. If None (default) the
upper limit is unbounded
:param dict/list result: List or dict where value should be placed when valid
:param any key: key to use for result (int for list)
:param function OKcontrol: function or class method to control
an OK button for a window.
Ignored if None (default)
:param bool CIFinput: allows use of a single '?' or '.' character
as valid input.
'''
def __init__(self, typ, positiveonly=False, min=None, max=None,
result=None, key=None, OKcontrol=None, CIFinput=False):
'Create the validator'
wx.PyValidator.__init__(self)
# save passed parameters
self.typ = typ
self.positiveonly = positiveonly
self.min = min
self.max = max
self.result = result
self.key = key
self.OKcontrol = OKcontrol
self.CIFinput = CIFinput
# set allowed keys by data type
self.Bind(wx.EVT_CHAR, self.OnChar)
if self.typ == int and self.positiveonly:
self.validchars = '0123456789'
elif self.typ == int:
self.validchars = '0123456789+-'
elif self.typ == float:
# allow for above and sind, cosd, sqrt, tand, pi, and abbreviations
# also addition, subtraction, division, multiplication, exponentiation
self.validchars = '0123456789.-+eE/cosindcqrtap()*'
else:
self.validchars = None
return
if self.CIFinput:
self.validchars += '?.'
[docs] def Clone(self):
'Create a copy of the validator, a strange, but required component'
return NumberValidator(typ=self.typ,
positiveonly=self.positiveonly,
min=self.min, max=self.max,
result=self.result, key=self.key,
OKcontrol=self.OKcontrol,
CIFinput=self.CIFinput)
[docs] def TransferToWindow(self):
'Needed by validator, strange, but required component'
return True # Prevent wxDialog from complaining.
[docs] def TransferFromWindow(self):
'Needed by validator, strange, but required component'
return True # Prevent wxDialog from complaining.
[docs] def TestValid(self,tc):
'''Check if the value is valid by casting the input string
into the current type.
Set the invalid variable in the TextCtrl object accordingly.
If the value is valid, save it in the dict/list where
the initial value was stored, if appropriate.
:param wx.TextCtrl tc: A reference to the TextCtrl that the validator
is associated with.
'''
tc.invalid = False # assume valid
if self.CIFinput:
val = tc.GetValue().strip()
if val == '?' or val == '.':
self.result[self.key] = val
log.LogVarChange(self.result,self.key)
return
try:
val = self.typ(tc.GetValue())
except (ValueError, SyntaxError) as e:
if self.typ is float: # for float values, see if an expression can be evaluated
val = G2py3.FormulaEval(tc.GetValue())
if val is None:
tc.invalid = True
return
else:
tc.evaluated = True
else:
tc.invalid = True
return
# if self.max != None and self.typ == int:
# if val > self.max:
# tc.invalid = True
# if self.min != None and self.typ == int:
# if val < self.min:
# tc.invalid = True # invalid
if self.max != None:
if val > self.max:
tc.invalid = True
if self.min != None:
if val < self.min:
tc.invalid = True # invalid
if self.key is not None and self.result is not None and not tc.invalid:
self.result[self.key] = val
log.LogVarChange(self.result,self.key)
[docs] def ShowValidity(self,tc):
'''Set the control colors to show invalid input
:param wx.TextCtrl tc: A reference to the TextCtrl that the validator
is associated with.
'''
if tc.invalid:
tc.SetForegroundColour("red")
tc.SetBackgroundColour("yellow")
tc.SetFocus()
tc.Refresh()
return False
else: # valid input
tc.SetBackgroundColour(
wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
tc.SetForegroundColour("black")
tc.Refresh()
return True
[docs] def OnChar(self, event):
'''Called each type a key is pressed
ignores keys that are not allowed for int and float types
'''
key = event.GetKeyCode()
tc = self.GetWindow()
if key == wx.WXK_RETURN:
if tc.invalid:
self.CheckInput(True)
else:
self.CheckInput(False)
return
if key < wx.WXK_SPACE or key == wx.WXK_DELETE or key > 255: # control characters get processed
event.Skip()
if tc.invalid:
wx.CallAfter(self.CheckInput,True)
else:
wx.CallAfter(self.CheckInput,False)
return
elif chr(key) in self.validchars: # valid char pressed?
event.Skip()
if tc.invalid:
wx.CallAfter(self.CheckInput,True)
else:
wx.CallAfter(self.CheckInput,False)
return
if not wx.Validator_IsSilent(): wx.Bell()
return # Returning without calling event.Skip, which eats the keystroke
################################################################################
[docs]class ASCIIValidator(wx.PyValidator):
'''A validator to be used with a TextCtrl to prevent
entering characters other than ASCII characters.
The value is checked for validity after every keystroke
If an invalid number is entered, the box is highlighted.
If the number is valid, it is saved in result[key]
:param dict/list result: List or dict where value should be placed when valid
:param any key: key to use for result (int for list)
'''
def __init__(self, result=None, key=None):
'Create the validator'
import string
wx.PyValidator.__init__(self)
# save passed parameters
self.result = result
self.key = key
self.validchars = string.ascii_letters + string.digits + string.punctuation + string.whitespace
self.Bind(wx.EVT_CHAR, self.OnChar)
[docs] def Clone(self):
'Create a copy of the validator, a strange, but required component'
return ASCIIValidator(result=self.result, key=self.key)
tc = self.GetWindow()
tc.invalid = False # make sure the validity flag is defined in parent
[docs] def TransferToWindow(self):
'Needed by validator, strange, but required component'
return True # Prevent wxDialog from complaining.
[docs] def TransferFromWindow(self):
'Needed by validator, strange, but required component'
return True # Prevent wxDialog from complaining.
[docs] def TestValid(self,tc):
'''Check if the value is valid by casting the input string
into ASCII.
Save it in the dict/list where the initial value was stored
:param wx.TextCtrl tc: A reference to the TextCtrl that the validator
is associated with.
'''
self.result[self.key] = tc.GetValue().encode('ascii','replace')
log.LogVarChange(self.result,self.key)
[docs] def OnChar(self, event):
'''Called each type a key is pressed
ignores keys that are not allowed for int and float types
'''
key = event.GetKeyCode()
tc = self.GetWindow()
if key == wx.WXK_RETURN:
self.TestValid(tc)
return
if key < wx.WXK_SPACE or key == wx.WXK_DELETE or key > 255: # control characters get processed
event.Skip()
self.TestValid(tc)
return
elif chr(key) in self.validchars: # valid char pressed?
event.Skip()
self.TestValid(tc)
return
if not wx.Validator_IsSilent():
wx.Bell()
return # Returning without calling event.Skip, which eats the keystroke
################################################################################
[docs]class EnumSelector(wx.ComboBox):
'''A customized :class:`wxpython.ComboBox` that selects items from a list
of choices, but sets a dict (list) entry to the corresponding
entry from the input list of values.
:param wx.Panel parent: the parent to the :class:`~wxpython.ComboBox` (usually a
frame or panel)
:param dict dct: a dict (or list) to contain the value set
for the :class:`~wxpython.ComboBox`.
:param item: the dict key (or list index) where ``dct[item]`` will
be set to the value selected in the :class:`~wxpython.ComboBox`. Also, dct[item]
contains the starting value shown in the widget. If the value
does not match an entry in :data:`values`, the first value
in :data:`choices` is used as the default, but ``dct[item]`` is
not changed.
:param list choices: a list of choices to be displayed to the
user such as
::
["default","option 1","option 2",]
Note that these options will correspond to the entries in
:data:`values` (if specified) item by item.
:param list values: a list of values that correspond to
the options in :data:`choices`, such as
::
[0,1,2]
The default for :data:`values` is to use the same list as
specified for :data:`choices`.
:param (other): additional keyword arguments accepted by
:class:`~wxpython.ComboBox` can be specified.
'''
def __init__(self,parent,dct,item,choices,values=None,**kw):
if values is None:
values = choices
if dct[item] in values:
i = values.index(dct[item])
else:
i = 0
startval = choices[i]
wx.ComboBox.__init__(self,parent,wx.ID_ANY,startval,
choices = choices,
style=wx.CB_DROPDOWN|wx.CB_READONLY,
**kw)
self.choices = choices
self.values = values
self.dct = dct
self.item = item
self.Bind(wx.EVT_COMBOBOX, self.onSelection)
def onSelection(self,event):
# respond to a selection by setting the enum value in the CIF dictionary
if self.GetValue() in self.choices: # should always be true!
self.dct[self.item] = self.values[self.choices.index(self.GetValue())]
else:
self.dct[self.item] = self.values[0] # unknown
################################################################################
[docs]class G2CheckBox(wx.CheckBox):
'''A customized version of a CheckBox that automatically initializes
the control to a supplied list or dict entry and updates that
entry as the widget is used.
:param wx.Panel parent: name of panel or frame that will be
the parent to the widget. Can be None.
:param str label: text to put on check button
:param dict/list loc: the dict or list with the initial value to be
placed in the CheckBox.
:param int/str key: the dict key or the list index for the value to be
edited by the CheckBox. The ``loc[key]`` element must exist.
The CheckBox will be initialized from this value.
If the value is anything other that True (or 1), it will be taken as
False.
'''
def __init__(self,parent,label,loc,key):
wx.CheckBox.__init__(self,parent,id=wx.ID_ANY,label=label)
self.loc = loc
self.key = key
self.SetValue(self.loc[self.key]==True)
self.Bind(wx.EVT_CHECKBOX, self._OnCheckBox)
def _OnCheckBox(self,event):
self.loc[self.key] = self.GetValue()
log.LogVarChange(self.loc,self.key)
################################################################################
[docs]class SymOpDialog(wx.Dialog):
'''Class to select a symmetry operator
'''
def __init__(self,parent,SGData,New=True,ForceUnit=False):
wx.Dialog.__init__(self,parent,-1,'Select symmetry operator',
pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
panel = wx.Panel(self)
self.SGData = SGData
self.New = New
self.Force = ForceUnit
self.OpSelected = [0,0,0,[0,0,0],False,False]
mainSizer = wx.BoxSizer(wx.VERTICAL)
if ForceUnit:
choice = ['No','Yes']
self.force = wx.RadioBox(panel,-1,'Force to unit cell?',choices=choice)
self.force.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
mainSizer.Add(self.force,0,WACV)
mainSizer.Add((5,5),0)
if SGData['SGInv']:
choice = ['No','Yes']
self.inv = wx.RadioBox(panel,-1,'Choose inversion?',choices=choice)
self.inv.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
mainSizer.Add(self.inv,0,WACV)
mainSizer.Add((5,5),0)
if SGData['SGLatt'] != 'P':
LattOp = G2spc.Latt2text(SGData['SGLatt']).split(';')
self.latt = wx.RadioBox(panel,-1,'Choose cell centering?',choices=LattOp)
self.latt.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
mainSizer.Add(self.latt,0,WACV)
mainSizer.Add((5,5),0)
if SGData['SGLaue'] in ['-1','2/m','mmm','4/m','4/mmm']:
Ncol = 2
else:
Ncol = 3
OpList = []
for M,T in SGData['SGOps']:
OpList.append(G2spc.MT2text(M,T))
self.oprs = wx.RadioBox(panel,-1,'Choose space group operator?',choices=OpList,
majorDimension=Ncol)
self.oprs.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
mainSizer.Add(self.oprs,0,WACV)
mainSizer.Add((5,5),0)
mainSizer.Add(wx.StaticText(panel,-1," Choose unit cell?"),0,WACV)
mainSizer.Add((5,5),0)
cellSizer = wx.BoxSizer(wx.HORIZONTAL)
cellSizer.Add((5,0),0)
cellName = ['X','Y','Z']
self.cell = []
for i in range(3):
self.cell.append(wx.SpinCtrl(panel,-1,cellName[i],size=wx.Size(50,20)))
self.cell[-1].SetRange(-3,3)
self.cell[-1].SetValue(0)
self.cell[-1].Bind(wx.EVT_SPINCTRL, self.OnOpSelect)
cellSizer.Add(self.cell[-1],0,WACV)
mainSizer.Add(cellSizer,0,)
if self.New:
choice = ['No','Yes']
self.new = wx.RadioBox(panel,-1,'Generate new positions?',choices=choice)
self.new.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
mainSizer.Add(self.new,0,WACV)
mainSizer.Add((5,5),0)
OkBtn = wx.Button(panel,-1,"Ok")
OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
cancelBtn = wx.Button(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)
panel.SetSizer(mainSizer)
panel.Fit()
self.Fit()
def OnOpSelect(self,event):
if self.SGData['SGInv']:
self.OpSelected[0] = self.inv.GetSelection()
if self.SGData['SGLatt'] != 'P':
self.OpSelected[1] = self.latt.GetSelection()
self.OpSelected[2] = self.oprs.GetSelection()
for i in range(3):
self.OpSelected[3][i] = float(self.cell[i].GetValue())
if self.New:
self.OpSelected[4] = self.new.GetSelection()
if self.Force:
self.OpSelected[5] = self.force.GetSelection()
def GetSelection(self):
return self.OpSelected
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 DisAglDialog(wx.Dialog):
'''Distance/Angle Controls input dialog. After
:meth:`ShowModal` returns, the results are found in
dict :attr:`self.data`, which is accessed using :meth:`GetData`.
:param wx.Frame parent: reference to parent frame (or None)
:param dict data: a dict containing the current
search ranges or an empty dict, which causes default values
to be used.
Will be used to set element `DisAglCtls` in
:ref:`Phase Tree Item <Phase_table>`
:param dict default: A dict containing the default
search ranges for each element.
'''
def __init__(self,parent,data,default):
wx.Dialog.__init__(self,parent,wx.ID_ANY,
'Distance Angle Controls',
pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
self.default = default
self.panel = wx.Panel(self) #just a dummy - gets destroyed in Draw!
self._default(data,self.default)
self.Draw(self.data)
def _default(self,data,default):
'''Set starting values for the search values, either from
the input array or from defaults, if input is null
'''
if data:
self.data = copy.deepcopy(data) # don't mess with originals
else:
self.data = {}
self.data['Name'] = default['Name']
self.data['Factors'] = [0.85,0.85]
self.data['AtomTypes'] = default['AtomTypes']
self.data['BondRadii'] = default['BondRadii'][:]
self.data['AngleRadii'] = default['AngleRadii'][:]
[docs] def Draw(self,data):
'''Creates the contents of the dialog. Normally called
by :meth:`__init__`.
'''
self.panel.Destroy()
self.panel = wx.Panel(self)
mainSizer = wx.BoxSizer(wx.VERTICAL)
mainSizer.Add(wx.StaticText(self.panel,-1,'Controls for phase '+data['Name']),
0,WACV|wx.LEFT,10)
mainSizer.Add((10,10),1)
radiiSizer = wx.FlexGridSizer(0,3,5,5)
radiiSizer.Add(wx.StaticText(self.panel,-1,' Type'),0,WACV)
radiiSizer.Add(wx.StaticText(self.panel,-1,'Bond radii'),0,WACV)
radiiSizer.Add(wx.StaticText(self.panel,-1,'Angle radii'),0,WACV)
self.objList = {}
for id,item in enumerate(self.data['AtomTypes']):
radiiSizer.Add(wx.StaticText(self.panel,-1,' '+item),0,WACV)
bRadii = wx.TextCtrl(self.panel,-1,value='%.3f'%(data['BondRadii'][id]),style=wx.TE_PROCESS_ENTER)
self.objList[bRadii.GetId()] = ['BondRadii',id]
bRadii.Bind(wx.EVT_TEXT_ENTER,self.OnRadiiVal)
bRadii.Bind(wx.EVT_KILL_FOCUS,self.OnRadiiVal)
radiiSizer.Add(bRadii,0,WACV)
aRadii = wx.TextCtrl(self.panel,-1,value='%.3f'%(data['AngleRadii'][id]),style=wx.TE_PROCESS_ENTER)
self.objList[aRadii.GetId()] = ['AngleRadii',id]
aRadii.Bind(wx.EVT_TEXT_ENTER,self.OnRadiiVal)
aRadii.Bind(wx.EVT_KILL_FOCUS,self.OnRadiiVal)
radiiSizer.Add(aRadii,0,WACV)
mainSizer.Add(radiiSizer,0,wx.EXPAND)
factorSizer = wx.FlexGridSizer(0,2,5,5)
Names = ['Bond','Angle']
for i,name in enumerate(Names):
factorSizer.Add(wx.StaticText(self.panel,-1,name+' search factor'),0,WACV)
bondFact = wx.TextCtrl(self.panel,-1,value='%.3f'%(data['Factors'][i]),style=wx.TE_PROCESS_ENTER)
self.objList[bondFact.GetId()] = ['Factors',i]
bondFact.Bind(wx.EVT_TEXT_ENTER,self.OnRadiiVal)
bondFact.Bind(wx.EVT_KILL_FOCUS,self.OnRadiiVal)
factorSizer.Add(bondFact)
mainSizer.Add(factorSizer,0,wx.EXPAND)
OkBtn = wx.Button(self.panel,-1,"Ok")
OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
ResetBtn = wx.Button(self.panel,-1,'Reset')
ResetBtn.Bind(wx.EVT_BUTTON, self.OnReset)
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
btnSizer.Add((20,20),1)
btnSizer.Add(OkBtn)
btnSizer.Add(ResetBtn)
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 OnRadiiVal(self,event):
Obj = event.GetEventObject()
item = self.objList[Obj.GetId()]
try:
self.data[item[0]][item[1]] = float(Obj.GetValue())
except ValueError:
pass
Obj.SetValue("%.3f"%(self.data[item[0]][item[1]])) #reset in case of error
[docs] def GetData(self):
'Returns the values from the dialog'
return self.data
[docs] def OnOk(self,event):
'Called when the OK button is pressed'
parent = self.GetParent()
parent.Raise()
self.EndModal(wx.ID_OK)
[docs] def OnReset(self,event):
'Called when the Reset button is pressed'
data = {}
self._default(data,self.default)
self.Draw(self.data)
[docs]class PickTwoDialog(wx.Dialog):
'''This does not seem to be in use
'''
def __init__(self,parent,title,prompt,names,choices):
wx.Dialog.__init__(self,parent,-1,title,
pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
self.panel = wx.Panel(self) #just a dummy - gets destroyed in Draw!
self.prompt = prompt
self.choices = choices
self.names = names
self.Draw()
def Draw(self):
Indx = {}
def OnSelection(event):
Obj = event.GetEventObject()
id = Indx[Obj.GetId()]
self.choices[id] = Obj.GetValue().encode() #to avoid Unicode versions
self.Draw()
self.panel.DestroyChildren()
self.panel.Destroy()
self.panel = wx.Panel(self)
mainSizer = wx.BoxSizer(wx.VERTICAL)
mainSizer.Add(wx.StaticText(self.panel,-1,self.prompt),0,wx.ALIGN_CENTER)
for isel,name in enumerate(self.choices):
lineSizer = wx.BoxSizer(wx.HORIZONTAL)
lineSizer.Add(wx.StaticText(self.panel,-1,'Reference atom '+str(isel+1)),0,wx.ALIGN_CENTER)
nameList = self.names[:]
if isel:
if self.choices[0] in nameList:
nameList.remove(self.choices[0])
choice = wx.ComboBox(self.panel,-1,value=name,choices=nameList,
style=wx.CB_READONLY|wx.CB_DROPDOWN)
Indx[choice.GetId()] = isel
choice.Bind(wx.EVT_COMBOBOX, OnSelection)
lineSizer.Add(choice,0,WACV)
mainSizer.Add(lineSizer)
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(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.choices
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 SingleFloatDialog(wx.Dialog):
'Dialog to obtain a single float value from user'
def __init__(self,parent,title,prompt,value,limits=[0.,1.],format='%.5g'):
wx.Dialog.__init__(self,parent,-1,title,
pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
self.panel = wx.Panel(self) #just a dummy - gets destroyed in Draw!
self.limits = limits
self.value = value
self.prompt = prompt
self.format = format
self.Draw()
def Draw(self):
def OnValItem(event):
try:
val = float(valItem.GetValue())
if val < self.limits[0] or val > self.limits[1]:
raise ValueError
except ValueError:
val = self.value
self.value = val
valItem.SetValue(self.format%(self.value))
self.panel.Destroy()
self.panel = wx.Panel(self)
mainSizer = wx.BoxSizer(wx.VERTICAL)
mainSizer.Add(wx.StaticText(self.panel,-1,self.prompt),0,wx.ALIGN_CENTER)
valItem = wx.TextCtrl(self.panel,-1,value=self.format%(self.value),style=wx.TE_PROCESS_ENTER)
mainSizer.Add(valItem,0,wx.ALIGN_CENTER)
valItem.Bind(wx.EVT_TEXT_ENTER,OnValItem)
valItem.Bind(wx.EVT_KILL_FOCUS,OnValItem)
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(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 GetValue(self):
return self.value
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 SingleStringDialog(wx.Dialog):
'''Dialog to obtain a single string value from user
:param wx.Frame parent: name of parent frame
:param str title: title string for dialog
:param str prompt: string to tell use what they are inputting
:param str value: default input value, if any
'''
def __init__(self,parent,title,prompt,value='',size=(200,-1)):
wx.Dialog.__init__(self,parent,wx.ID_ANY,title,
pos=wx.DefaultPosition,
style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
self.value = value
self.prompt = prompt
self.CenterOnParent()
self.panel = wx.Panel(self)
mainSizer = wx.BoxSizer(wx.VERTICAL)
mainSizer.Add(wx.StaticText(self.panel,-1,self.prompt),0,wx.ALIGN_CENTER)
self.valItem = wx.TextCtrl(self.panel,-1,value=self.value,size=size)
mainSizer.Add(self.valItem,0,wx.ALIGN_CENTER)
btnsizer = wx.StdDialogButtonSizer()
OKbtn = wx.Button(self.panel, wx.ID_OK)
OKbtn.SetDefault()
btnsizer.AddButton(OKbtn)
btn = wx.Button(self.panel, wx.ID_CANCEL)
btnsizer.AddButton(btn)
btnsizer.Realize()
mainSizer.Add(btnsizer,0,wx.ALIGN_CENTER)
self.panel.SetSizer(mainSizer)
self.panel.Fit()
self.Fit()
[docs] def Show(self):
'''Use this method after creating the dialog to post it
:returns: True if the user pressed OK; False if the User pressed Cancel
'''
if self.ShowModal() == wx.ID_OK:
self.value = self.valItem.GetValue()
return True
else:
return False
[docs] def GetValue(self):
'''Use this method to get the value entered by the user
:returns: string entered by user
'''
return self.value
################################################################################
[docs]class MultiStringDialog(wx.Dialog):
'''Dialog to obtain a multi string values from user
:param wx.Frame parent: name of parent frame
:param str title: title string for dialog
:param str prompts: strings to tell use what they are inputting
:param str values: default input values, if any
'''
def __init__(self,parent,title,prompts,values=[]): #,size=(200,-1)?
wx.Dialog.__init__(self,parent,wx.ID_ANY,title,
pos=wx.DefaultPosition,
style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
self.values = values
self.prompts = prompts
self.CenterOnParent()
self.panel = wx.Panel(self)
mainSizer = wx.BoxSizer(wx.VERTICAL)
promptSizer = wx.FlexGridSizer(0,2,5,5)
self.Indx = {}
for prompt,value in zip(prompts,values):
promptSizer.Add(wx.StaticText(self.panel,-1,prompt),0,WACV)
valItem = wx.TextCtrl(self.panel,-1,value=value,style=wx.TE_PROCESS_ENTER)
self.Indx[valItem.GetId()] = prompt
valItem.Bind(wx.EVT_TEXT,self.newValue)
promptSizer.Add(valItem,0,WACV)
mainSizer.Add(promptSizer,0)
btnsizer = wx.StdDialogButtonSizer()
OKbtn = wx.Button(self.panel, wx.ID_OK)
OKbtn.SetDefault()
btnsizer.AddButton(OKbtn)
btn = wx.Button(self.panel, wx.ID_CANCEL)
btnsizer.AddButton(btn)
btnsizer.Realize()
mainSizer.Add(btnsizer,0,wx.ALIGN_CENTER)
self.panel.SetSizer(mainSizer)
self.panel.Fit()
self.Fit()
def newValue(self,event):
Obj = event.GetEventObject()
item = self.Indx[Obj.GetId()]
id = self.prompts.index(item)
self.values[id] = Obj.GetValue()
[docs] def Show(self):
'''Use this method after creating the dialog to post it
:returns: True if the user pressed OK; False if the User pressed Cancel
'''
if self.ShowModal() == wx.ID_OK:
return True
else:
return False
[docs] def GetValues(self):
'''Use this method to get the value entered by the user
:returns: string entered by user
'''
return self.values
################################################################################
[docs]class G2MultiChoiceDialog(wx.Dialog):
'''A dialog similar to MultiChoiceDialog except that buttons are
added to set all choices and to toggle all choices.
:param wx.Frame ParentFrame: reference to parent frame
:param str title: heading above list of choices
:param str header: Title to place on window frame
:param list ChoiceList: a list of choices where one will be selected
:param bool toggle: If True (default) the toggle and select all buttons
are displayed
:param bool monoFont: If False (default), use a variable-spaced font;
if True use a equally-spaced font.
:param bool filterBox: If True (default) an input widget is placed on
the window and only entries matching the entered text are shown.
:param kw: optional keyword parameters for the wx.Dialog may
be included such as size [which defaults to `(320,310)`] and
style (which defaults to `wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.CENTRE| wx.OK | wx.CANCEL`);
note that `wx.OK` and `wx.CANCEL` controls
the presence of the eponymous buttons in the dialog.
:returns: the name of the created dialog
'''
def __init__(self,parent, title, header, ChoiceList, toggle=True,
monoFont=False, filterBox=True, **kw):
# process keyword parameters, notably style
options = {'size':(320,310), # default Frame keywords
'style':wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.CENTRE| wx.OK | wx.CANCEL,
}
options.update(kw)
self.ChoiceList = ChoiceList # list of choices (list of str values)
self.Selections = len(self.ChoiceList) * [False,] # selection status for each choice (list of bools)
self.filterlist = range(len(self.ChoiceList)) # list of the choice numbers that have been filtered (list of int indices)
if options['style'] & wx.OK:
useOK = True
options['style'] ^= wx.OK
else:
useOK = False
if options['style'] & wx.CANCEL:
useCANCEL = True
options['style'] ^= wx.CANCEL
else:
useCANCEL = False
# create the dialog frame
wx.Dialog.__init__(self,parent,wx.ID_ANY,header,**options)
# fill the dialog
Sizer = wx.BoxSizer(wx.VERTICAL)
topSizer = wx.BoxSizer(wx.HORIZONTAL)
topSizer.Add(
wx.StaticText(self,wx.ID_ANY,title,size=(-1,35)),
1,wx.ALL|wx.EXPAND|WACV,1)
if filterBox:
self.timer = wx.Timer()
self.timer.Bind(wx.EVT_TIMER,self.Filter)
topSizer.Add(wx.StaticText(self,wx.ID_ANY,'Name \nFilter: '),0,wx.ALL|WACV,1)
self.filterBox = wx.TextCtrl(self, wx.ID_ANY, size=(80,-1),style=wx.TE_PROCESS_ENTER)
self.filterBox.Bind(wx.EVT_CHAR,self.onChar)
self.filterBox.Bind(wx.EVT_TEXT_ENTER,self.Filter)
topSizer.Add(self.filterBox,0,wx.ALL|WACV,0)
Sizer.Add(topSizer,0,wx.ALL|wx.EXPAND,8)
self.clb = wx.CheckListBox(self, wx.ID_ANY, (30,30), wx.DefaultSize, ChoiceList)
if monoFont:
font1 = wx.Font(self.clb.GetFont().GetPointSize(),
wx.MODERN, wx.NORMAL, wx.NORMAL, False)
self.clb.SetFont(font1)
Sizer.Add(self.clb,1,wx.LEFT|wx.RIGHT|wx.EXPAND,10)
Sizer.Add((-1,10))
# set/toggle buttons
if toggle:
bSizer = wx.BoxSizer(wx.VERTICAL)
setBut = wx.Button(self,wx.ID_ANY,'Set All')
setBut.Bind(wx.EVT_BUTTON,self._SetAll)
bSizer.Add(setBut,0,wx.ALIGN_CENTER)
bSizer.Add((-1,5))
togBut = wx.Button(self,wx.ID_ANY,'Toggle All')
togBut.Bind(wx.EVT_BUTTON,self._ToggleAll)
bSizer.Add(togBut,0,wx.ALIGN_CENTER)
Sizer.Add(bSizer,0,wx.LEFT,12)
# OK/Cancel buttons
btnsizer = wx.StdDialogButtonSizer()
if useOK:
self.OKbtn = wx.Button(self, wx.ID_OK)
self.OKbtn.SetDefault()
btnsizer.AddButton(self.OKbtn)
if useCANCEL:
btn = wx.Button(self, wx.ID_CANCEL)
btnsizer.AddButton(btn)
btnsizer.Realize()
Sizer.Add((-1,5))
Sizer.Add(btnsizer,0,wx.ALIGN_RIGHT,50)
Sizer.Add((-1,20))
# OK done, let's get outa here
self.SetSizer(Sizer)
self.CenterOnParent()
[docs] def GetSelections(self):
'Returns a list of the indices for the selected choices'
# update self.Selections with settings for displayed items
for i in range(len(self.filterlist)):
self.Selections[self.filterlist[i]] = self.clb.IsChecked(i)
# return all selections, shown or hidden
return [i for i in range(len(self.Selections)) if self.Selections[i]]
[docs] def SetSelections(self,selList):
'''Sets the selection indices in selList as selected. Resets any previous
selections for compatibility with wx.MultiChoiceDialog. Note that
the state for only the filtered items is shown.
:param list selList: indices of items to be selected. These indices
are referenced to the order in self.ChoiceList
'''
self.Selections = len(self.ChoiceList) * [False,] # reset selections
for sel in selList:
self.Selections[sel] = True
self._ShowSelections()
def _ShowSelections(self):
'Show the selection state for displayed items'
self.clb.SetChecked(
[i for i in range(len(self.filterlist)) if self.Selections[self.filterlist[i]]]
) # Note anything previously checked will be cleared.
def _SetAll(self,event):
'Set all viewed choices on'
self.clb.SetChecked(range(len(self.filterlist)))
def _ToggleAll(self,event):
'flip the state of all viewed choices'
for i in range(len(self.filterlist)):
self.clb.Check(i,not self.clb.IsChecked(i))
def onChar(self,event):
self.OKbtn.Enable(False)
if self.timer.IsRunning():
self.timer.Stop()
self.timer.Start(1000,oneShot=True)
event.Skip()
def Filter(self,event):
if self.timer.IsRunning():
self.timer.Stop()
self.GetSelections() # record current selections
txt = self.filterBox.GetValue()
self.clb.Clear()
self.Update()
self.filterlist = []
if txt:
txt = txt.lower()
ChoiceList = []
for i,item in enumerate(self.ChoiceList):
if item.lower().find(txt) != -1:
ChoiceList.append(item)
self.filterlist.append(i)
else:
self.filterlist = range(len(self.ChoiceList))
ChoiceList = self.ChoiceList
self.clb.AppendItems(ChoiceList)
self._ShowSelections()
self.OKbtn.Enable(True)
################################################################################
[docs]class G2SingleChoiceDialog(wx.Dialog):
'''A dialog similar to wx.SingleChoiceDialog except that a filter can be
added.
:param wx.Frame ParentFrame: reference to parent frame
:param str title: heading above list of choices
:param str header: Title to place on window frame
:param list ChoiceList: a list of choices where one will be selected
:param bool monoFont: If False (default), use a variable-spaced font;
if True use a equally-spaced font.
:param bool filterBox: If True (default) an input widget is placed on
the window and only entries matching the entered text are shown.
:param kw: optional keyword parameters for the wx.Dialog may
be included such as size [which defaults to `(320,310)`] and
style (which defaults to ``wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.CENTRE | wx.OK | wx.CANCEL``);
note that ``wx.OK`` and ``wx.CANCEL`` controls
the presence of the eponymous buttons in the dialog.
:returns: the name of the created dialog
'''
def __init__(self,parent, title, header, ChoiceList,
monoFont=False, filterBox=True, **kw):
# process keyword parameters, notably style
options = {'size':(320,310), # default Frame keywords
'style':wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.CENTRE| wx.OK | wx.CANCEL,
}
options.update(kw)
self.ChoiceList = ChoiceList
self.filterlist = range(len(self.ChoiceList))
if options['style'] & wx.OK:
useOK = True
options['style'] ^= wx.OK
else:
useOK = False
if options['style'] & wx.CANCEL:
useCANCEL = True
options['style'] ^= wx.CANCEL
else:
useCANCEL = False
# create the dialog frame
wx.Dialog.__init__(self,parent,wx.ID_ANY,header,**options)
# fill the dialog
Sizer = wx.BoxSizer(wx.VERTICAL)
topSizer = wx.BoxSizer(wx.HORIZONTAL)
topSizer.Add(
wx.StaticText(self,wx.ID_ANY,title,size=(-1,35)),
1,wx.ALL|wx.EXPAND|WACV,1)
if filterBox:
self.timer = wx.Timer()
self.timer.Bind(wx.EVT_TIMER,self.Filter)
topSizer.Add(wx.StaticText(self,wx.ID_ANY,'Filter: '),0,wx.ALL,1)
self.filterBox = wx.TextCtrl(self, wx.ID_ANY, size=(80,-1),
style=wx.TE_PROCESS_ENTER)
self.filterBox.Bind(wx.EVT_CHAR,self.onChar)
self.filterBox.Bind(wx.EVT_TEXT_ENTER,self.Filter)
topSizer.Add(self.filterBox,0,wx.ALL,0)
Sizer.Add(topSizer,0,wx.ALL|wx.EXPAND,8)
self.clb = wx.ListBox(self, wx.ID_ANY, (30,30), wx.DefaultSize, ChoiceList)
self.clb.Bind(wx.EVT_LEFT_DCLICK,self.onDoubleClick)
if monoFont:
font1 = wx.Font(self.clb.GetFont().GetPointSize(),
wx.MODERN, wx.NORMAL, wx.NORMAL, False)
self.clb.SetFont(font1)
Sizer.Add(self.clb,1,wx.LEFT|wx.RIGHT|wx.EXPAND,10)
Sizer.Add((-1,10))
# OK/Cancel buttons
btnsizer = wx.StdDialogButtonSizer()
if useOK:
self.OKbtn = wx.Button(self, wx.ID_OK)
self.OKbtn.SetDefault()
btnsizer.AddButton(self.OKbtn)
if useCANCEL:
btn = wx.Button(self, wx.ID_CANCEL)
btnsizer.AddButton(btn)
btnsizer.Realize()
Sizer.Add((-1,5))
Sizer.Add(btnsizer,0,wx.ALIGN_RIGHT,50)
Sizer.Add((-1,20))
# OK done, let's get outa here
self.SetSizer(Sizer)
[docs] def GetSelection(self):
'Returns the index of the selected choice'
i = self.clb.GetSelection()
if i < 0 or i >= len(self.filterlist):
return wx.NOT_FOUND
return self.filterlist[i]
def onChar(self,event):
self.OKbtn.Enable(False)
if self.timer.IsRunning():
self.timer.Stop()
self.timer.Start(1000,oneShot=True)
event.Skip()
def Filter(self,event):
if self.timer.IsRunning():
self.timer.Stop()
txt = self.filterBox.GetValue()
self.clb.Clear()
self.Update()
self.filterlist = []
if txt:
txt = txt.lower()
ChoiceList = []
for i,item in enumerate(self.ChoiceList):
if item.lower().find(txt) != -1:
ChoiceList.append(item)
self.filterlist.append(i)
else:
self.filterlist = range(len(self.ChoiceList))
ChoiceList = self.ChoiceList
self.clb.AppendItems(ChoiceList)
self.OKbtn.Enable(True)
def onDoubleClick(self,event):
self.EndModal(wx.ID_OK)
################################################################################
[docs]def ItemSelector(ChoiceList, ParentFrame=None,
title='Select an item',
size=None, header='Item Selector',
useCancel=True,multiple=False):
''' Provide a wx dialog to select a single item or multiple items from list of choices
:param list ChoiceList: a list of choices where one will be selected
:param wx.Frame ParentFrame: Name of parent frame (default None)
:param str title: heading above list of choices (default 'Select an item')
:param wx.Size size: Size for dialog to be created (default None -- size as needed)
:param str header: Title to place on window frame (default 'Item Selector')
:param bool useCancel: If True (default) both the OK and Cancel buttons are offered
:param bool multiple: If True then multiple items can be selected (default False)
:returns: the selection index or None or a selection list if multiple is true
'''
if multiple:
if useCancel:
dlg = G2MultiChoiceDialog(
ParentFrame,title, header, ChoiceList)
else:
dlg = G2MultiChoiceDialog(
ParentFrame,title, header, ChoiceList,
style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
else:
if useCancel:
dlg = wx.SingleChoiceDialog(
ParentFrame,title, header, ChoiceList)
else:
dlg = wx.SingleChoiceDialog(
ParentFrame,title, header,ChoiceList,
style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
if size: dlg.SetSize(size)
if dlg.ShowModal() == wx.ID_OK:
if multiple:
dlg.Destroy()
return dlg.GetSelections()
else:
dlg.Destroy()
return dlg.GetSelection()
else:
dlg.Destroy()
return None
dlg.Destroy()
################################################################################
[docs]class GridFractionEditor(wg.PyGridCellEditor):
'''A grid cell editor class that allows entry of values as fractions as well
as sine and cosine values [as s() and c()]
'''
def __init__(self,grid):
wg.PyGridCellEditor.__init__(self)
def Create(self, parent, id, evtHandler):
self._tc = wx.TextCtrl(parent, id, "")
self._tc.SetInsertionPoint(0)
self.SetControl(self._tc)
if evtHandler:
self._tc.PushEventHandler(evtHandler)
self._tc.Bind(wx.EVT_CHAR, self.OnChar)
def SetSize(self, rect):
self._tc.SetDimensions(rect.x, rect.y, rect.width+2, rect.height+2,
wx.SIZE_ALLOW_MINUS_ONE)
def BeginEdit(self, row, col, grid):
self.startValue = grid.GetTable().GetValue(row, col)
self._tc.SetValue(str(self.startValue))
self._tc.SetInsertionPointEnd()
self._tc.SetFocus()
self._tc.SetSelection(0, self._tc.GetLastPosition())
def EndEdit(self, row, col, grid):
changed = False
val = self._tc.GetValue().lower()
if val != self.startValue:
changed = True
neg = False
if '-' in val:
neg = True
if '/' in val and '.' not in val:
val += '.'
elif 's' in val and not 'sind(' in val:
if neg:
val = '-sind('+val.strip('-s')+')'
else:
val = 'sind('+val.strip('s')+')'
elif 'c' in val and not 'cosd(' in val:
if neg:
val = '-cosd('+val.strip('-c')+')'
else:
val = 'cosd('+val.strip('c')+')'
try:
val = float(eval(val))
except (SyntaxError,NameError):
val = self.startValue
grid.GetTable().SetValue(row, col, val) # update the table
self.startValue = ''
self._tc.SetValue('')
return changed
def Reset(self):
self._tc.SetValue(self.startValue)
self._tc.SetInsertionPointEnd()
def Clone(self):
return GridFractionEditor(grid)
def StartingKey(self, evt):
self.OnChar(evt)
if evt.GetSkipped():
self._tc.EmulateKeyPress(evt)
def OnChar(self, evt):
key = evt.GetKeyCode()
if key == 15:
return
if key > 255:
evt.Skip()
return
char = chr(key)
if char in '.+-/0123456789cosind()':
self._tc.WriteText(char)
else:
evt.Skip()
################################################################################
[docs]def SelectEdit1Var(G2frame,array,labelLst,elemKeysLst,dspLst,refFlgElem):
'''Select a variable from a list, then edit it and select histograms
to copy it to.
:param wx.Frame G2frame: main GSAS-II frame
:param dict array: the array (dict or list) where values to be edited are kept
:param list labelLst: labels for each data item
:param list elemKeysLst: a list of lists of keys needed to be applied (see below)
to obtain the value of each parameter
:param list dspLst: list list of digits to be displayed (10,4) is 10 digits
with 4 decimal places. Can be None.
:param list refFlgElem: a list of lists of keys needed to be applied (see below)
to obtain the refine flag for each parameter or None if the parameter
does not have refine flag.
Example::
array = data
labelLst = ['v1','v2']
elemKeysLst = [['v1'], ['v2',0]]
refFlgElem = [None, ['v2',1]]
* The value for v1 will be in data['v1'] and this cannot be refined while,
* The value for v2 will be in data['v2'][0] and its refinement flag is data['v2'][1]
'''
def unkey(dct,keylist):
'''dive into a nested set of dicts/lists applying keys in keylist
consecutively
'''
d = dct
for k in keylist:
d = d[k]
return d
def OnChoice(event):
'Respond when a parameter is selected in the Choice box'
valSizer.DeleteWindows()
lbl = event.GetString()
copyopts['currentsel'] = lbl
i = labelLst.index(lbl)
OKbtn.Enable(True)
ch.SetLabel(lbl)
args = {}
if dspLst[i]:
args = {'nDig':dspLst[i]}
Val = ValidatedTxtCtrl(
dlg,
unkey(array,elemKeysLst[i][:-1]),
elemKeysLst[i][-1],
**args)
copyopts['startvalue'] = unkey(array,elemKeysLst[i])
#unkey(array,elemKeysLst[i][:-1])[elemKeysLst[i][-1]] =
valSizer.Add(Val,0,wx.LEFT,5)
dlg.SendSizeEvent()
# SelectEdit1Var execution begins here
saveArray = copy.deepcopy(array) # keep original values
TreeItemType = G2frame.PatternTree.GetItemText(G2frame.PickId)
copyopts = {'InTable':False,"startvalue":None,'currentsel':None}
hst = G2frame.PatternTree.GetItemText(G2frame.PatternId)
histList = G2pdG.GetHistsLikeSelected(G2frame)
if not histList:
G2frame.ErrorDialog('No match','No histograms match '+hst,G2frame.dataFrame)
return
dlg = wx.Dialog(G2frame.dataDisplay,wx.ID_ANY,'Set a parameter value',
style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
mainSizer = wx.BoxSizer(wx.VERTICAL)
mainSizer.Add((5,5))
subSizer = wx.BoxSizer(wx.HORIZONTAL)
subSizer.Add((-1,-1),1,wx.EXPAND)
subSizer.Add(wx.StaticText(dlg,wx.ID_ANY,'Select a parameter and set a new value'))
subSizer.Add((-1,-1),1,wx.EXPAND)
mainSizer.Add(subSizer,0,wx.EXPAND,0)
mainSizer.Add((0,10))
subSizer = wx.FlexGridSizer(0,2,5,0)
subSizer.Add(wx.StaticText(dlg,wx.ID_ANY,'Parameter: '))
ch = wx.Choice(dlg, wx.ID_ANY, choices = sorted(labelLst))
ch.SetSelection(-1)
ch.Bind(wx.EVT_CHOICE, OnChoice)
subSizer.Add(ch)
subSizer.Add(wx.StaticText(dlg,wx.ID_ANY,'Value: '))
valSizer = wx.BoxSizer(wx.HORIZONTAL)
subSizer.Add(valSizer)
mainSizer.Add(subSizer)
mainSizer.Add((-1,20))
subSizer = wx.BoxSizer(wx.HORIZONTAL)
subSizer.Add(G2CheckBox(dlg, 'Edit in table ', copyopts, 'InTable'))
mainSizer.Add(subSizer)
btnsizer = wx.StdDialogButtonSizer()
OKbtn = wx.Button(dlg, wx.ID_OK,'Continue')
OKbtn.Enable(False)
OKbtn.SetDefault()
OKbtn.Bind(wx.EVT_BUTTON,lambda event: dlg.EndModal(wx.ID_OK))
btnsizer.AddButton(OKbtn)
btn = wx.Button(dlg, wx.ID_CANCEL)
btnsizer.AddButton(btn)
btnsizer.Realize()
mainSizer.Add((-1,5),1,wx.EXPAND,1)
mainSizer.Add(btnsizer,0,wx.ALIGN_CENTER,0)
mainSizer.Add((-1,10))
dlg.SetSizer(mainSizer)
dlg.CenterOnParent()
if dlg.ShowModal() != wx.ID_OK:
array.update(saveArray)
dlg.Destroy()
return
dlg.Destroy()
copyList = []
lbl = copyopts['currentsel']
dlg = G2MultiChoiceDialog(
G2frame.dataFrame,
'Copy parameter '+lbl+' from\n'+hst,
'Copy parameters', histList)
dlg.CenterOnParent()
try:
if dlg.ShowModal() == wx.ID_OK:
for i in dlg.GetSelections():
copyList.append(histList[i])
else:
# reset the parameter since cancel was pressed
array.update(saveArray)
return
finally:
dlg.Destroy()
prelbl = [hst]
i = labelLst.index(lbl)
keyLst = elemKeysLst[i]
refkeys = refFlgElem[i]
dictlst = [unkey(array,keyLst[:-1])]
if refkeys is not None:
refdictlst = [unkey(array,refkeys[:-1])]
else:
refdictlst = None
Id = GetPatternTreeItemId(G2frame,G2frame.root,hst)
hstData = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,Id,'Instrument Parameters'))[0]
for h in copyList:
Id = GetPatternTreeItemId(G2frame,G2frame.root,h)
instData = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,Id,'Instrument Parameters'))[0]
if len(hstData) != len(instData) or hstData['Type'][0] != instData['Type'][0]: #don't mix data types or lam & lam1/lam2 parms!
print h+' not copied - instrument parameters not commensurate'
continue
hData = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,Id,TreeItemType))
if TreeItemType == 'Instrument Parameters':
hData = hData[0]
#copy the value if it is changed or we will not edit in a table
valNow = unkey(array,keyLst)
if copyopts['startvalue'] != valNow or not copyopts['InTable']:
unkey(hData,keyLst[:-1])[keyLst[-1]] = valNow
prelbl += [h]
dictlst += [unkey(hData,keyLst[:-1])]
if refdictlst is not None:
refdictlst += [unkey(hData,refkeys[:-1])]
if refdictlst is None:
args = {}
else:
args = {'checkdictlst':refdictlst,
'checkelemlst':len(dictlst)*[refkeys[-1]],
'checklabel':'Refine?'}
if copyopts['InTable']:
dlg = ScrolledMultiEditor(
G2frame.dataDisplay,dictlst,
len(dictlst)*[keyLst[-1]],prelbl,
header='Editing parameter '+lbl,
CopyButton=True,**args)
dlg.CenterOnParent()
if dlg.ShowModal() != wx.ID_OK:
array.update(saveArray)
dlg.Destroy()
################################################################################
[docs]class ShowLSParms(wx.Dialog):
'''Create frame to show least-squares parameters
'''
def __init__(self,parent,title,parmDict,varyList,fullVaryList,
size=(300,430)):
wx.Dialog.__init__(self,parent,wx.ID_ANY,title,size=size,
style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
mainSizer = wx.BoxSizer(wx.VERTICAL)
panel = wxscroll.ScrolledPanel(
self, wx.ID_ANY,
#size=size,
style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
num = len(varyList)
mainSizer.Add(wx.StaticText(self,wx.ID_ANY,'Number of refined variables: '+str(num)))
if len(varyList) != len(fullVaryList):
num = len(fullVaryList) - len(varyList)
mainSizer.Add(wx.StaticText(self,wx.ID_ANY,' + '+str(num)+' parameters are varied via constraints'))
subSizer = wx.FlexGridSizer(cols=4,hgap=2,vgap=2)
parmNames = parmDict.keys()
parmNames.sort()
subSizer.Add((-1,-1))
subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'Parameter name '))
subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'refine?'))
subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'value'),0,wx.ALIGN_RIGHT)
explainRefine = False
for name in parmNames:
# skip entries without numerical values
if isinstance(parmDict[name],basestring): continue
try:
value = G2py3.FormatSigFigs(parmDict[name])
except TypeError:
value = str(parmDict[name])+' -?' # unexpected
#continue
v = G2obj.getVarDescr(name)
if v is None or v[-1] is None:
subSizer.Add((-1,-1))
else:
ch = HelpButton(panel,G2obj.fmtVarDescr(name))
subSizer.Add(ch,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1)
subSizer.Add(wx.StaticText(panel,wx.ID_ANY,str(name)))
if name in varyList:
subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'R'))
elif name in fullVaryList:
subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'C'))
explainRefine = True
else:
subSizer.Add((-1,-1))
subSizer.Add(wx.StaticText(panel,wx.ID_ANY,value),0,wx.ALIGN_RIGHT)
# finish up ScrolledPanel
panel.SetSizer(subSizer)
panel.SetAutoLayout(1)
panel.SetupScrolling()
mainSizer.Add(panel,1, wx.ALL|wx.EXPAND,1)
if explainRefine:
mainSizer.Add(
wx.StaticText(self,wx.ID_ANY,
'"R" indicates a refined variable\n'+
'"C" indicates generated from a constraint'
),
0, wx.ALL,0)
# make OK button
btnsizer = wx.BoxSizer(wx.HORIZONTAL)
btn = wx.Button(self, wx.ID_CLOSE,"Close")
btn.Bind(wx.EVT_BUTTON,self._onClose)
btnsizer.Add(btn)
mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)
# Allow window to be enlarged but not made smaller
self.SetSizer(mainSizer)
self.SetMinSize(self.GetSize())
def _onClose(self,event):
self.EndModal(wx.ID_CANCEL)
################################################################################
[docs]class downdate(wx.Dialog):
'''Dialog to allow a user to select a version of GSAS-II to install
'''
def __init__(self,parent=None):
style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
wx.Dialog.__init__(self, parent, wx.ID_ANY, 'Select Version', style=style)
pnl = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
insver = GSASIIpath.svnGetRev(local=True)
curver = int(GSASIIpath.svnGetRev(local=False))
label = wx.StaticText(
pnl, wx.ID_ANY,
'Select a specific GSAS-II version to install'
)
sizer.Add(label, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
sizer1 = wx.BoxSizer(wx.HORIZONTAL)
sizer1.Add(
wx.StaticText(pnl, wx.ID_ANY,
'Currently installed version: '+str(insver)),
0, wx.ALIGN_CENTRE|wx.ALL, 5)
sizer.Add(sizer1)
sizer1 = wx.BoxSizer(wx.HORIZONTAL)
sizer1.Add(
wx.StaticText(pnl, wx.ID_ANY,
'Select GSAS-II version to install: '),
0, wx.ALIGN_CENTRE|wx.ALL, 5)
self.spin = wx.SpinCtrl(pnl, wx.ID_ANY,size=(150,-1))
self.spin.SetRange(1, curver)
self.spin.SetValue(curver)
self.Bind(wx.EVT_SPINCTRL, self._onSpin, self.spin)
self.Bind(wx.EVT_KILL_FOCUS, self._onSpin, self.spin)
sizer1.Add(self.spin)
sizer.Add(sizer1)
line = wx.StaticLine(pnl,-1, size=(-1,3), style=wx.LI_HORIZONTAL)
sizer.Add(line, 0, wx.EXPAND|wx.ALIGN_CENTER|wx.ALL, 10)
self.text = wx.StaticText(pnl, wx.ID_ANY, "")
sizer.Add(self.text, 0, wx.ALIGN_LEFT|wx.EXPAND|wx.ALL, 5)
line = wx.StaticLine(pnl,-1, size=(-1,3), style=wx.LI_HORIZONTAL)
sizer.Add(line, 0, wx.EXPAND|wx.ALIGN_CENTER|wx.ALL, 10)
sizer.Add(
wx.StaticText(
pnl, wx.ID_ANY,
'If "Install" is pressed, your project will be saved;\n'
'GSAS-II will exit; The specified version will be loaded\n'
'and GSAS-II will restart. Press "Cancel" to abort.'),
0, wx.EXPAND|wx.ALL, 10)
btnsizer = wx.StdDialogButtonSizer()
btn = wx.Button(pnl, wx.ID_OK, "Install")
btn.SetDefault()
btnsizer.AddButton(btn)
btn = wx.Button(pnl, wx.ID_CANCEL)
btnsizer.AddButton(btn)
btnsizer.Realize()
sizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)
pnl.SetSizer(sizer)
sizer.Fit(self)
self.topsizer=sizer
self.CenterOnParent()
self._onSpin(None)
def _onSpin(self,event):
'Called to load info about the selected version in the dialog'
ver = self.spin.GetValue()
d = GSASIIpath.svnGetLog(version=ver)
date = d.get('date','?').split('T')[0]
s = '(Version '+str(ver)+' created '+date
s += ' by '+d.get('author','?')+')'
msg = d.get('msg')
if msg: s += '\n\nComment: '+msg
self.text.SetLabel(s)
self.topsizer.Fit(self)
[docs] def getVersion(self):
'Get the version number in the dialog'
return self.spin.GetValue()
################################################################################
[docs]class MyHelp(wx.Menu):
'''
A class that creates the contents of a help menu.
The menu will start with two entries:
* 'Help on <helpType>': where helpType is a reference to an HTML page to
be opened
* About: opens an About dialog using OnHelpAbout. N.B. on the Mac this
gets moved to the App menu to be consistent with Apple style.
NOTE: for this to work properly with respect to system menus, the title
for the menu must be &Help, or it will not be processed properly:
::
menu.Append(menu=MyHelp(self,...),title="&Help")
'''
def __init__(self,frame,helpType=None,helpLbl=None,morehelpitems=[],title=''):
wx.Menu.__init__(self,title)
self.HelpById = {}
self.frame = frame
self.Append(help='', id=wx.ID_ABOUT, kind=wx.ITEM_NORMAL,
text='&About GSAS-II')
frame.Bind(wx.EVT_MENU, self.OnHelpAbout, id=wx.ID_ABOUT)
if GSASIIpath.whichsvn():
helpobj = self.Append(
help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
text='&Check for updates')
frame.Bind(wx.EVT_MENU, self.OnCheckUpdates, helpobj)
helpobj = self.Append(
help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
text='&Regress to an old GSAS-II version')
frame.Bind(wx.EVT_MENU, self.OnSelectVersion, helpobj)
for lbl,indx in morehelpitems:
helpobj = self.Append(text=lbl,
id=wx.ID_ANY, kind=wx.ITEM_NORMAL)
frame.Bind(wx.EVT_MENU, self.OnHelpById, helpobj)
self.HelpById[helpobj.GetId()] = indx
# add a help item only when helpType is specified
if helpType is not None:
self.AppendSeparator()
if helpLbl is None: helpLbl = helpType
helpobj = self.Append(text='Help on '+helpLbl,
id=wx.ID_ANY, kind=wx.ITEM_NORMAL)
frame.Bind(wx.EVT_MENU, self.OnHelpById, helpobj)
self.HelpById[helpobj.GetId()] = helpType
[docs] def OnHelpById(self,event):
'''Called when Help on... is pressed in a menu. Brings up
a web page for documentation.
'''
helpType = self.HelpById.get(event.GetId())
if helpType is None:
print 'Error: help lookup failed!',event.GetEventObject()
print 'id=',event.GetId()
else:
if helpType == 'Tutorials':
self.frame.Tutorials = True
ShowHelp(helpType,self.frame)
[docs] def OnHelpAbout(self, event):
"Display an 'About GSAS-II' box"
global __version__
info = wx.AboutDialogInfo()
info.Name = 'GSAS-II'
ver = GSASIIpath.svnGetRev()
if ver:
info.Version = 'Revision '+str(ver)+' (svn), version '+__version__
else:
info.Version = 'Revision '+str(GSASIIpath.GetVersionNumber())+' (.py files), version '+__version__
#info.Developers = ['Robert B. Von Dreele','Brian H. Toby']
info.Copyright = ('(c) ' + time.strftime('%Y') +
''' Argonne National Laboratory
This product includes software developed
by the UChicago Argonne, LLC, as
Operator of Argonne National Laboratory.''')
info.Description = '''General Structure Analysis System-II (GSAS-II)
Robert B. Von Dreele and Brian H. Toby
Please cite as:
B.H. Toby & R.B. Von Dreele, J. Appl. Cryst. 46, 544-549 (2013) '''
info.WebSite = ("https://subversion.xray.aps.anl.gov/trac/pyGSAS","GSAS-II home page")
wx.AboutBox(info)
[docs] def OnCheckUpdates(self,event):
'''Check if the GSAS-II repository has an update for the current source files
and perform that update if requested.
'''
if not GSASIIpath.whichsvn():
dlg = wx.MessageDialog(self.frame,
'No Subversion','Cannot update GSAS-II because subversion (svn) was not found.',
wx.OK)
dlg.ShowModal()
dlg.Destroy()
return
wx.BeginBusyCursor()
local = GSASIIpath.svnGetRev()
if local is None:
wx.EndBusyCursor()
dlg = wx.MessageDialog(self.frame,
'Unable to run subversion on the GSAS-II current directory. Is GSAS-II installed correctly?',
'Subversion error',
wx.OK)
dlg.ShowModal()
dlg.Destroy()
return
print 'Installed GSAS-II version: '+local
repos = GSASIIpath.svnGetRev(local=False)
wx.EndBusyCursor()
if repos is None:
dlg = wx.MessageDialog(self.frame,
'Unable to access the GSAS-II server. Is this computer on the internet?',
'Server unavailable',
wx.OK)
dlg.ShowModal()
dlg.Destroy()
return
print 'GSAS-II version on server: '+repos
if local == repos:
dlg = wx.MessageDialog(self.frame,
'GSAS-II is up-to-date. Version '+local+' is already loaded.',
'GSAS-II Up-to-date',
wx.OK)
dlg.ShowModal()
dlg.Destroy()
return
mods = GSASIIpath.svnFindLocalChanges()
if mods:
dlg = wx.MessageDialog(self.frame,
'You have version '+local+
' of GSAS-II installed, but the current version is '+repos+
'. However, '+str(len(mods))+
' file(s) on your local computer have been modified.'
' Updating will attempt to merge your local changes with '
'the latest GSAS-II version, but if '
'conflicts arise, local changes will be '
'discarded. It is also possible that the '
'local changes my prevent GSAS-II from running. '
'Press OK to start an update if this is acceptable:',
'Local GSAS-II Mods',
wx.OK|wx.CANCEL)
if dlg.ShowModal() != wx.ID_OK:
dlg.Destroy()
return
else:
dlg.Destroy()
else:
dlg = wx.MessageDialog(self.frame,
'You have version '+local+
' of GSAS-II installed, but the current version is '+repos+
'. Press OK to start an update:',
'GSAS-II Updates',
wx.OK|wx.CANCEL)
if dlg.ShowModal() != wx.ID_OK:
dlg.Destroy()
return
dlg.Destroy()
print 'start updates'
dlg = wx.MessageDialog(self.frame,
'Your project will now be saved, GSAS-II will exit and an update '
'will be performed and GSAS-II will restart. Press Cancel to '
'abort the update',
'Start update?',
wx.OK|wx.CANCEL)
if dlg.ShowModal() != wx.ID_OK:
dlg.Destroy()
return
dlg.Destroy()
self.frame.OnFileSave(event)
GSASIIpath.svnUpdateProcess(projectfile=self.frame.GSASprojectfile)
return
[docs] def OnSelectVersion(self,event):
'''Allow the user to select a specific version of GSAS-II
'''
if not GSASIIpath.whichsvn():
dlg = wx.MessageDialog(self,'No Subversion','Cannot update GSAS-II because subversion (svn) '+
'was not found.'
,wx.OK)
dlg.ShowModal()
return
local = GSASIIpath.svnGetRev()
if local is None:
dlg = wx.MessageDialog(self.frame,
'Unable to run subversion on the GSAS-II current directory. Is GSAS-II installed correctly?',
'Subversion error',
wx.OK)
dlg.ShowModal()
return
mods = GSASIIpath.svnFindLocalChanges()
if mods:
dlg = wx.MessageDialog(self.frame,
'You have version '+local+
' of GSAS-II installed'
'. However, '+str(len(mods))+
' file(s) on your local computer have been modified.'
' Downdating will attempt to merge your local changes with '
'the selected GSAS-II version. '
'Downdating is not encouraged because '
'if merging is not possible, your local changes will be '
'discarded. It is also possible that the '
'local changes my prevent GSAS-II from running. '
'Press OK to continue anyway.',
'Local GSAS-II Mods',
wx.OK|wx.CANCEL)
if dlg.ShowModal() != wx.ID_OK:
dlg.Destroy()
return
dlg.Destroy()
dlg = downdate(parent=self.frame)
if dlg.ShowModal() == wx.ID_OK:
ver = dlg.getVersion()
else:
dlg.Destroy()
return
dlg.Destroy()
print('start regress to '+str(ver))
GSASIIpath.svnUpdateProcess(
projectfile=self.frame.GSASprojectfile,
version=str(ver)
)
self.frame.OnFileSave(event)
return
################################################################################
[docs]class AddHelp(wx.Menu):
'''For the Mac: creates an entry to the help menu of type
'Help on <helpType>': where helpType is a reference to an HTML page to
be opened.
NOTE: when appending this menu (menu.Append) be sure to set the title to
'&Help' so that wx handles it correctly.
'''
def __init__(self,frame,helpType,helpLbl=None,title=''):
wx.Menu.__init__(self,title)
self.frame = frame
if helpLbl is None: helpLbl = helpType
# add a help item only when helpType is specified
helpobj = self.Append(text='Help on '+helpLbl,
id=wx.ID_ANY, kind=wx.ITEM_NORMAL)
frame.Bind(wx.EVT_MENU, self.OnHelpById, helpobj)
self.HelpById = helpType
[docs] def OnHelpById(self,event):
'''Called when Help on... is pressed in a menu. Brings up
a web page for documentation.
'''
ShowHelp(self.HelpById,self.frame)
################################################################################
[docs]class MyHtmlPanel(wx.Panel):
'''Defines a panel to display HTML help information, as an alternative to
displaying help information in a web browser.
'''
def __init__(self, frame, id):
self.frame = frame
wx.Panel.__init__(self, frame, id)
sizer = wx.BoxSizer(wx.VERTICAL)
back = wx.Button(self, -1, "Back")
back.Bind(wx.EVT_BUTTON, self.OnBack)
self.htmlwin = G2HtmlWindow(self, id, size=(750,450))
sizer.Add(self.htmlwin, 1,wx.EXPAND)
sizer.Add(back, 0, wx.ALIGN_LEFT, 0)
self.SetSizer(sizer)
sizer.Fit(frame)
self.Bind(wx.EVT_SIZE,self.OnHelpSize)
def OnHelpSize(self,event): #does the job but weirdly!!
anchor = self.htmlwin.GetOpenedAnchor()
if anchor:
self.htmlwin.ScrollToAnchor(anchor)
wx.CallAfter(self.htmlwin.ScrollToAnchor,anchor)
event.Skip()
def OnBack(self, event):
self.htmlwin.HistoryBack()
def LoadFile(self,file):
pos = file.rfind('#')
if pos != -1:
helpfile = file[:pos]
helpanchor = file[pos+1:]
else:
helpfile = file
helpanchor = None
self.htmlwin.LoadPage(helpfile)
if helpanchor is not None:
self.htmlwin.ScrollToAnchor(helpanchor)
xs,ys = self.htmlwin.GetViewStart()
self.htmlwin.Scroll(xs,ys-1)
[docs]class G2HtmlWindow(wx.html.HtmlWindow):
'''Displays help information in a primitive HTML browser type window
'''
def __init__(self, parent, *args, **kwargs):
self.parent = parent
wx.html.HtmlWindow.__init__(self, parent, *args, **kwargs)
def LoadPage(self, *args, **kwargs):
wx.html.HtmlWindow.LoadPage(self, *args, **kwargs)
self.TitlePage()
def OnLinkClicked(self, *args, **kwargs):
wx.html.HtmlWindow.OnLinkClicked(self, *args, **kwargs)
xs,ys = self.GetViewStart()
self.Scroll(xs,ys-1)
self.TitlePage()
def HistoryBack(self, *args, **kwargs):
wx.html.HtmlWindow.HistoryBack(self, *args, **kwargs)
self.TitlePage()
def TitlePage(self):
self.parent.frame.SetTitle(self.GetOpenedPage() + ' -- ' +
self.GetOpenedPageTitle())
################################################################################
[docs]class DataFrame(wx.Frame):
'''Create the data item window and all the entries in menus used in
that window. For Linux and windows, the menu entries are created for the
current data item window, but in the Mac the menu is accessed from all
windows. This means that a different menu is posted depending on which
data item is posted. On the Mac, all the menus contain the data tree menu
items, but additional menus are added specific to the data item.
Note that while the menus are created here,
the binding for the menus is done later in various GSASII*GUI modules,
where the functions to be called are defined.
'''
[docs] def Bind(self,eventtype,handler,*args,**kwargs):
'''Override the Bind() function: on the Mac the binding is to
the main window, so that menus operate with any window on top.
For other platforms, either wrap calls that will be logged
or call the default wx.Frame Bind() to bind to the menu item directly.
Note that bindings can be made to objects by Id or by direct reference to the
object. As a convention, when bindings are to objects, they are not logged
but when bindings are by Id, they are logged.
'''
if sys.platform == "darwin": # mac
self.G2frame.Bind(eventtype,handler,*args,**kwargs)
return
if eventtype == wx.EVT_MENU and 'id' in kwargs:
menulabels = log.SaveMenuCommand(kwargs['id'],self.G2frame,handler)
if menulabels:
#print 'intercepting bind for',handler,menulabels,kwargs['id']
wx.Frame.Bind(self,eventtype,self.G2frame.MenuBinding,*args,**kwargs)
return
wx.Frame.Bind(self,eventtype,handler,*args,**kwargs)
def _init_menus(self):
'define all GSAS-II data frame menus'
# for use where no menu or data frame help is provided
self.BlankMenu = wx.MenuBar()
# Controls
self.ControlsMenu = wx.MenuBar()
self.PrefillDataMenu(self.ControlsMenu,helpType='Controls',empty=True)
self.PostfillDataMenu(empty=True)
# Notebook
self.DataNotebookMenu = wx.MenuBar()
self.PrefillDataMenu(self.DataNotebookMenu,helpType='Notebook',empty=True)
self.PostfillDataMenu(empty=True)
# Comments
self.DataCommentsMenu = wx.MenuBar()
self.PrefillDataMenu(self.DataCommentsMenu,helpType='Comments',empty=True)
self.PostfillDataMenu(empty=True)
# Constraints - something amiss here - get weird wx C++ error after refine!
self.ConstraintMenu = wx.MenuBar()
self.PrefillDataMenu(self.ConstraintMenu,helpType='Constraints')
self.ConstraintTab = wx.Menu(title='')
self.ConstraintMenu.Append(menu=self.ConstraintTab, title='Select tab')
for id,txt in (
(wxID_CONSPHASE,'Phase'),
(wxID_CONSHAP,'Histogram/Phase'),
(wxID_CONSHIST,'Histogram'),
(wxID_CONSGLOBAL,'Global')):
self.ConstraintTab.Append(
id=id, kind=wx.ITEM_NORMAL,text=txt,
help='Select '+txt+' constraint editing tab')
self.ConstraintEdit = wx.Menu(title='')
self.ConstraintMenu.Append(menu=self.ConstraintEdit, title='Edit')
self.ConstraintEdit.Append(id=wxID_HOLDADD, kind=wx.ITEM_NORMAL,text='Add hold',
help='Add hold on a parameter value')
self.ConstraintEdit.Append(id=wxID_EQUIVADD, kind=wx.ITEM_NORMAL,text='Add equivalence',
help='Add equivalence between parameter values')
self.ConstraintEdit.Append(id=wxID_CONSTRAINTADD, kind=wx.ITEM_NORMAL,text='Add constraint',
help='Add constraint on parameter values')
self.ConstraintEdit.Append(id=wxID_FUNCTADD, kind=wx.ITEM_NORMAL,text='Add New Var',
help='Add variable composed of existing parameter')
# item = self.ConstraintEdit.Append(id=wx.ID_ANY,kind=wx.ITEM_NORMAL,text='Update GUI')
# def UpdateGSASIIconstrGUI(event):
# import GSASIIconstrGUI
# reload(GSASIIconstrGUI)
# import GSASIIobj
# reload(GSASIIobj)
# self.Bind(wx.EVT_MENU,UpdateGSASIIconstrGUI,id=item.GetId())
self.PostfillDataMenu()
# Rigid bodies
self.VectorRBEdit = wx.Menu(title='')
self.VectorRBEdit.Append(id=wxID_RIGIDBODYADD, kind=wx.ITEM_NORMAL,text='Add rigid body',
help='Add vector rigid body')
self.ResidueRBMenu = wx.Menu(title='')
self.ResidueRBMenu.Append(id=wxID_RIGIDBODYIMPORT, kind=wx.ITEM_NORMAL,text='Import XYZ',
help='Import rigid body XYZ from file')
self.ResidueRBMenu.Append(id=wxID_RESIDUETORSSEQ, kind=wx.ITEM_NORMAL,text='Define sequence',
help='Define torsion sequence')
self.ResidueRBMenu.Append(id=wxID_RIGIDBODYADD, kind=wx.ITEM_NORMAL,text='Import residues',
help='Import residue rigid bodies from macro file')
self.RigidBodyMenu = wx.MenuBar()
self.PrefillDataMenu(self.RigidBodyMenu,helpType='Rigid bodies')
self.RigidBodyMenu.Append(menu=self.VectorRBEdit, title='Edit')
self.PostfillDataMenu()
# Restraints
self.RestraintTab = wx.Menu(title='')
self.RestraintEdit = wx.Menu(title='')
self.RestraintEdit.Append(id=wxID_RESTSELPHASE, kind=wx.ITEM_NORMAL,text='Select phase',
help='Select phase')
self.RestraintEdit.Append(id=wxID_RESTRAINTADD, kind=wx.ITEM_NORMAL,text='Add restraints',
help='Add restraints')
self.RestraintEdit.Enable(wxID_RESTRAINTADD,True) #gets disenabled if macromolecule phase
self.RestraintEdit.Append(id=wxID_AARESTRAINTADD, kind=wx.ITEM_NORMAL,text='Add residue restraints',
help='Add residue based restraints for macromolecules from macro file')
self.RestraintEdit.Enable(wxID_AARESTRAINTADD,False) #gets enabled if macromolecule phase
self.RestraintEdit.Append(id=wxID_AARESTRAINTPLOT, kind=wx.ITEM_NORMAL,text='Plot residue restraints',
help='Plot selected residue based restraints for macromolecules from macro file')
self.RestraintEdit.Enable(wxID_AARESTRAINTPLOT,False) #gets enabled if macromolecule phase
self.RestraintEdit.Append(id=wxID_RESRCHANGEVAL, kind=wx.ITEM_NORMAL,text='Change value',
help='Change observed value')
self.RestraintEdit.Append(id=wxID_RESTCHANGEESD, kind=wx.ITEM_NORMAL,text='Change esd',
help='Change esd in observed value')
self.RestraintEdit.Append(id=wxID_RESTDELETE, kind=wx.ITEM_NORMAL,text='Delete restraints',
help='Delete selected restraints')
self.RestraintMenu = wx.MenuBar()
self.PrefillDataMenu(self.RestraintMenu,helpType='Restraints')
self.RestraintMenu.Append(menu=self.RestraintTab, title='Select tab')
self.RestraintMenu.Append(menu=self.RestraintEdit, title='Edit')
self.PostfillDataMenu()
# Sequential results
self.SequentialMenu = wx.MenuBar()
self.PrefillDataMenu(self.SequentialMenu,helpType='Sequential',helpLbl='Sequential Refinement')
self.SequentialFile = wx.Menu(title='')
self.SequentialMenu.Append(menu=self.SequentialFile, title='Selected Cols')
self.SequentialFile.Append(id=wxID_RENAMESEQSEL, kind=wx.ITEM_NORMAL,text='Rename',
help='Rename selected sequential refinement columns')
self.SequentialFile.Append(id=wxID_SAVESEQSEL, kind=wx.ITEM_NORMAL,text='Save as text',
help='Save selected sequential refinement results as a text file')
self.SequentialFile.Append(id=wxID_SAVESEQSELCSV, kind=wx.ITEM_NORMAL,text='Save as CSV',
help='Save selected sequential refinement results as a CSV spreadsheet file')
self.SequentialFile.Append(id=wxID_PLOTSEQSEL, kind=wx.ITEM_NORMAL,text='Plot selected',
help='Plot selected sequential refinement results')
self.SequentialPvars = wx.Menu(title='')
self.SequentialMenu.Append(menu=self.SequentialPvars, title='Pseudo Vars')
self.SequentialPvars.Append(
id=wxADDSEQVAR, kind=wx.ITEM_NORMAL,text='Add',
help='Add a new pseudo-variable')
self.SequentialPvars.Append(
id=wxDELSEQVAR, kind=wx.ITEM_NORMAL,text='Delete',
help='Delete an existing pseudo-variable')
self.SequentialPvars.Append(
id=wxEDITSEQVAR, kind=wx.ITEM_NORMAL,text='Edit',
help='Edit an existing pseudo-variable')
self.SequentialPfit = wx.Menu(title='')
self.SequentialMenu.Append(menu=self.SequentialPfit, title='Parametric Fit')
self.SequentialPfit.Append(
id=wxADDPARFIT, kind=wx.ITEM_NORMAL,text='Add equation',
help='Add a new equation to minimize')
self.SequentialPfit.Append(
id=wxCOPYPARFIT, kind=wx.ITEM_NORMAL,text='Copy equation',
help='Copy an equation to minimize - edit it next')
self.SequentialPfit.Append(
id=wxDELPARFIT, kind=wx.ITEM_NORMAL,text='Delete equation',
help='Delete an equation for parametric minimization')
self.SequentialPfit.Append(
id=wxEDITPARFIT, kind=wx.ITEM_NORMAL,text='Edit equation',
help='Edit an existing parametric minimization equation')
self.SequentialPfit.Append(
id=wxDOPARFIT, kind=wx.ITEM_NORMAL,text='Fit to equation(s)',
help='Perform a parametric minimization')
self.PostfillDataMenu()
# PWDR & SASD
self.PWDRMenu = wx.MenuBar()
self.PrefillDataMenu(self.PWDRMenu,helpType='PWDR Analysis',helpLbl='Powder Fit Error Analysis')
self.ErrorAnal = wx.Menu(title='')
self.PWDRMenu.Append(menu=self.ErrorAnal,title='Commands')
self.ErrorAnal.Append(id=wxID_PWDANALYSIS,kind=wx.ITEM_NORMAL,text='Error Analysis',
help='Error analysis on powder pattern')
self.ErrorAnal.Append(id=wxID_PWDCOPY,kind=wx.ITEM_NORMAL,text='Copy params',
help='Copy of PWDR parameters')
self.PostfillDataMenu()
# HKLF
self.HKLFMenu = wx.MenuBar()
self.PrefillDataMenu(self.HKLFMenu,helpType='HKLF Analysis',helpLbl='HKLF Fit Error Analysis')
self.ErrorAnal = wx.Menu(title='')
self.HKLFMenu.Append(menu=self.ErrorAnal,title='Commands')
self.ErrorAnal.Append(id=wxID_PWDANALYSIS,kind=wx.ITEM_NORMAL,text='Error Analysis',
help='Error analysis on single crystal data')
self.ErrorAnal.Append(id=wxID_PWD3DHKLPLOT,kind=wx.ITEM_NORMAL,text='Plot 3D HKLs',
help='Plot HKLs from single crystal data in 3D')
self.ErrorAnal.Append(id=wxID_PWDCOPY,kind=wx.ITEM_NORMAL,text='Copy params',
help='Copy of HKLF parameters')
self.PostfillDataMenu()
# PDR / Limits
self.LimitMenu = wx.MenuBar()
self.PrefillDataMenu(self.LimitMenu,helpType='Limits')
self.LimitEdit = wx.Menu(title='')
self.LimitMenu.Append(menu=self.LimitEdit, title='Edit')
self.LimitEdit.Append(id=wxID_LIMITCOPY, kind=wx.ITEM_NORMAL,text='Copy',
help='Copy limits to other histograms')
self.LimitEdit.Append(id=wxID_ADDEXCLREGION, kind=wx.ITEM_NORMAL,text='Add exclude',
help='Add excluded region - select a point on plot; drag to adjust')
self.PostfillDataMenu()
# PDR / Background
self.BackMenu = wx.MenuBar()
self.PrefillDataMenu(self.BackMenu,helpType='Background')
self.BackEdit = wx.Menu(title='')
self.BackMenu.Append(menu=self.BackEdit, title='File')
self.BackEdit.Append(id=wxID_BACKCOPY, kind=wx.ITEM_NORMAL,text='Copy',
help='Copy background parameters to other histograms')
self.BackEdit.Append(id=wxID_BACKFLAGCOPY, kind=wx.ITEM_NORMAL,text='Copy flags',
help='Copy background refinement flags to other histograms')
self.PostfillDataMenu()
# PDR / Instrument Parameters
self.InstMenu = wx.MenuBar()
self.PrefillDataMenu(self.InstMenu,helpType='Instrument Parameters')
self.InstEdit = wx.Menu(title='')
self.InstMenu.Append(menu=self.InstEdit, title='Operations')
self.InstEdit.Append(help='Calibrate from indexed peaks',
id=wxID_INSTCALIB, kind=wx.ITEM_NORMAL,text='Calibrate')
self.InstEdit.Append(help='Reset instrument profile parameters to default',
id=wxID_INSTLOAD, kind=wx.ITEM_NORMAL,text='Load profile...')
self.InstEdit.Append(help='Load instrument profile parameters from file',
id=wxID_INSTSAVE, kind=wx.ITEM_NORMAL,text='Save profile...')
self.InstEdit.Append(help='Save instrument profile parameters to file',
id=wxID_INSTPRMRESET, kind=wx.ITEM_NORMAL,text='Reset profile')
self.InstEdit.Append(help='Copy instrument profile parameters to other histograms',
id=wxID_INSTCOPY, kind=wx.ITEM_NORMAL,text='Copy')
self.InstEdit.Append(id=wxID_INSTFLAGCOPY, kind=wx.ITEM_NORMAL,text='Copy flags',
help='Copy instrument parameter refinement flags to other histograms')
# self.InstEdit.Append(help='Change radiation type (Ka12 - synch)',
# id=wxID_CHANGEWAVETYPE, kind=wx.ITEM_NORMAL,text='Change radiation')
self.InstEdit.Append(id=wxID_INST1VAL, kind=wx.ITEM_NORMAL,text='Set one value',
help='Set one instrument parameter value across multiple histograms')
self.PostfillDataMenu()
# PDR / Sample Parameters
self.SampleMenu = wx.MenuBar()
self.PrefillDataMenu(self.SampleMenu,helpType='Sample Parameters')
self.SampleEdit = wx.Menu(title='')
self.SampleMenu.Append(menu=self.SampleEdit, title='Command')
self.SetScale = self.SampleEdit.Append(id=wxID_SETSCALE, kind=wx.ITEM_NORMAL,text='Set scale',
help='Set scale by matching to another histogram')
self.SampleEdit.Append(id=wxID_SAMPLELOAD, kind=wx.ITEM_NORMAL,text='Load',
help='Load sample parameters from file')
self.SampleEdit.Append(id=wxID_SAMPLESAVE, kind=wx.ITEM_NORMAL,text='Save',
help='Save sample parameters to file')
self.SampleEdit.Append(id=wxID_SAMPLECOPY, kind=wx.ITEM_NORMAL,text='Copy',
help='Copy refinable and most other sample parameters to other histograms')
self.SampleEdit.Append(id=wxID_SAMPLECOPYSOME, kind=wx.ITEM_NORMAL,text='Copy selected...',
help='Copy selected sample parameters to other histograms')
self.SampleEdit.Append(id=wxID_SAMPLEFLAGCOPY, kind=wx.ITEM_NORMAL,text='Copy flags',
help='Copy sample parameter refinement flags to other histograms')
self.SampleEdit.Append(id=wxID_SAMPLE1VAL, kind=wx.ITEM_NORMAL,text='Set one value',
help='Set one sample parameter value across multiple histograms')
self.PostfillDataMenu()
self.SetScale.Enable(False)
# PDR / Peak List
self.PeakMenu = wx.MenuBar()
self.PrefillDataMenu(self.PeakMenu,helpType='Peak List')
self.PeakEdit = wx.Menu(title='')
self.PeakMenu.Append(menu=self.PeakEdit, title='Peak Fitting')
self.AutoSearch = self.PeakEdit.Append(help='Automatic peak search',
id=wxID_AUTOSEARCH, kind=wx.ITEM_NORMAL,text='Auto search')
self.UnDo = self.PeakEdit.Append(help='Undo last least squares refinement',
id=wxID_UNDO, kind=wx.ITEM_NORMAL,text='UnDo')
self.PeakFit = self.PeakEdit.Append(id=wxID_LSQPEAKFIT, kind=wx.ITEM_NORMAL,text='Peakfit',
help='Peak fitting' )
self.PFOneCycle = self.PeakEdit.Append(id=wxID_LSQONECYCLE, kind=wx.ITEM_NORMAL,text='Peakfit one cycle',
help='One cycle of Peak fitting' )
self.PeakEdit.Append(id=wxID_RESETSIGGAM, kind=wx.ITEM_NORMAL,
text='Reset sig and gam',help='Reset sigma and gamma to global fit' )
self.PeakCopy = self.PeakEdit.Append(help='Copy peaks to other histograms',
id=wxID_PEAKSCOPY, kind=wx.ITEM_NORMAL,text='Peak copy')
self.SeqPeakFit = self.PeakEdit.Append(id=wxID_SEQPEAKFIT, kind=wx.ITEM_NORMAL,text='Seq PeakFit',
help='Sequential Peak fitting for all histograms' )
self.PeakEdit.Append(id=wxID_CLEARPEAKS, kind=wx.ITEM_NORMAL,text='Clear peaks',
help='Clear the peak list' )
self.PostfillDataMenu()
self.UnDo.Enable(False)
self.PeakFit.Enable(False)
self.PFOneCycle.Enable(False)
self.AutoSearch.Enable(True)
# PDR / Index Peak List
self.IndPeaksMenu = wx.MenuBar()
self.PrefillDataMenu(self.IndPeaksMenu,helpType='Index Peak List')
self.IndPeaksEdit = wx.Menu(title='')
self.IndPeaksMenu.Append(menu=self.IndPeaksEdit,title='Operations')
self.IndPeaksEdit.Append(help='Load/Reload index peaks from peak list',id=wxID_INDXRELOAD,
kind=wx.ITEM_NORMAL,text='Load/Reload')
self.PostfillDataMenu()
# PDR / Unit Cells List
self.IndexMenu = wx.MenuBar()
self.PrefillDataMenu(self.IndexMenu,helpType='Unit Cells List')
self.IndexEdit = wx.Menu(title='')
self.IndexMenu.Append(menu=self.IndexEdit, title='Cell Index/Refine')
self.IndexPeaks = self.IndexEdit.Append(help='', id=wxID_INDEXPEAKS, kind=wx.ITEM_NORMAL,
text='Index Cell')
self.CopyCell = self.IndexEdit.Append( id=wxID_COPYCELL, kind=wx.ITEM_NORMAL,text='Copy Cell',
help='Copy selected unit cell from indexing to cell refinement fields')
self.RefineCell = self.IndexEdit.Append( id=wxID_REFINECELL, kind=wx.ITEM_NORMAL,
text='Refine Cell',help='Refine unit cell parameters from indexed peaks')
self.MakeNewPhase = self.IndexEdit.Append( id=wxID_MAKENEWPHASE, kind=wx.ITEM_NORMAL,
text='Make new phase',help='Make new phase from selected unit cell')
self.PostfillDataMenu()
self.IndexPeaks.Enable(False)
self.CopyCell.Enable(False)
self.RefineCell.Enable(False)
self.MakeNewPhase.Enable(False)
# PDR / Reflection Lists
self.ReflMenu = wx.MenuBar()
self.PrefillDataMenu(self.ReflMenu,helpType='Reflection List')
self.ReflEdit = wx.Menu(title='')
self.ReflMenu.Append(menu=self.ReflEdit, title='Reflection List')
self.SelectPhase = self.ReflEdit.Append(help='Select phase for reflection list',id=wxID_SELECTPHASE,
kind=wx.ITEM_NORMAL,text='Select phase')
self.ReflEdit.Append(id=wxID_PWDHKLPLOT,kind=wx.ITEM_NORMAL,text='Plot HKLs',
help='Plot HKLs from powder pattern')
self.ReflEdit.Append(id=wxID_PWD3DHKLPLOT,kind=wx.ITEM_NORMAL,text='Plot 3D HKLs',
help='Plot HKLs from powder pattern in 3D')
self.PostfillDataMenu()
#SASD & REFL/ Substance editor
self.SubstanceMenu = wx.MenuBar()
self.PrefillDataMenu(self.SubstanceMenu,helpType='Substances')
self.SubstanceEdit = wx.Menu(title='')
self.SubstanceMenu.Append(menu=self.SubstanceEdit, title='Edit')
self.SubstanceEdit.Append(id=wxID_LOADSUBSTANCE, kind=wx.ITEM_NORMAL,text='Load substance',
help='Load substance from file')
self.SubstanceEdit.Append(id=wxID_ADDSUBSTANCE, kind=wx.ITEM_NORMAL,text='Add substance',
help='Add new substance to list')
self.SubstanceEdit.Append(id=wxID_COPYSUBSTANCE, kind=wx.ITEM_NORMAL,text='Copy substances',
help='Copy substances')
self.SubstanceEdit.Append(id=wxID_DELETESUBSTANCE, kind=wx.ITEM_NORMAL,text='Delete substance',
help='Delete substance from list')
self.SubstanceEdit.Append(id=wxID_ELEMENTADD, kind=wx.ITEM_NORMAL,text='Add elements',
help='Add elements to substance')
self.SubstanceEdit.Append(id=wxID_ELEMENTDELETE, kind=wx.ITEM_NORMAL,text='Delete elements',
help='Delete elements from substance')
self.PostfillDataMenu()
# SASD/ Models
self.ModelMenu = wx.MenuBar()
self.PrefillDataMenu(self.ModelMenu,helpType='Models')
self.ModelEdit = wx.Menu(title='')
self.ModelMenu.Append(menu=self.ModelEdit, title='Models')
self.ModelEdit.Append(id=wxID_MODELADD,kind=wx.ITEM_NORMAL,text='Add',
help='Add new term to model')
self.ModelEdit.Append(id=wxID_MODELFIT, kind=wx.ITEM_NORMAL,text='Fit',
help='Fit model parameters to data')
self.SasdUndo = self.ModelEdit.Append(id=wxID_MODELUNDO, kind=wx.ITEM_NORMAL,text='Undo',
help='Undo model fit')
self.SasdUndo.Enable(False)
self.ModelEdit.Append(id=wxID_MODELFITALL, kind=wx.ITEM_NORMAL,text='Sequential fit',
help='Sequential fit of model parameters to all SASD data')
self.ModelEdit.Append(id=wxID_MODELCOPY, kind=wx.ITEM_NORMAL,text='Copy',
help='Copy model parameters to other histograms')
self.ModelEdit.Append(id=wxID_MODELCOPYFLAGS, kind=wx.ITEM_NORMAL,text='Copy flags',
help='Copy model refinement flags to other histograms')
self.PostfillDataMenu()
# IMG / Image Controls
self.ImageMenu = wx.MenuBar()
self.PrefillDataMenu(self.ImageMenu,helpType='Image Controls')
self.ImageEdit = wx.Menu(title='')
self.ImageMenu.Append(menu=self.ImageEdit, title='Operations')
self.ImageEdit.Append(help='Calibrate detector by fitting to calibrant lines',
id=wxID_IMCALIBRATE, kind=wx.ITEM_NORMAL,text='Calibrate')
self.ImageEdit.Append(help='Recalibrate detector by fitting to calibrant lines',
id=wxID_IMRECALIBRATE, kind=wx.ITEM_NORMAL,text='Recalibrate')
self.ImageEdit.Append(help='Clear calibration data points and rings',id=wxID_IMCLEARCALIB,
kind=wx.ITEM_NORMAL,text='Clear calibration')
self.ImageEdit.Append(help='Integrate selected image',id=wxID_IMINTEGRATE,
kind=wx.ITEM_NORMAL,text='Integrate')
self.ImageEdit.Append(help='Integrate all images selected from list',id=wxID_INTEGRATEALL,
kind=wx.ITEM_NORMAL,text='Integrate all')
self.ImageEdit.Append(help='Copy image controls to other images',
id=wxID_IMCOPYCONTROLS, kind=wx.ITEM_NORMAL,text='Copy Controls')
self.ImageEdit.Append(help='Save image controls to file',
id=wxID_IMSAVECONTROLS, kind=wx.ITEM_NORMAL,text='Save Controls')
self.ImageEdit.Append(help='Load image controls from file',
id=wxID_IMLOADCONTROLS, kind=wx.ITEM_NORMAL,text='Load Controls')
self.PostfillDataMenu()
# IMG / Masks
self.MaskMenu = wx.MenuBar()
self.PrefillDataMenu(self.MaskMenu,helpType='Image Masks')
self.MaskEdit = wx.Menu(title='')
self.MaskMenu.Append(menu=self.MaskEdit, title='Operations')
submenu = wx.Menu()
self.MaskEdit.AppendMenu(
wx.ID_ANY,'Create new', submenu,
help=''
)
self.MaskEdit.Append(help='Copy mask to other images',
id=wxID_MASKCOPY, kind=wx.ITEM_NORMAL,text='Copy mask')
self.MaskEdit.Append(help='Save mask to file',
id=wxID_MASKSAVE, kind=wx.ITEM_NORMAL,text='Save mask')
self.MaskEdit.Append(help='Load mask from file',
id=wxID_MASKLOAD, kind=wx.ITEM_NORMAL,text='Load mask')
submenu.Append(help='Create an arc mask with mouse input',
id=wxID_NEWMASKARC, kind=wx.ITEM_NORMAL,text='Arc mask')
submenu.Append(help='Create a frame mask with mouse input',
id=wxID_NEWMASKFRAME, kind=wx.ITEM_NORMAL,text='Frame mask')
submenu.Append(help='Create a polygon mask with mouse input',
id=wxID_NEWMASKPOLY, kind=wx.ITEM_NORMAL,text='Polygon mask')
submenu.Append(help='Create a ring mask with mouse input',
id=wxID_NEWMASKRING, kind=wx.ITEM_NORMAL,text='Ring mask')
submenu.Append(help='Create a spot mask with mouse input',
id=wxID_NEWMASKSPOT, kind=wx.ITEM_NORMAL,text='Spot mask')
self.PostfillDataMenu()
# IMG / Stress/Strain
self.StrStaMenu = wx.MenuBar()
self.PrefillDataMenu(self.StrStaMenu,helpType='Stress/Strain')
self.StrStaEdit = wx.Menu(title='')
self.StrStaMenu.Append(menu=self.StrStaEdit, title='Operations')
self.StrStaEdit.Append(help='Append d-zero for one ring',
id=wxID_APPENDDZERO, kind=wx.ITEM_NORMAL,text='Append d-zero')
self.StrStaEdit.Append(help='Fit stress/strain data',
id=wxID_STRSTAFIT, kind=wx.ITEM_NORMAL,text='Fit stress/strain')
self.StrStaEdit.Append(help='Update d-zero from ave d-zero',
id=wxID_UPDATEDZERO, kind=wx.ITEM_NORMAL,text='Update d-zero')
self.StrStaEdit.Append(help='Fit stress/strain data for all images',
id=wxID_STRSTAALLFIT, kind=wx.ITEM_NORMAL,text='All image fit')
self.StrStaEdit.Append(help='Copy stress/strain data to other images',
id=wxID_STRSTACOPY, kind=wx.ITEM_NORMAL,text='Copy stress/strain')
self.StrStaEdit.Append(help='Save stress/strain data to file',
id=wxID_STRSTASAVE, kind=wx.ITEM_NORMAL,text='Save stress/strain')
self.StrStaEdit.Append(help='Load stress/strain data from file',
id=wxID_STRSTALOAD, kind=wx.ITEM_NORMAL,text='Load stress/strain')
self.PostfillDataMenu()
# PDF / PDF Controls
self.PDFMenu = wx.MenuBar()
self.PrefillDataMenu(self.PDFMenu,helpType='PDF Controls')
self.PDFEdit = wx.Menu(title='')
self.PDFMenu.Append(menu=self.PDFEdit, title='PDF Controls')
self.PDFEdit.Append(help='Add element to sample composition',id=wxID_PDFADDELEMENT, kind=wx.ITEM_NORMAL,
text='Add element')
self.PDFEdit.Append(help='Delete element from sample composition',id=wxID_PDFDELELEMENT, kind=wx.ITEM_NORMAL,
text='Delete element')
self.PDFEdit.Append(help='Copy PDF controls', id=wxID_PDFCOPYCONTROLS, kind=wx.ITEM_NORMAL,
text='Copy controls')
# self.PDFEdit.Append(help='Load PDF controls from file',id=wxID_PDFLOADCONTROLS, kind=wx.ITEM_NORMAL,
# text='Load Controls')
# self.PDFEdit.Append(help='Save PDF controls to file', id=wxID_PDFSAVECONTROLS, kind=wx.ITEM_NORMAL,
# text='Save controls')
self.PDFEdit.Append(help='Compute PDF', id=wxID_PDFCOMPUTE, kind=wx.ITEM_NORMAL,
text='Compute PDF')
self.PDFEdit.Append(help='Compute all PDFs', id=wxID_PDFCOMPUTEALL, kind=wx.ITEM_NORMAL,
text='Compute all PDFs')
self.PostfillDataMenu()
# Phase / General tab
self.DataGeneral = wx.MenuBar()
self.PrefillDataMenu(self.DataGeneral,helpType='General', helpLbl='Phase/General')
self.DataGeneral.Append(menu=wx.Menu(title=''),title='Select tab')
self.GeneralCalc = wx.Menu(title='')
self.DataGeneral.Append(menu=self.GeneralCalc,title='Compute')
self.GeneralCalc.Append(help='Compute Fourier map',id=wxID_FOURCALC, kind=wx.ITEM_NORMAL,
text='Fourier map')
self.GeneralCalc.Append(help='Search Fourier map',id=wxID_FOURSEARCH, kind=wx.ITEM_NORMAL,
text='Search map')
self.GeneralCalc.Append(help='Run charge flipping',id=wxID_CHARGEFLIP, kind=wx.ITEM_NORMAL,
text='Charge flipping')
self.GeneralCalc.Append(help='Clear map',id=wxID_FOURCLEAR, kind=wx.ITEM_NORMAL,
text='Clear map')
self.GeneralCalc.Append(help='Run Monte Carlo - Simulated Annealing',id=wxID_SINGLEMCSA, kind=wx.ITEM_NORMAL,
text='MC/SA')
self.GeneralCalc.Append(help='Run Monte Carlo - Simulated Annealing on multiprocessors',id=wxID_MULTIMCSA, kind=wx.ITEM_NORMAL,
text='Multi MC/SA') #currently not useful
self.PostfillDataMenu()
# Phase / Data tab
self.DataMenu = wx.MenuBar()
self.PrefillDataMenu(self.DataMenu,helpType='Data', helpLbl='Phase/Data')
self.DataMenu.Append(menu=wx.Menu(title=''),title='Select tab')
self.DataEdit = wx.Menu(title='')
self.DataMenu.Append(menu=self.DataEdit, title='Edit')
self.DataEdit.Append(id=wxID_PWDRADD, kind=wx.ITEM_NORMAL,text='Add powder histograms',
help='Select new powder histograms to be used for this phase')
self.DataEdit.Append(id=wxID_HKLFADD, kind=wx.ITEM_NORMAL,text='Add single crystal histograms',
help='Select new single crystal histograms to be used for this phase')
self.DataEdit.Append(id=wxID_DATADELETE, kind=wx.ITEM_NORMAL,text='Remove histograms',
help='Remove histograms from use for this phase')
self.PostfillDataMenu()
# Phase / Atoms tab
self.AtomsMenu = wx.MenuBar()
self.PrefillDataMenu(self.AtomsMenu,helpType='Atoms')
self.AtomsMenu.Append(menu=wx.Menu(title=''),title='Select tab')
self.AtomEdit = wx.Menu(title='')
self.AtomCompute = wx.Menu(title='')
self.AtomsMenu.Append(menu=self.AtomEdit, title='Edit')
self.AtomsMenu.Append(menu=self.AtomCompute, title='Compute')
self.AtomEdit.Append(id=wxID_ATOMSEDITADD, kind=wx.ITEM_NORMAL,text='Append atom',
help='Appended as an H atom')
self.AtomEdit.Append(id=wxID_ATOMSVIEWADD, kind=wx.ITEM_NORMAL,text='Append view point',
help='Appended as an H atom')
self.AtomEdit.Append(id=wxID_ATOMSEDITINSERT, kind=wx.ITEM_NORMAL,text='Insert atom',
help='Select atom row to insert before; inserted as an H atom')
self.AtomEdit.Append(id=wxID_ATOMVIEWINSERT, kind=wx.ITEM_NORMAL,text='Insert view point',
help='Select atom row to insert before; inserted as an H atom')
self.AtomEdit.Append(id=wxID_ATOMMOVE, kind=wx.ITEM_NORMAL,text='Move atom to view point',
help='Select single atom to move')
self.AtomEdit.Append(id=wxID_ATOMSEDITDELETE, kind=wx.ITEM_NORMAL,text='Delete atom',
help='Select atoms to delete first')
self.AtomEdit.Append(id=wxID_ATOMSREFINE, kind=wx.ITEM_NORMAL,text='Set atom refinement flags',
help='Select atoms to refine first')
self.AtomEdit.Append(id=wxID_ATOMSMODIFY, kind=wx.ITEM_NORMAL,text='Modify atom parameters',
help='Select atoms to modify first')
self.AtomEdit.Append(id=wxID_ATOMSTRANSFORM, kind=wx.ITEM_NORMAL,text='Transform atoms',
help='Select atoms to transform first')
self.AtomEdit.Append(id=wxID_RELOADDRAWATOMS, kind=wx.ITEM_NORMAL,text='Reload draw atoms',
help='Reload atom drawing list')
submenu = wx.Menu()
self.AtomEdit.AppendMenu(wx.ID_ANY, 'Reimport atoms', submenu,
help='Reimport atoms from file; sequence must match')
# setup a cascade menu for the formats that have been defined
self.ReImportMenuId = {} # points to readers for each menu entry
for reader in self.G2frame.ImportPhaseReaderlist:
item = submenu.Append(
wx.ID_ANY,help=reader.longFormatName,
kind=wx.ITEM_NORMAL,text='reimport coordinates from '+reader.formatName+' file')
self.ReImportMenuId[item.GetId()] = reader
item = submenu.Append(
wx.ID_ANY,
help='Reimport coordinates, try to determine format from file',
kind=wx.ITEM_NORMAL,
text='guess format from file')
self.ReImportMenuId[item.GetId()] = None # try all readers
self.AtomCompute.Append(id=wxID_ATOMSDISAGL, kind=wx.ITEM_NORMAL,text='Show Distances && Angles',
help='Compute distances & angles for selected atoms')
self.AtomCompute.Append(id=wxID_ATOMSPDISAGL, kind=wx.ITEM_NORMAL,text='Save Distances && Angles',
help='Compute distances & angles for selected atoms')
self.AtomCompute.ISOcalc = self.AtomCompute.Append(
id=wxID_ISODISP, kind=wx.ITEM_NORMAL,
text='Compute ISODISTORT mode values',
help='Compute values of ISODISTORT modes from atom parameters')
self.PostfillDataMenu()
# Phase / Imcommensurate "waves" tab
self.WavesData = wx.MenuBar()
self.PrefillDataMenu(self.WavesData,helpType='Wave Data', helpLbl='Imcommensurate wave data')
self.WavesData.Append(menu=wx.Menu(title=''),title='Select tab')
self.PostfillDataMenu()
# Phase / Draw Options tab
self.DataDrawOptions = wx.MenuBar()
self.PrefillDataMenu(self.DataDrawOptions,helpType='Draw Options', helpLbl='Phase/Draw Options')
self.DataDrawOptions.Append(menu=wx.Menu(title=''),title='Select tab')
self.PostfillDataMenu()
# Phase / Draw Atoms tab
self.DrawAtomsMenu = wx.MenuBar()
self.PrefillDataMenu(self.DrawAtomsMenu,helpType='Draw Atoms')
self.DrawAtomsMenu.Append(menu=wx.Menu(title=''),title='Select tab')
self.DrawAtomEdit = wx.Menu(title='')
self.DrawAtomCompute = wx.Menu(title='')
self.DrawAtomRestraint = wx.Menu(title='')
self.DrawAtomRigidBody = wx.Menu(title='')
self.DrawAtomsMenu.Append(menu=self.DrawAtomEdit, title='Edit')
self.DrawAtomsMenu.Append(menu=self.DrawAtomCompute,title='Compute')
self.DrawAtomsMenu.Append(menu=self.DrawAtomRestraint, title='Restraints')
self.DrawAtomsMenu.Append(menu=self.DrawAtomRigidBody, title='Rigid body')
self.DrawAtomEdit.Append(id=wxID_DRAWATOMSTYLE, kind=wx.ITEM_NORMAL,text='Atom style',
help='Select atoms first')
self.DrawAtomEdit.Append(id=wxID_DRAWATOMLABEL, kind=wx.ITEM_NORMAL,text='Atom label',
help='Select atoms first')
self.DrawAtomEdit.Append(id=wxID_DRAWATOMCOLOR, kind=wx.ITEM_NORMAL,text='Atom color',
help='Select atoms first')
self.DrawAtomEdit.Append(id=wxID_DRAWATOMRESETCOLOR, kind=wx.ITEM_NORMAL,text='Reset atom colors',
help='Resets all atom colors to defaults')
self.DrawAtomEdit.Append(id=wxID_DRAWVIEWPOINT, kind=wx.ITEM_NORMAL,text='View point',
help='View point is 1st atom selected')
self.DrawAtomEdit.Append(id=wxID_DRAWADDEQUIV, kind=wx.ITEM_NORMAL,text='Add atoms',
help='Add symmetry & cell equivalents to drawing set from selected atoms')
self.DrawAtomEdit.Append(id=wxID_DRAWTRANSFORM, kind=wx.ITEM_NORMAL,text='Transform atoms',
help='Transform selected atoms by symmetry & cell translations')
self.DrawAtomEdit.Append(id=wxID_DRAWFILLCOORD, kind=wx.ITEM_NORMAL,text='Fill CN-sphere',
help='Fill coordination sphere for selected atoms')
self.DrawAtomEdit.Append(id=wxID_DRAWFILLCELL, kind=wx.ITEM_NORMAL,text='Fill unit cell',
help='Fill unit cell with selected atoms')
self.DrawAtomEdit.Append(id=wxID_DRAWDELETE, kind=wx.ITEM_NORMAL,text='Delete atoms',
help='Delete atoms from drawing set')
self.DrawAtomCompute.Append(id=wxID_DRAWDISTVP, kind=wx.ITEM_NORMAL,text='View pt. dist.',
help='Compute distance of selected atoms from view point')
self.DrawAtomCompute.Append(id=wxID_DRAWDISAGLTOR, kind=wx.ITEM_NORMAL,text='Dist. Ang. Tors.',
help='Compute distance, angle or torsion for 2-4 selected atoms')
self.DrawAtomCompute.Append(id=wxID_DRAWPLANE, kind=wx.ITEM_NORMAL,text='Best plane',
help='Compute best plane for 4+ selected atoms')
self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRBOND, kind=wx.ITEM_NORMAL,text='Add bond restraint',
help='Add bond restraint for selected atoms (2)')
self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRANGLE, kind=wx.ITEM_NORMAL,text='Add angle restraint',
help='Add angle restraint for selected atoms (3: one end 1st)')
self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRPLANE, kind=wx.ITEM_NORMAL,text='Add plane restraint',
help='Add plane restraint for selected atoms (4+)')
self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRCHIRAL, kind=wx.ITEM_NORMAL,text='Add chiral restraint',
help='Add chiral restraint for selected atoms (4: center atom 1st)')
self.DrawAtomRigidBody.Append(id=wxID_DRAWDEFINERB, kind=wx.ITEM_NORMAL,text='Define rigid body',
help='Define rigid body with selected atoms')
self.PostfillDataMenu()
# Phase / MCSA tab
self.MCSAMenu = wx.MenuBar()
self.PrefillDataMenu(self.MCSAMenu,helpType='MC/SA')
self.MCSAMenu.Append(menu=wx.Menu(title=''),title='Select tab')
self.MCSAEdit = wx.Menu(title='')
self.MCSAMenu.Append(menu=self.MCSAEdit, title='MC/SA')
self.MCSAEdit.Append(id=wxID_ADDMCSAATOM, kind=wx.ITEM_NORMAL,text='Add atom',
help='Add single atom to MC/SA model')
self.MCSAEdit.Append(id=wxID_ADDMCSARB, kind=wx.ITEM_NORMAL,text='Add rigid body',
help='Add rigid body to MC/SA model' )
self.MCSAEdit.Append(id=wxID_CLEARMCSARB, kind=wx.ITEM_NORMAL,text='Clear rigid bodies',
help='Clear all atoms & rigid bodies from MC/SA model' )
self.MCSAEdit.Append(id=wxID_MOVEMCSA, kind=wx.ITEM_NORMAL,text='Move MC/SA solution',
help='Move MC/SA solution to atom list' )
self.MCSAEdit.Append(id=wxID_MCSACLEARRESULTS, kind=wx.ITEM_NORMAL,text='Clear results',
help='Clear table of MC/SA results' )
self.PostfillDataMenu()
# Phase / Texture tab
self.TextureMenu = wx.MenuBar()
self.PrefillDataMenu(self.TextureMenu,helpType='Texture')
self.TextureMenu.Append(menu=wx.Menu(title=''),title='Select tab')
self.TextureEdit = wx.Menu(title='')
self.TextureMenu.Append(menu=self.TextureEdit, title='Texture')
# self.TextureEdit.Append(id=wxID_REFINETEXTURE, kind=wx.ITEM_NORMAL,text='Refine texture',
# help='Refine the texture coefficients from sequential Pawley results')
self.TextureEdit.Append(id=wxID_CLEARTEXTURE, kind=wx.ITEM_NORMAL,text='Clear texture',
help='Clear the texture coefficients' )
self.PostfillDataMenu()
# Phase / Pawley tab
self.PawleyMenu = wx.MenuBar()
self.PrefillDataMenu(self.PawleyMenu,helpType='Pawley')
self.PawleyMenu.Append(menu=wx.Menu(title=''),title='Select tab')
self.PawleyEdit = wx.Menu(title='')
self.PawleyMenu.Append(menu=self.PawleyEdit,title='Operations')
self.PawleyEdit.Append(id=wxID_PAWLEYLOAD, kind=wx.ITEM_NORMAL,text='Pawley create',
help='Initialize Pawley reflection list')
self.PawleyEdit.Append(id=wxID_PAWLEYESTIMATE, kind=wx.ITEM_NORMAL,text='Pawley estimate',
help='Estimate initial Pawley intensities')
self.PawleyEdit.Append(id=wxID_PAWLEYUPDATE, kind=wx.ITEM_NORMAL,text='Pawley update',
help='Update negative Pawley intensities with -0.5*Fobs and turn off refinemnt')
self.PostfillDataMenu()
# Phase / Map peaks tab
self.MapPeaksMenu = wx.MenuBar()
self.PrefillDataMenu(self.MapPeaksMenu,helpType='Map peaks')
self.MapPeaksMenu.Append(menu=wx.Menu(title=''),title='Select tab')
self.MapPeaksEdit = wx.Menu(title='')
self.MapPeaksMenu.Append(menu=self.MapPeaksEdit, title='Map peaks')
self.MapPeaksEdit.Append(id=wxID_PEAKSMOVE, kind=wx.ITEM_NORMAL,text='Move peaks',
help='Move selected peaks to atom list')
self.MapPeaksEdit.Append(id=wxID_PEAKSVIEWPT, kind=wx.ITEM_NORMAL,text='View point',
help='View point is 1st peak selected')
self.MapPeaksEdit.Append(id=wxID_PEAKSDISTVP, kind=wx.ITEM_NORMAL,text='View pt. dist.',
help='Compute distance of selected peaks from view point')
self.MapPeaksEdit.Append(id=wxID_SHOWBONDS, kind=wx.ITEM_NORMAL,text='Hide bonds',
help='Hide or show bonds between peak positions')
self.MapPeaksEdit.Append(id=wxID_PEAKSDA, kind=wx.ITEM_NORMAL,text='Calc dist/ang',
help='Calculate distance or angle for selection')
self.MapPeaksEdit.Append(id=wxID_FINDEQVPEAKS, kind=wx.ITEM_NORMAL,text='Equivalent peaks',
help='Find equivalent peaks')
self.MapPeaksEdit.Append(id=wxID_PEAKSUNIQUE, kind=wx.ITEM_NORMAL,text='Unique peaks',
help='Select unique set')
self.MapPeaksEdit.Append(id=wxID_PEAKSDELETE, kind=wx.ITEM_NORMAL,text='Delete peaks',
help='Delete selected peaks')
self.MapPeaksEdit.Append(id=wxID_PEAKSCLEAR, kind=wx.ITEM_NORMAL,text='Clear peaks',
help='Clear the map peak list')
self.PostfillDataMenu()
# Phase / Rigid bodies tab
self.RigidBodiesMenu = wx.MenuBar()
self.PrefillDataMenu(self.RigidBodiesMenu,helpType='Rigid bodies')
self.RigidBodiesMenu.Append(menu=wx.Menu(title=''),title='Select tab')
self.RigidBodiesEdit = wx.Menu(title='')
self.RigidBodiesMenu.Append(menu=self.RigidBodiesEdit, title='Edit')
self.RigidBodiesEdit.Append(id=wxID_ASSIGNATMS2RB, kind=wx.ITEM_NORMAL,text='Assign atoms to rigid body',
help='Select & position rigid body in structure of existing atoms')
self.RigidBodiesEdit.Append(id=wxID_AUTOFINDRESRB, kind=wx.ITEM_NORMAL,text='Auto find residues',
help='Auto find of residue RBs in macromolecule')
self.RigidBodiesEdit.Append(id=wxID_COPYRBPARMS, kind=wx.ITEM_NORMAL,text='Copy rigid body parms',
help='Copy rigid body location & TLS parameters')
self.RigidBodiesEdit.Append(id=wxID_GLOBALTHERM, kind=wx.ITEM_NORMAL,text='Global thermal motion',
help='Global setting of residue thermal motion models')
self.RigidBodiesEdit.Append(id=wxID_GLOBALRESREFINE, kind=wx.ITEM_NORMAL,text='Global residue refine',
help='Global setting of residue RB refinement flags')
self.RigidBodiesEdit.Append(id=wxID_RBREMOVEALL, kind=wx.ITEM_NORMAL,text='Remove all rigid bodies',
help='Remove all rigid body assignment for atoms')
self.PostfillDataMenu()
# end of GSAS-II menu definitions
def _init_ctrls(self, parent,name=None,size=None,pos=None):
wx.Frame.__init__(
self,parent=parent,
#style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX | wx.FRAME_FLOAT_ON_PARENT ,
style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX,
size=size,pos=pos,title='GSAS-II data display')
self._init_menus()
if name:
self.SetLabel(name)
self.Show()
def __init__(self,parent,frame,data=None,name=None, size=None,pos=None):
self.G2frame = frame
self._init_ctrls(parent,name,size,pos)
self.data = data
clientSize = wx.ClientDisplayRect()
Size = self.GetSize()
xPos = clientSize[2]-Size[0]
self.SetPosition(wx.Point(xPos,clientSize[1]+250))
self.AtomGrid = []
self.selectedRow = 0
def setSizePosLeft(self,Width):
clientSize = wx.ClientDisplayRect()
Width[1] = min(Width[1],clientSize[2]-300)
Width[0] = max(Width[0],300)
self.SetSize(Width)
# self.SetPosition(wx.Point(clientSize[2]-Width[0],clientSize[1]+250))
def Clear(self):
self.ClearBackground()
self.DestroyChildren()
################################################################################
##### GSNotebook
################################################################################
[docs]class GSNoteBook(wx.aui.AuiNotebook):
'''Notebook used in various locations; implemented with wx.aui extension
'''
def __init__(self, parent, name='',size = None):
wx.aui.AuiNotebook.__init__(self, parent, -1,
style=wx.aui.AUI_NB_TOP |
wx.aui.AUI_NB_SCROLL_BUTTONS)
if size: self.SetSize(size)
self.parent = parent
self.PageChangeHandler = None
def PageChangeEvent(self,event):
G2frame = self.parent.G2frame
page = event.GetSelection()
if self.PageChangeHandler:
if log.LogInfo['Logging']:
log.MakeTabLog(
G2frame.dataFrame.GetTitle(),
G2frame.dataDisplay.GetPageText(page)
)
self.PageChangeHandler(event)
[docs] def Bind(self,eventtype,handler,*args,**kwargs):
'''Override the Bind() function so that page change events can be trapped
'''
if eventtype == wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED:
self.PageChangeHandler = handler
wx.aui.AuiNotebook.Bind(self,eventtype,self.PageChangeEvent)
return
wx.aui.AuiNotebook.Bind(self,eventtype,handler,*args,**kwargs)
def Clear(self):
GSNoteBook.DeleteAllPages(self)
def FindPage(self,name):
numPage = self.GetPageCount()
for page in range(numPage):
if self.GetPageText(page) == name:
return page
def ChangeSelection(self,page):
# in wx.Notebook ChangeSelection is like SetSelection, but it
# does not invoke the event related to pressing the tab button
# I don't see a way to do that in aui.
oldPage = self.GetSelection()
self.SetSelection(page)
return oldPage
# def __getattribute__(self,name):
# '''This method provides a way to print out a message every time
# that a method in a class is called -- to see what all the calls
# might be, or where they might be coming from.
# Cute trick for debugging!
# '''
# attr = object.__getattribute__(self, name)
# if hasattr(attr, '__call__'):
# def newfunc(*args, **kwargs):
# print('GSauiNoteBook calling %s' %attr.__name__)
# result = attr(*args, **kwargs)
# return result
# return newfunc
# else:
# return attr
################################################################################
##### GSGrid
################################################################################
[docs]class GSGrid(wg.Grid):
'''Basic wx.Grid implementation
'''
def __init__(self, parent, name=''):
wg.Grid.__init__(self,parent,-1,name=name)
#self.SetSize(parent.GetClientSize())
# above removed to speed drawing of initial grid
# does not appear to be needed
def Clear(self):
wg.Grid.ClearGrid(self)
def SetCellReadOnly(self,r,c,readonly=True):
self.SetReadOnly(r,c,isReadOnly=readonly)
def SetCellStyle(self,r,c,color="white",readonly=True):
self.SetCellBackgroundColour(r,c,color)
self.SetReadOnly(r,c,isReadOnly=readonly)
def GetSelection(self):
#this is to satisfy structure drawing stuff in G2plt when focus changes
return None
[docs]class Table(wg.PyGridTableBase):
'''Basic data table for use with GSgrid
'''
def __init__(self, data=[], rowLabels=None, colLabels=None, types = None):
wg.PyGridTableBase.__init__(self)
self.colLabels = colLabels
self.rowLabels = rowLabels
self.dataTypes = types
self.data = data
def AppendRows(self, numRows=1):
self.data.append([])
return True
def CanGetValueAs(self, row, col, typeName):
if self.dataTypes:
colType = self.dataTypes[col].split(':')[0]
if typeName == colType:
return True
else:
return False
else:
return False
def CanSetValueAs(self, row, col, typeName):
return self.CanGetValueAs(row, col, typeName)
def DeleteRow(self,pos):
data = self.GetData()
self.SetData([])
new = []
for irow,row in enumerate(data):
if irow <> pos:
new.append(row)
self.SetData(new)
def GetColLabelValue(self, col):
if self.colLabels:
return self.colLabels[col]
def GetData(self):
data = []
for row in range(self.GetNumberRows()):
data.append(self.GetRowValues(row))
return data
def GetNumberCols(self):
try:
return len(self.colLabels)
except TypeError:
return None
def GetNumberRows(self):
return len(self.data)
def GetRowLabelValue(self, row):
if self.rowLabels:
return self.rowLabels[row]
def GetColValues(self, col):
data = []
for row in range(self.GetNumberRows()):
data.append(self.GetValue(row, col))
return data
def GetRowValues(self, row):
data = []
for col in range(self.GetNumberCols()):
data.append(self.GetValue(row, col))
return data
def GetTypeName(self, row, col):
try:
return self.dataTypes[col]
except TypeError:
return None
def GetValue(self, row, col):
try:
return self.data[row][col]
except IndexError:
return None
def InsertRows(self, pos, rows):
for row in range(rows):
self.data.insert(pos,[])
pos += 1
def IsEmptyCell(self,row,col):
try:
return not self.data[row][col]
except IndexError:
return True
def OnKeyPress(self, event):
dellist = self.GetSelectedRows()
if event.GetKeyCode() == wx.WXK_DELETE and dellist:
grid = self.GetView()
for i in dellist: grid.DeleteRow(i)
def SetColLabelValue(self, col, label):
numcols = self.GetNumberCols()
if col > numcols-1:
self.colLabels.append(label)
else:
self.colLabels[col]=label
def SetData(self,data):
for row in range(len(data)):
self.SetRowValues(row,data[row])
def SetRowLabelValue(self, row, label):
self.rowLabels[row]=label
def SetRowValues(self,row,data):
self.data[row] = data
def SetValue(self, row, col, value):
def innerSetValue(row, col, value):
try:
self.data[row][col] = value
except TypeError:
return
except IndexError:
print row,col,value
# add a new row
if row > self.GetNumberRows():
self.data.append([''] * self.GetNumberCols())
elif col > self.GetNumberCols():
for row in range(self.GetNumberRows):
self.data[row].append('')
print self.data
self.data[row][col] = value
innerSetValue(row, col, value)
################################################################################
#### Help
################################################################################
[docs]def ShowHelp(helpType,frame):
'''Called to bring up a web page for documentation.'''
global htmlFirstUse
# look up a definition for help info from dict
helplink = helpLocDict.get(helpType)
if helplink is None:
# no defined link to use, create a default based on key
helplink = 'gsasII.html#'+helpType.replace(' ','_')
helplink = os.path.join(path2GSAS2,'help',helplink)
if helpMode == 'internal':
try:
htmlPanel.LoadFile(helplink)
htmlFrame.Raise()
except:
htmlFrame = wx.Frame(frame, -1, size=(610, 510))
htmlFrame.Show(True)
htmlFrame.SetTitle("HTML Window") # N.B. reset later in LoadFile
htmlPanel = MyHtmlPanel(htmlFrame,-1)
htmlPanel.LoadFile(helplink)
else:
pfx = "file://"
if sys.platform.lower().startswith('win'):
pfx = ''
if htmlFirstUse:
webbrowser.open_new(pfx+helplink)
htmlFirstUse = False
else:
webbrowser.open(pfx+helplink, new=0, autoraise=True)
################################################################################
##### Notebook
################################################################################
[docs]def UpdateNotebook(G2frame,data):
'''Called when the data tree notebook entry is selected. Allows for
editing of the text in that tree entry
'''
def OnNoteBook(event):
data = G2frame.dataDisplay.GetValue().split('\n')
G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,G2frame.root,'Notebook'),data)
if 'nt' not in os.name:
G2frame.dataDisplay.AppendText('\n')
if G2frame.dataDisplay:
G2frame.dataDisplay.Destroy()
G2frame.dataFrame.SetLabel('Notebook')
G2frame.dataDisplay = wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER | wx.TE_DONTWRAP)
G2frame.dataDisplay.Bind(wx.EVT_TEXT_ENTER,OnNoteBook)
G2frame.dataDisplay.Bind(wx.EVT_KILL_FOCUS,OnNoteBook)
for line in data:
G2frame.dataDisplay.AppendText(line+"\n")
G2frame.dataDisplay.AppendText('Notebook entry @ '+time.ctime()+"\n")
G2frame.dataFrame.setSizePosLeft([400,250])
################################################################################
##### Controls
################################################################################
[docs]def UpdateControls(G2frame,data):
'''Edit overall GSAS-II controls in main Controls data tree entry
'''
#patch
if 'deriv type' not in data:
data = {}
data['deriv type'] = 'analytic Hessian'
data['min dM/M'] = 0.0001
data['shift factor'] = 1.
data['max cyc'] = 3
data['F**2'] = True
data['minF/sig'] = 0
if 'shift factor' not in data:
data['shift factor'] = 1.
if 'max cyc' not in data:
data['max cyc'] = 3
if 'F**2' not in data:
data['F**2'] = True
data['minF/sig'] = 0
if 'Author' not in data:
data['Author'] = 'no name'
if 'FreePrm1' not in data:
data['FreePrm1'] = 'Sample humidity (%)'
if 'FreePrm2' not in data:
data['FreePrm2'] = 'Sample voltage (V)'
if 'FreePrm3' not in data:
data['FreePrm3'] = 'Applied load (MN)'
if 'Copy2Next' not in data:
data['Copy2Next'] = False
if 'Reverse Seq' not in data:
data['Reverse Seq'] = False
#end patch
def SeqSizer():
def OnSelectData(event):
choices = GetPatternTreeDataNames(G2frame,['PWDR',])
sel = []
if 'Seq Data' in data:
for item in data['Seq Data']:
sel.append(choices.index(item))
sel = [choices.index(item) for item in data['Seq Data']]
dlg = G2MultiChoiceDialog(G2frame.dataFrame, 'Sequential refinement',
'Select dataset to include',
choices)
dlg.SetSelections(sel)
names = []
if dlg.ShowModal() == wx.ID_OK:
for sel in dlg.GetSelections():
names.append(choices[sel])
data['Seq Data'] = names
G2frame.EnableSeqRefineMenu()
dlg.Destroy()
wx.CallAfter(UpdateControls,G2frame,data)
def OnReverse(event):
data['Reverse Seq'] = reverseSel.GetValue()
def OnCopySel(event):
data['Copy2Next'] = copySel.GetValue()
seqSizer = wx.BoxSizer(wx.VERTICAL)
dataSizer = wx.BoxSizer(wx.HORIZONTAL)
dataSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Sequential Refinement: '),0,WACV)
selSeqData = wx.Button(G2frame.dataDisplay,-1,label=' Select data')
selSeqData.Bind(wx.EVT_BUTTON,OnSelectData)
dataSizer.Add(selSeqData,0,WACV)
SeqData = data.get('Seq Data',[])
if not SeqData:
lbl = ' (no powder data selected)'
else:
lbl = ' ('+str(len(SeqData))+' dataset(s) selected)'
dataSizer.Add(wx.StaticText(G2frame.dataDisplay,label=lbl),0,WACV)
seqSizer.Add(dataSizer,0)
if SeqData:
selSizer = wx.BoxSizer(wx.HORIZONTAL)
reverseSel = wx.CheckBox(G2frame.dataDisplay,-1,label=' Reverse order?')
reverseSel.Bind(wx.EVT_CHECKBOX,OnReverse)
reverseSel.SetValue(data['Reverse Seq'])
selSizer.Add(reverseSel,0,WACV)
copySel = wx.CheckBox(G2frame.dataDisplay,-1,label=' Copy results to next histogram?')
copySel.Bind(wx.EVT_CHECKBOX,OnCopySel)
copySel.SetValue(data['Copy2Next'])
selSizer.Add(copySel,0,WACV)
seqSizer.Add(selSizer,0)
return seqSizer
def LSSizer():
def OnDerivType(event):
data['deriv type'] = derivSel.GetValue()
derivSel.SetValue(data['deriv type'])
wx.CallAfter(UpdateControls,G2frame,data)
def OnConvergence(event):
try:
value = max(1.e-9,min(1.0,float(Cnvrg.GetValue())))
except ValueError:
value = 0.0001
data['min dM/M'] = value
Cnvrg.SetValue('%.2g'%(value))
def OnMaxCycles(event):
data['max cyc'] = int(maxCyc.GetValue())
maxCyc.SetValue(str(data['max cyc']))
def OnFactor(event):
try:
value = min(max(float(Factr.GetValue()),0.00001),100.)
except ValueError:
value = 1.0
data['shift factor'] = value
Factr.SetValue('%.5f'%(value))
def OnFsqRef(event):
data['F**2'] = fsqRef.GetValue()
def OnMinSig(event):
try:
value = min(max(float(minSig.GetValue()),0.),5.)
except ValueError:
value = 1.0
data['minF/sig'] = value
minSig.SetValue('%.2f'%(value))
LSSizer = wx.FlexGridSizer(cols=4,vgap=5,hgap=5)
LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Refinement derivatives: '),0,WACV)
Choice=['analytic Jacobian','numeric','analytic Hessian']
derivSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['deriv type'],choices=Choice,
style=wx.CB_READONLY|wx.CB_DROPDOWN)
derivSel.SetValue(data['deriv type'])
derivSel.Bind(wx.EVT_COMBOBOX, OnDerivType)
LSSizer.Add(derivSel,0,WACV)
LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Min delta-M/M: '),0,WACV)
Cnvrg = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2g'%(data['min dM/M']),style=wx.TE_PROCESS_ENTER)
Cnvrg.Bind(wx.EVT_TEXT_ENTER,OnConvergence)
Cnvrg.Bind(wx.EVT_KILL_FOCUS,OnConvergence)
LSSizer.Add(Cnvrg,0,WACV)
if 'Hessian' in data['deriv type']:
LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Max cycles: '),0,WACV)
Choice = ['0','1','2','3','5','10','15','20']
maxCyc = wx.ComboBox(parent=G2frame.dataDisplay,value=str(data['max cyc']),choices=Choice,
style=wx.CB_READONLY|wx.CB_DROPDOWN)
maxCyc.SetValue(str(data['max cyc']))
maxCyc.Bind(wx.EVT_COMBOBOX, OnMaxCycles)
LSSizer.Add(maxCyc,0,WACV)
else:
LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Initial shift factor: '),0,WACV)
Factr = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.5f'%(data['shift factor']),style=wx.TE_PROCESS_ENTER)
Factr.Bind(wx.EVT_TEXT_ENTER,OnFactor)
Factr.Bind(wx.EVT_KILL_FOCUS,OnFactor)
LSSizer.Add(Factr,0,WACV)
if G2frame.Sngl:
LSSizer.Add((1,0),)
LSSizer.Add((1,0),)
fsqRef = wx.CheckBox(G2frame.dataDisplay,-1,label='Refine HKLF as F^2? ')
fsqRef.SetValue(data['F**2'])
fsqRef.Bind(wx.EVT_CHECKBOX,OnFsqRef)
LSSizer.Add(fsqRef,0,WACV)
LSSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label='Min obs/sig (0-5): '),0,WACV)
minSig = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2f'%(data['minF/sig']),style=wx.TE_PROCESS_ENTER)
minSig.Bind(wx.EVT_TEXT_ENTER,OnMinSig)
minSig.Bind(wx.EVT_KILL_FOCUS,OnMinSig)
LSSizer.Add(minSig,0,WACV)
return LSSizer
def AuthSizer():
def OnAuthor(event):
data['Author'] = auth.GetValue()
Author = data['Author']
authSizer = wx.BoxSizer(wx.HORIZONTAL)
authSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' CIF Author (last, first):'),0,WACV)
auth = wx.TextCtrl(G2frame.dataDisplay,-1,value=Author,style=wx.TE_PROCESS_ENTER)
auth.Bind(wx.EVT_TEXT_ENTER,OnAuthor)
auth.Bind(wx.EVT_KILL_FOCUS,OnAuthor)
authSizer.Add(auth,0,WACV)
return authSizer
if G2frame.dataDisplay:
G2frame.dataDisplay.Destroy()
if not G2frame.dataFrame.GetStatusBar():
Status = G2frame.dataFrame.CreateStatusBar()
Status.SetStatusText('')
G2frame.dataFrame.SetLabel('Controls')
G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
SetDataMenuBar(G2frame,G2frame.dataFrame.ControlsMenu)
mainSizer = wx.BoxSizer(wx.VERTICAL)
mainSizer.Add((5,5),0)
mainSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Refinement Controls:'),0,WACV)
mainSizer.Add(LSSizer())
mainSizer.Add((5,5),0)
mainSizer.Add(SeqSizer())
mainSizer.Add((5,5),0)
mainSizer.Add(AuthSizer())
mainSizer.Add((5,5),0)
mainSizer.Layout()
G2frame.dataDisplay.SetSizer(mainSizer)
G2frame.dataDisplay.SetSize(mainSizer.Fit(G2frame.dataFrame))
G2frame.dataFrame.setSizePosLeft(mainSizer.Fit(G2frame.dataFrame))
################################################################################
##### Comments
################################################################################
def UpdateComments(G2frame,data):
if G2frame.dataDisplay:
G2frame.dataDisplay.Destroy()
G2frame.dataFrame.SetLabel('Comments')
G2frame.dataDisplay = wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
style=wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_DONTWRAP)
for line in data:
G2frame.dataDisplay.AppendText(line+'\n')
G2frame.dataFrame.setSizePosLeft([400,250])
################################################################################
##### Display of Sequential Results
################################################################################
[docs]def UpdateSeqResults(G2frame,data,prevSize=None):
"""
Called when the Sequential Results data tree entry is selected
to show results from a sequential refinement.
:param wx.Frame G2frame: main GSAS-II data tree windows
:param dict data: a dictionary containing the following items:
* 'histNames' - list of histogram names in order as processed by Sequential Refinement
* 'varyList' - list of variables - identical over all refinements in sequence
note that this is the original list of variables, prior to processing
constraints.
* 'variableLabels' -- a dict of labels to be applied to each parameter
(this is created as an empty dict if not present in data).
* keyed by histName - dictionaries for all data sets processed, which contains:
* 'variables'- result[0] from leastsq call
* 'varyList' - list of variables passed to leastsq call (not same as above)
* 'sig' - esds for variables
* 'covMatrix' - covariance matrix from individual refinement
* 'title' - histogram name; same as dict item name
* 'newAtomDict' - new atom parameters after shifts applied
* 'newCellDict' - refined cell parameters after shifts to A0-A5 from Dij terms applied'
"""
def GetSampleParms():
'''Make a dictionary of the sample parameters are not the same over the
refinement series.
'''
if 'IMG' in histNames[0]:
sampleParmDict = {'Sample load':[],}
else:
sampleParmDict = {'Temperature':[],'Pressure':[],
'FreePrm1':[],'FreePrm2':[],'FreePrm3':[],}
Controls = G2frame.PatternTree.GetItemPyData(
GetPatternTreeItemId(G2frame,G2frame.root, 'Controls'))
sampleParm = {}
for name in histNames:
if 'IMG' in name:
for item in sampleParmDict:
sampleParmDict[item].append(data[name]['parmDict'][item])
else:
Id = GetPatternTreeItemId(G2frame,G2frame.root,name)
sampleData = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,Id,'Sample Parameters'))
for item in sampleParmDict:
sampleParmDict[item].append(sampleData[item])
for item in sampleParmDict:
frstValue = sampleParmDict[item][0]
if np.any(np.array(sampleParmDict[item])-frstValue):
if item.startswith('FreePrm'):
sampleParm[Controls[item]] = sampleParmDict[item]
else:
sampleParm[item] = sampleParmDict[item]
return sampleParm
def GetColumnInfo(col):
'''returns column label, lists of values and errors (or None) for each column in the table
for plotting. The column label is reformatted from Unicode to MatPlotLib encoding
'''
colName = G2frame.SeqTable.GetColLabelValue(col)
plotName = variableLabels.get(colName,colName)
plotName = plotSpCharFix(plotName)
return plotName,colList[col],colSigs[col]
def PlotSelect(event):
'Plots a row (covariance) or column on double-click'
cols = G2frame.dataDisplay.GetSelectedCols()
rows = G2frame.dataDisplay.GetSelectedRows()
if cols:
G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
elif rows:
name = histNames[rows[0]] #only does 1st one selected
G2plt.PlotCovariance(G2frame,data[name])
else:
G2frame.ErrorDialog(
'Select row or columns',
'Nothing selected in table. Click on column or row label(s) to plot. N.B. Grid selection can be a bit funky.'
)
def OnPlotSelSeq(event):
'plot the selected columns or row from menu command'
cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
rows = G2frame.dataDisplay.GetSelectedRows()
if cols:
G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
elif rows:
name = histNames[rows[0]] #only does 1st one selected
G2plt.PlotCovariance(G2frame,data[name])
else:
G2frame.ErrorDialog(
'Select columns',
'No columns or rows selected in table. Click on row or column labels to select fields for plotting.'
)
def OnRenameSelSeq(event):
cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
colNames = [G2frame.SeqTable.GetColLabelValue(c) for c in cols]
newNames = colNames[:]
for i,name in enumerate(colNames):
if name in variableLabels:
newNames[i] = variableLabels[name]
if not cols:
G2frame.ErrorDialog('Select columns',
'No columns selected in table. Click on column labels to select fields for rename.')
return
dlg = MultiStringDialog(G2frame.dataDisplay,'Set column names',colNames,newNames)
if dlg.Show():
newNames = dlg.GetValues()
variableLabels.update(dict(zip(colNames,newNames)))
data['variableLabels'] = variableLabels
dlg.Destroy()
UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
def OnSaveSelSeqCSV(event):
'export the selected columns to a .csv file from menu command'
OnSaveSelSeq(event,csv=True)
def OnSaveSelSeq(event,csv=False):
'export the selected columns to a .txt file from menu command'
def WriteCSV():
def WriteList(headerItems):
line = ''
for lbl in headerItems:
if line: line += ','
line += '"'+lbl+'"'
return line
head = ['name']
for col in cols:
item = G2frame.SeqTable.GetColLabelValue(col)
if col in havesig:
head += [item,'esd-'+item]
else:
head += [item]
SeqFile.write(WriteList(head)+'\n')
for row,name in enumerate(saveNames):
line = '"'+saveNames[row]+'"'
for col in cols:
if col in havesig:
line += ','+str(saveData[col][row])+','+str(saveSigs[col][row])
else:
line += ','+str(saveData[col][row])
SeqFile.write(line+'\n')
def WriteSeq():
lenName = len(saveNames[0])
line = ' %s '%('name'.center(lenName))
for col in cols:
item = G2frame.SeqTable.GetColLabelValue(col)
if col in havesig:
line += ' %12s %12s '%(item.center(12),'esd'.center(12))
else:
line += ' %12s '%(item.center(12))
SeqFile.write(line+'\n')
for row,name in enumerate(saveNames):
line = " '%s' "%(saveNames[row])
for col in cols:
if col in havesig:
line += ' %12.6f %12.6f '%(saveData[col][row],saveSigs[col][row])
else:
line += ' %12.6f '%saveData[col][row]
SeqFile.write(line+'\n')
cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
nrows = G2frame.SeqTable.GetNumberRows()
if not cols:
G2frame.ErrorDialog('Select columns',
'No columns selected in table. Click on column labels to select fields for output.')
return
saveNames = [G2frame.SeqTable.GetRowLabelValue(r) for r in range(nrows)]
saveData = {}
saveSigs = {}
havesig = []
for col in cols:
name,vals,sigs = GetColumnInfo(col)
saveData[col] = vals
if sigs:
havesig.append(col)
saveSigs[col] = sigs
if csv:
wild = 'CSV output file (*.csv)|*.csv'
else:
wild = 'Text output file (*.txt)|*.txt'
dlg = wx.FileDialog(
G2frame,
'Choose text output file for your selection', '.', '',
wild,wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
try:
if dlg.ShowModal() == wx.ID_OK:
SeqTextFile = dlg.GetPath()
SeqTextFile = G2IO.FileDlgFixExt(dlg,SeqTextFile)
SeqFile = open(SeqTextFile,'w')
if csv:
WriteCSV()
else:
WriteSeq()
SeqFile.close()
finally:
dlg.Destroy()
def striphist(var,insChar=''):
'strip a histogram number from a var name'
sv = var.split(':')
if len(sv) <= 1: return var
sv[1] = insChar
return ':'.join(sv)
def plotSpCharFix(lbl):
'Change selected unicode characters to their matplotlib equivalent'
for u,p in [
(u'\u03B1',r'$\alpha$'),
(u'\u03B2',r'$\beta$'),
(u'\u03B3',r'$\gamma$'),
(u'\u0394\u03C7',r'$\Delta\chi$'),
]:
lbl = lbl.replace(u,p)
return lbl
def SelectXaxis():
'returns a selected column number (or None) as the X-axis selection'
ncols = G2frame.SeqTable.GetNumberCols()
colNames = [G2frame.SeqTable.GetColLabelValue(r) for r in range(ncols)]
dlg = G2SingleChoiceDialog(
G2frame.dataDisplay,
'Select x-axis parameter for plot or Cancel for sequence number',
'Select X-axis',
colNames)
try:
if dlg.ShowModal() == wx.ID_OK:
col = dlg.GetSelection()
else:
col = None
finally:
dlg.Destroy()
return col
def EnablePseudoVarMenus():
'Enables or disables the PseudoVar menu items that require existing defs'
if Controls['SeqPseudoVars']:
val = True
else:
val = False
G2frame.dataFrame.SequentialPvars.Enable(wxDELSEQVAR,val)
G2frame.dataFrame.SequentialPvars.Enable(wxEDITSEQVAR,val)
def DelPseudoVar(event):
'Ask the user to select a pseudo var expression to delete'
choices = Controls['SeqPseudoVars'].keys()
selected = ItemSelector(
choices,G2frame.dataFrame,
multiple=True,
title='Select expressions to remove',
header='Delete expression')
if selected is None: return
for item in selected:
del Controls['SeqPseudoVars'][choices[item]]
if selected:
UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
def EditPseudoVar(event):
'Edit an existing pseudo var expression'
choices = Controls['SeqPseudoVars'].keys()
if len(choices) == 1:
selected = 0
else:
selected = ItemSelector(
choices,G2frame.dataFrame,
multiple=False,
title='Select an expression to edit',
header='Edit expression')
if selected is not None:
dlg = G2exG.ExpressionDialog(
G2frame.dataDisplay,PSvarDict,
Controls['SeqPseudoVars'][choices[selected]],
header="Edit the PseudoVar expression",
VarLabel="PseudoVar #"+str(selected+1),
fit=False)
newobj = dlg.Show(True)
if newobj:
calcobj = G2obj.ExpressionCalcObj(newobj)
del Controls['SeqPseudoVars'][choices[selected]]
Controls['SeqPseudoVars'][calcobj.eObj.expression] = newobj
UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
def AddNewPseudoVar(event):
'Create a new pseudo var expression'
dlg = G2exG.ExpressionDialog(
G2frame.dataDisplay,PSvarDict,
header='Enter an expression for a PseudoVar here',
VarLabel = "New PseudoVar",
fit=False)
obj = dlg.Show(True)
dlg.Destroy()
if obj:
calcobj = G2obj.ExpressionCalcObj(obj)
Controls['SeqPseudoVars'][calcobj.eObj.expression] = obj
UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
# PATCH: this routine can go away eventually
def CreatePSvarDict(seqnum,name):
'''Create a parameter dict (parmDict) for everything that might be used
in a PseudoVar.
Also creates a list of revised labels (modVaryList) for the covariance matrix to
match the items in the parameter dict and a matching list of ESDs (ESDvaryList).
:param int seqnum: the sequence number of the histogram in the sequential
refinement
:param str name: the name of the histogram in the data tree
:returns: parmDict,modVaryList,ESDvaryList
'''
parmDict = {}
modVaryList = []
for i,(key,val) in enumerate(zip(data[name]['varyList'],data[name]['variables'])):
skey = striphist(key)
if skey in data[name].get('newAtomDict',{}):
# replace coordinate shifts with equivalents from lookup table
repkey,repval = data[name]['newAtomDict'][skey]
parmDict[repkey] = repval
modVaryList.append(repkey)
elif skey in data[name].get('newCellDict',{}):
# replace recip. cell term shifts with equivalents from lookup table
repkey,repval = data[name]['newCellDict'][skey]
parmDict[repkey] = repval
modVaryList.append(repkey)
else:
parmDict[key] = val
modVaryList.append(key)
# create a cell parm dict, override initial settings with values in parmDict
for phase in Phases:
phasedict = Phases[phase]
pId = phasedict['pId']
cell = Rcelldict.copy()
cell.update(
{lbl:parmDict[lbl] for lbl in RcellLbls[pId] if lbl in parmDict}
)
pfx = str(pId)+'::' # prefix for A values from phase
A,zeros = G2stIO.cellFill(pfx,SGdata[pId],cell,zeroDict[pId])
parmDict.update({pfx+cellUlbl[i]:val for i,val in
enumerate(G2lat.A2cell(A))
if i in uniqCellIndx[pId]
})
parmDict[pfx+"vol"] = G2lat.calc_V(A)
# now add misc terms to dict
parmDict['Rwp'] = data[name]['Rvals']['Rwp']
parmDict[u'\u0394\u03C7\u00B2 (%)'] = 100.*data[name]['Rvals'].get('DelChi2',-1)
for key in sampleParms:
parmDict[key] = sampleParms[key][seqnum]
return parmDict,modVaryList,data[name]['sig']
def UpdateParmDict(parmDict):
'''generate the atom positions and the direct & reciprocal cell values,
because they might be needed to evaluate the pseudovar
'''
Ddict = dict(zip(['D11','D22','D33','D12','D13','D23'],
['A'+str(i) for i in range(6)])
)
delList = []
phaselist = []
for item in parmDict:
if ':' not in item: continue
key = item.split(':')
if len(key) < 3: continue
# remove the dA[xyz] terms, they would only bring confusion
if key[2].startswith('dA'):
delList.append(item)
# compute and update the corrected reciprocal cell terms using the Dij values
elif key[2] in Ddict:
if key[0] not in phaselist: phaselist.append(key[0])
akey = key[0]+'::'+Ddict[key[2]]
parmDict[akey] -= parmDict[item]
delList.append(item)
for item in delList:
del parmDict[item]
for i in phaselist:
pId = int(i)
# apply cell symmetry
A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata[pId],parmDict,zeroDict[pId])
# convert to direct cell & add the unique terms to the dictionary
for i,val in enumerate(G2lat.A2cell(A)):
if i in uniqCellIndx[pId]:
lbl = str(pId)+'::'+cellUlbl[i]
parmDict[lbl] = val
lbl = str(pId)+'::'+'vol'
parmDict[lbl] = G2lat.calc_V(A)
return parmDict
def EvalPSvarDeriv(calcobj,parmDict,var,ESD):
'''Evaluate an expression derivative with respect to a
GSAS-II variable name.
Note this likely could be faster if the loop over calcobjs were done
inside after the Dict was created.
'''
step = ESD/10
Ddict = dict(zip(['D11','D22','D33','D12','D13','D23'],
['A'+str(i) for i in range(6)])
)
results = []
phaselist = []
for incr in step,-step:
VparmDict = parmDict.copy()
# as saved, the parmDict has updated 'A[xyz]' values, but 'dA[xyz]'
# values are not zeroed: fix that!
VparmDict.update({item:0.0 for item in parmDict if 'dA' in item})
VparmDict[var] += incr
G2mv.Dict2Map(VparmDict,[]) # apply constraints
# generate the atom positions and the direct & reciprocal cell values now, because they might
# needed to evaluate the pseudovar
for item in VparmDict:
if ':' not in item: continue
key = item.split(':')
if len(key) < 3: continue
# apply any new shifts to atom positions
if key[2].startswith('dA'):
VparmDict[''.join(item.split('d'))] += VparmDict[item]
VparmDict[item] = 0.0
# compute and update the corrected reciprocal cell terms using the Dij values
if key[2] in Ddict:
if key[0] not in phaselist: phaselist.append(key[0])
akey = key[0]+'::'+Ddict[key[2]]
VparmDict[akey] -= VparmDict[item]
for i in phaselist:
pId = int(i)
# apply cell symmetry
A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata[pId],VparmDict,zeroDict[pId])
# convert to direct cell & add the unique terms to the dictionary
for i,val in enumerate(G2lat.A2cell(A)):
if i in uniqCellIndx[pId]:
lbl = str(pId)+'::'+cellUlbl[i]
VparmDict[lbl] = val
lbl = str(pId)+'::'+'vol'
VparmDict[lbl] = G2lat.calc_V(A)
# dict should be fully updated, use it & calculate
calcobj.SetupCalc(VparmDict)
results.append(calcobj.EvalExpression())
return (results[0] - results[1]) / (2.*step)
def EnableParFitEqMenus():
'Enables or disables the Parametric Fit menu items that require existing defs'
if Controls['SeqParFitEqList']:
val = True
else:
val = False
G2frame.dataFrame.SequentialPfit.Enable(wxDELPARFIT,val)
G2frame.dataFrame.SequentialPfit.Enable(wxEDITPARFIT,val)
G2frame.dataFrame.SequentialPfit.Enable(wxDOPARFIT,val)
def ParEqEval(Values,calcObjList,varyList):
'''Evaluate the parametric expression(s)
:param list Values: a list of values for each variable parameter
:param list calcObjList: a list of :class:`GSASIIobj.ExpressionCalcObj`
expression objects to evaluate
:param list varyList: a list of variable names for each value in Values
'''
result = []
for calcobj in calcObjList:
calcobj.UpdateVars(varyList,Values)
result.append((calcobj.depVal-calcobj.EvalExpression())/calcobj.depSig)
return result
def DoParEqFit(event,eqObj=None):
'Parametric fit minimizer'
varyValueDict = {} # dict of variables and their initial values
calcObjList = [] # expression objects, ready to go for each data point
if eqObj is not None:
eqObjList = [eqObj,]
else:
eqObjList = Controls['SeqParFitEqList']
UseFlags = G2frame.SeqTable.GetColValues(0)
for obj in eqObjList:
expr = obj.expression
# assemble refined vars for this equation
varyValueDict.update({var:val for var,val in obj.GetVariedVarVal()})
# lookup dependent var position
depVar = obj.GetDepVar()
if depVar in colLabels:
indx = colLabels.index(depVar)
else:
raise Exception('Dependent variable '+depVar+' not found')
# assemble a list of the independent variables
indepVars = obj.GetIndependentVars()
# loop over each datapoint
for j,row in enumerate(zip(*colList)):
if not UseFlags[j]: continue
# assemble equations to fit
calcobj = G2obj.ExpressionCalcObj(obj)
# prepare a dict of needed independent vars for this expression
indepVarDict = {var:row[i] for i,var in enumerate(colLabels) if var in indepVars}
calcobj.SetupCalc(indepVarDict)
# values and sigs for current value of dependent var
calcobj.depVal = row[indx]
calcobj.depSig = colSigs[indx][j]
calcObjList.append(calcobj)
# varied parameters
varyList = varyValueDict.keys()
values = varyValues = [varyValueDict[key] for key in varyList]
if not varyList:
print 'no variables to refine!'
return
try:
result = so.leastsq(ParEqEval,varyValues,full_output=True, #ftol=Ftol,
args=(calcObjList,varyList)
)
values = result[0]
covar = result[1]
if covar is None:
raise Exception
esdDict = {}
for i,avar in enumerate(varyList):
esdDict[avar] = np.sqrt(covar[i,i])
except:
print('====> Fit failed')
return
print('==== Fit Results ====')
for obj in eqObjList:
obj.UpdateVariedVars(varyList,values)
ind = ' '
print(' '+obj.GetDepVar()+' = '+obj.expression)
for var in obj.assgnVars:
print(ind+var+' = '+obj.assgnVars[var])
for var in obj.freeVars:
avar = "::"+obj.freeVars[var][0]
val = obj.freeVars[var][1]
if obj.freeVars[var][2]:
print(ind+var+' = '+avar + " = " + G2mth.ValEsd(val,esdDict[avar]))
else:
print(ind+var+' = '+avar + " =" + G2mth.ValEsd(val,0))
# create a plot for each parametric variable
for fitnum,obj in enumerate(eqObjList):
calcobj = G2obj.ExpressionCalcObj(obj)
# lookup dependent var position
indx = colLabels.index(obj.GetDepVar())
# assemble a list of the independent variables
indepVars = obj.GetIndependentVars()
# loop over each datapoint
fitvals = []
for j,row in enumerate(zip(*colList)):
calcobj.SetupCalc(
{var:row[i] for i,var in enumerate(colLabels) if var in indepVars}
)
fitvals.append(calcobj.EvalExpression())
G2plt.PlotSelectedSequence(
G2frame,[indx],GetColumnInfo,SelectXaxis,
fitnum,fitvals)
def SingleParEqFit(eqObj):
DoParEqFit(None,eqObj)
def DelParFitEq(event):
'Ask the user to select function to delete'
txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
selected = ItemSelector(
txtlst,G2frame.dataFrame,
multiple=True,
title='Select a parametric equation(s) to remove',
header='Delete equation')
if selected is None: return
Controls['SeqParFitEqList'] = [obj for i,obj in enumerate(Controls['SeqParFitEqList']) if i not in selected]
EnableParFitEqMenus()
if Controls['SeqParFitEqList']: DoParEqFit(event)
def EditParFitEq(event):
'Edit an existing parametric equation'
txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
if len(txtlst) == 1:
selected = 0
else:
selected = ItemSelector(
txtlst,G2frame.dataFrame,
multiple=False,
title='Select a parametric equation to edit',
header='Edit equation')
if selected is not None:
dlg = G2exG.ExpressionDialog(
G2frame.dataDisplay,indepVarDict,
Controls['SeqParFitEqList'][selected],
depVarDict=depVarDict,
header="Edit the formula for this minimization function",
ExtraButton=['Fit',SingleParEqFit])
newobj = dlg.Show(True)
if newobj:
calcobj = G2obj.ExpressionCalcObj(newobj)
Controls['SeqParFitEqList'][selected] = newobj
EnableParFitEqMenus()
if Controls['SeqParFitEqList']: DoParEqFit(event)
def AddNewParFitEq(event):
'Create a new parametric equation to be fit to sequential results'
# compile the variable names used in previous freevars to avoid accidental name collisions
usedvarlist = []
for obj in Controls['SeqParFitEqList']:
for var in obj.freeVars:
if obj.freeVars[var][0] not in usedvarlist: usedvarlist.append(obj.freeVars[var][0])
dlg = G2exG.ExpressionDialog(
G2frame.dataDisplay,indepVarDict,
depVarDict=depVarDict,
header='Define an equation to minimize in the parametric fit',
ExtraButton=['Fit',SingleParEqFit],
usedVars=usedvarlist)
obj = dlg.Show(True)
dlg.Destroy()
if obj:
Controls['SeqParFitEqList'].append(obj)
EnableParFitEqMenus()
if Controls['SeqParFitEqList']: DoParEqFit(event)
def CopyParFitEq(event):
'Copy an existing parametric equation to be fit to sequential results'
# compile the variable names used in previous freevars to avoid accidental name collisions
usedvarlist = []
for obj in Controls['SeqParFitEqList']:
for var in obj.freeVars:
if obj.freeVars[var][0] not in usedvarlist: usedvarlist.append(obj.freeVars[var][0])
txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
if len(txtlst) == 1:
selected = 0
else:
selected = ItemSelector(
txtlst,G2frame.dataFrame,
multiple=False,
title='Select a parametric equation to copy',
header='Copy equation')
if selected is not None:
newEqn = copy.deepcopy(Controls['SeqParFitEqList'][selected])
for var in newEqn.freeVars:
newEqn.freeVars[var][0] = G2obj.MakeUniqueLabel(newEqn.freeVars[var][0],usedvarlist)
dlg = G2exG.ExpressionDialog(
G2frame.dataDisplay,indepVarDict,
newEqn,
depVarDict=depVarDict,
header="Edit the formula for this minimization function",
ExtraButton=['Fit',SingleParEqFit])
newobj = dlg.Show(True)
if newobj:
calcobj = G2obj.ExpressionCalcObj(newobj)
Controls['SeqParFitEqList'].append(newobj)
EnableParFitEqMenus()
if Controls['SeqParFitEqList']: DoParEqFit(event)
def GridSetToolTip(row,col):
'''Routine to show standard uncertainties for each element in table
as a tooltip
'''
if colSigs[col]:
return u'\u03c3 = '+str(colSigs[col][row])
return ''
def GridColLblToolTip(col):
'''Define a tooltip for a column. This will be the user-entered value
(from data['variableLabels']) or the default name
'''
if col < 0 or col > len(colLabels):
print 'Illegal column #',col
return
var = colLabels[col]
return variableLabels.get(var,G2obj.fmtVarDescr(var))
def SetLabelString(event):
'''Define or edit the label for a column in the table, to be used
as a tooltip and for plotting
'''
col = event.GetCol()
if col < 0 or col > len(colLabels):
return
var = colLabels[col]
lbl = variableLabels.get(var,G2obj.fmtVarDescr(var))
dlg = SingleStringDialog(G2frame.dataFrame,'Set variable label',
'Set a name for variable '+var,lbl,size=(400,-1))
if dlg.Show():
variableLabels[var] = dlg.GetValue()
dlg.Destroy()
#def GridRowLblToolTip(row): return 'Row ='+str(row)
# lookup table for unique cell parameters by symmetry
cellGUIlist = [
[['m3','m3m'],(0,)],
[['3R','3mR'],(0,3)],
[['3','3m1','31m','6/m','6/mmm','4/m','4/mmm'],(0,2)],
[['mmm'],(0,1,2)],
[['2/m'+'a'],(0,1,2,3)],
[['2/m'+'b'],(0,1,2,4)],
[['2/m'+'c'],(0,1,2,5)],
[['-1'],(0,1,2,3,4,5)],
]
# cell labels
cellUlbl = ('a','b','c',u'\u03B1',u'\u03B2',u'\u03B3') # unicode a,b,c,alpha,beta,gamma
#======================================================================
# start processing sequential results here (UpdateSeqResults)
#======================================================================
if not data:
print 'No sequential refinement results'
return
variableLabels = data.get('variableLabels',{})
data['variableLabels'] = variableLabels
Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
Controls = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.root,'Controls'))
# create a place to store Pseudo Vars & Parametric Fit functions, if needed
if 'SeqPseudoVars' not in Controls: Controls['SeqPseudoVars'] = {}
if 'SeqParFitEqList' not in Controls: Controls['SeqParFitEqList'] = []
histNames = data['histNames']
if G2frame.dataDisplay:
G2frame.dataDisplay.Destroy()
if not G2frame.dataFrame.GetStatusBar():
Status = G2frame.dataFrame.CreateStatusBar()
Status.SetStatusText("Select column to export; Double click on column to plot data; on row for Covariance")
sampleParms = GetSampleParms()
# make dict of varied atom coords keyed by absolute position
newAtomDict = data[histNames[0]].get('newAtomDict',{}) # dict with atom positions; relative & absolute
# Possible error: the next might need to be data[histNames[0]]['varyList']
# error will arise if there constraints on coordinates?
atomLookup = {newAtomDict[item][0]:item for item in newAtomDict if item in data['varyList']}
# make dict of varied cell parameters equivalents
ESDlookup = {} # provides the Dij term for each Ak term (where terms are refined)
Dlookup = {} # provides the Ak term for each Dij term (where terms are refined)
# N.B. These Dij vars are missing a histogram #
newCellDict = data[histNames[0]].get('newCellDict',{})
for item in newCellDict:
if item in data['varyList']:
ESDlookup[newCellDict[item][0]] = item
Dlookup[item] = newCellDict[item][0]
# add coordinate equivalents to lookup table
for parm in atomLookup:
Dlookup[atomLookup[parm]] = parm
ESDlookup[parm] = atomLookup[parm]
# get unit cell & symmetry for all phases & initial stuff for later use
RecpCellTerms = {}
SGdata = {}
uniqCellIndx = {}
initialCell = {}
RcellLbls = {}
zeroDict = {}
Rcelldict = {}
for phase in Phases:
phasedict = Phases[phase]
pId = phasedict['pId']
pfx = str(pId)+'::' # prefix for A values from phase
RcellLbls[pId] = [pfx+'A'+str(i) for i in range(6)]
RecpCellTerms[pId] = G2lat.cell2A(phasedict['General']['Cell'][1:7])
zeroDict[pId] = dict(zip(RcellLbls[pId],6*[0.,]))
SGdata[pId] = phasedict['General']['SGData']
Rcelldict.update({lbl:val for lbl,val in zip(RcellLbls[pId],RecpCellTerms[pId])})
laue = SGdata[pId]['SGLaue']
if laue == '2/m':
laue += SGdata[pId]['SGUniq']
for symlist,celllist in cellGUIlist:
if laue in symlist:
uniqCellIndx[pId] = celllist
break
else: # should not happen
uniqCellIndx[pId] = range(6)
for i in uniqCellIndx[pId]:
initialCell[str(pId)+'::A'+str(i)] = RecpCellTerms[pId][i]
SetDataMenuBar(G2frame,G2frame.dataFrame.SequentialMenu)
G2frame.dataFrame.SetLabel('Sequential refinement results')
if not G2frame.dataFrame.GetStatusBar():
Status = G2frame.dataFrame.CreateStatusBar()
Status.SetStatusText('')
G2frame.dataFrame.Bind(wx.EVT_MENU, OnRenameSelSeq, id=wxID_RENAMESEQSEL)
G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSelSeq, id=wxID_SAVESEQSEL)
G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSelSeqCSV, id=wxID_SAVESEQSELCSV)
G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlotSelSeq, id=wxID_PLOTSEQSEL)
G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewPseudoVar, id=wxADDSEQVAR)
G2frame.dataFrame.Bind(wx.EVT_MENU, DelPseudoVar, id=wxDELSEQVAR)
G2frame.dataFrame.Bind(wx.EVT_MENU, EditPseudoVar, id=wxEDITSEQVAR)
G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewParFitEq, id=wxADDPARFIT)
G2frame.dataFrame.Bind(wx.EVT_MENU, CopyParFitEq, id=wxCOPYPARFIT)
G2frame.dataFrame.Bind(wx.EVT_MENU, DelParFitEq, id=wxDELPARFIT)
G2frame.dataFrame.Bind(wx.EVT_MENU, EditParFitEq, id=wxEDITPARFIT)
G2frame.dataFrame.Bind(wx.EVT_MENU, DoParEqFit, id=wxDOPARFIT)
EnablePseudoVarMenus()
EnableParFitEqMenus()
#-----------------------------------------------------------------------------------
# build up the data table by columns -----------------------------------------------
nRows = len(histNames)
colList = [nRows*[True]]
colSigs = [None]
colLabels = ['Use']
Types = [wg.GRID_VALUE_BOOL]
# start with Rwp values
if 'IMG ' not in histNames[0][:4]:
colList += [[data[name]['Rvals']['Rwp'] for name in histNames]]
colSigs += [None]
colLabels += ['Rwp']
Types += [wg.GRID_VALUE_FLOAT+':10,3',]
# add % change in Chi^2 in last cycle
if histNames[0][:4] not in ['SASD','IMG '] and Controls['ShowCell']:
colList += [[100.*data[name]['Rvals'].get('DelChi2',-1) for name in histNames]]
colSigs += [None]
colLabels += [u'\u0394\u03C7\u00B2 (%)']
Types += [wg.GRID_VALUE_FLOAT,]
deltaChiCol = len(colLabels)-1
# add changing sample parameters to table
for key in sampleParms:
colList += [sampleParms[key]]
colSigs += [None]
colLabels += [key]
Types += [wg.GRID_VALUE_FLOAT,]
# add unique cell parameters
if Controls['ShowCell']:
for pId in sorted(RecpCellTerms):
pfx = str(pId)+'::' # prefix for A values from phase
cells = []
cellESDs = []
colLabels += [pfx+cellUlbl[i] for i in uniqCellIndx[pId]]
colLabels += [pfx+'Vol']
Types += (1+len(uniqCellIndx[pId]))*[wg.GRID_VALUE_FLOAT,]
for name in histNames:
covData = {
'varyList': [Dlookup.get(striphist(v),v) for v in data[name]['varyList']],
'covMatrix': data[name]['covMatrix']
}
A = RecpCellTerms[pId][:] # make copy of starting A values
# update with refined values
for i in range(6):
var = str(pId)+'::A'+str(i)
if var in ESDlookup:
val = data[name]['newCellDict'][ESDlookup[var]][1] # get refined value
A[i] = val # override with updated value
# apply symmetry
Albls = [pfx+'A'+str(i) for i in range(6)]
cellDict = dict(zip(Albls,A))
A,zeros = G2stIO.cellFill(pfx,SGdata[pId],cellDict,zeroDict[pId])
# convert to direct cell & add only unique values to table
c = G2lat.A2cell(A)
vol = G2lat.calc_V(A)
cE = G2stIO.getCellEsd(pfx,SGdata[pId],A,covData)
cells += [[c[i] for i in uniqCellIndx[pId]]+[vol]]
cellESDs += [[cE[i] for i in uniqCellIndx[pId]]+[cE[-1]]]
colList += zip(*cells)
colSigs += zip(*cellESDs)
# add the variables that were refined; change from rows to columns
colList += zip(*[data[name]['variables'] for name in histNames])
colLabels += data[histNames[0]]['varyList']
Types += len(data[histNames[0]]['varyList'])*[wg.GRID_VALUE_FLOAT]
colSigs += zip(*[data[name]['sig'] for name in histNames])
# tabulate constrained variables, removing histogram numbers if needed
# from parameter label
depValDict = {}
depSigDict = {}
for name in histNames:
for var in data[name].get('depParmDict',{}):
val,sig = data[name]['depParmDict'][var]
svar = striphist(var,'*')
if svar not in depValDict:
depValDict[svar] = [val]
depSigDict[svar] = [sig]
else:
depValDict[svar].append(val)
depSigDict[svar].append(sig)
# add the dependent constrained variables to the table
for var in sorted(depValDict):
if len(depValDict[var]) != len(histNames): continue
colLabels.append(var)
Types += [wg.GRID_VALUE_FLOAT,]
colSigs += [depSigDict[var]]
colList += [depValDict[var]]
# add atom parameters to table
colLabels += atomLookup.keys()
Types += len(atomLookup)*[wg.GRID_VALUE_FLOAT]
for parm in sorted(atomLookup):
colList += [[data[name]['newAtomDict'][atomLookup[parm]][1] for name in histNames]]
if atomLookup[parm] in data[histNames[0]]['varyList']:
col = data[histNames[0]]['varyList'].index(atomLookup[parm])
colSigs += [[data[name]['sig'][col] for name in histNames]]
else:
colSigs += [None] # should not happen
# evaluate Pseudovars, their ESDs and add them to grid
for expr in Controls['SeqPseudoVars']:
obj = Controls['SeqPseudoVars'][expr]
calcobj = G2obj.ExpressionCalcObj(obj)
valList = []
esdList = []
for seqnum,name in enumerate(histNames):
sigs = data[name]['sig']
G2mv.InitVars()
parmDict = data[name].get('parmDict')
if parmDict and Controls['ShowCell']:
constraintInfo = data[name].get('constraintInfo',[[],[],{},[],seqnum])
groups,parmlist,constrDict,fixedList,ihst = constraintInfo
varyList = data[name]['varyList']
parmDict = data[name]['parmDict']
G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmDict,SeqHist=ihst)
derivs = np.array(
[EvalPSvarDeriv(calcobj,parmDict.copy(),var,ESD)
for var,ESD in zip(varyList,sigs)]
)
esdList.append(np.sqrt(
np.inner(derivs,np.inner(data[name]['covMatrix'],derivs.T))
))
PSvarDict = parmDict.copy()
UpdateParmDict(PSvarDict)
#calcobj.SetupCalc(PSvarDict)
calcobj.UpdateDict(PSvarDict)
else:
# PATCH: this routine can go away eventually once parmDict is always in
# sequential refinement
PSvarDict,unused,unused = CreatePSvarDict(seqnum,name)
calcobj.SetupCalc(PSvarDict)
valList.append(calcobj.EvalExpression())
if not esdList:
esdList = None
colList += [valList]
colSigs += [esdList]
colLabels += [expr]
Types += [wg.GRID_VALUE_FLOAT,]
#---- table build done -------------------------------------------------------------
# Make dict needed for creating & editing pseudovars (PSvarDict).
name = histNames[0]
parmDict = data[name].get('parmDict')
if parmDict:
PSvarDict = parmDict.copy()
UpdateParmDict(PSvarDict)
else:
print 'Sequential refinement needs to be rerun to obtain ESDs for PseudoVariables'
# PATCH: this routine can go away eventually
PSvarDict,unused,unused = CreatePSvarDict(0,histNames[0])
# Also dicts of dependent (depVarDict) & independent vars (indepVarDict)
# for Parametric fitting from the data table
parmDict = dict(zip(colLabels,zip(*colList)[0])) # scratch dict w/all values in table
parmDict.update(
{var:val for var,val in data[name].get('newCellDict',{}).values()} # add varied reciprocal cell terms
)
name = histNames[0]
indepVarDict = { # values in table w/o ESDs
var:colList[i][0] for i,var in enumerate(colLabels) if colSigs[i] is None
}
# make dict of dependent vars (w/ESDs) that are not converted (Dij to Ak or dAx to Ax)
depVarDict = {
var:colList[i][0] for i,var in enumerate(colLabels)
if colSigs[i] is not None and striphist(var) not in Dlookup
}
# add recip cell coeff. values
depVarDict.update({var:val for var,val in data[name].get('newCellDict',{}).values()})
G2frame.dataDisplay = GSGrid(parent=G2frame.dataFrame)
G2frame.SeqTable = Table(
[list(c) for c in zip(*colList)], # convert from columns to rows
colLabels=colLabels,rowLabels=histNames,types=Types)
G2frame.dataDisplay.SetTable(G2frame.SeqTable, True)
#G2frame.dataDisplay.EnableEditing(False)
# make all but first column read-only
for c in range(1,len(colLabels)):
for r in range(nRows):
G2frame.dataDisplay.SetCellReadOnly(r,c)
G2frame.dataDisplay.Bind(wg.EVT_GRID_LABEL_LEFT_DCLICK, PlotSelect)
G2frame.dataDisplay.Bind(wg.EVT_GRID_LABEL_RIGHT_CLICK, SetLabelString)
G2frame.dataDisplay.SetRowLabelSize(8*len(histNames[0])) #pretty arbitrary 8
G2frame.dataDisplay.SetMargins(0,0)
G2frame.dataDisplay.AutoSizeColumns(True)
if prevSize:
G2frame.dataDisplay.SetSize(prevSize)
else:
G2frame.dataFrame.setSizePosLeft([700,350])
# highlight unconverged shifts
if histNames[0][:4] not in ['SASD','IMG ']:
for row,name in enumerate(histNames):
deltaChi = G2frame.SeqTable.GetValue(row,deltaChiCol)
if deltaChi > 10.:
G2frame.dataDisplay.SetCellStyle(row,deltaChiCol,color=wx.Colour(255,0,0))
elif deltaChi > 1.0:
G2frame.dataDisplay.SetCellStyle(row,deltaChiCol,color=wx.Colour(255,255,0))
G2frame.dataDisplay.InstallGridToolTip(GridSetToolTip,GridColLblToolTip)
################################################################################
##### Main PWDR panel
################################################################################
[docs]def UpdatePWHKPlot(G2frame,kind,item):
'''Called when the histogram main tree entry is called. Displays the
histogram weight factor, refinement statistics for the histogram
and the range of data for a simulation.
Also invokes a plot of the histogram.
'''
def onEditSimRange(event):
'Edit simulation range'
inp = [
min(data[1][0]),
max(data[1][0]),
None
]
inp[2] = (inp[1] - inp[0])/(len(data[1][0])-1.)
names = ('start angle', 'end angle', 'step size')
dictlst = [inp] * len(inp)
elemlst = range(len(inp))
dlg = ScrolledMultiEditor(
G2frame,[inp] * len(inp), range(len(inp)), names,
header='Edit simulation range',
minvals=(0.001,0.001,0.0001),
maxvals=(180.,180.,.1),
)
dlg.CenterOnParent()
val = dlg.ShowModal()
dlg.Destroy()
if val != wx.ID_OK: return
if inp[0] > inp[1]:
end,start,step = inp
else:
start,end,step = inp
step = abs(step)
N = int((end-start)/step)+1
newdata = np.linspace(start,end,N,True)
if len(newdata) < 2: return # too small a range - reject
data[1] = [newdata,np.zeros_like(newdata),np.ones_like(newdata),
np.zeros_like(newdata),np.zeros_like(newdata),np.zeros_like(newdata)]
Tmin = newdata[0]
Tmax = newdata[-1]
G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,item,'Limits'),
[(Tmin,Tmax),[Tmin,Tmax]])
UpdatePWHKPlot(G2frame,kind,item) # redisplay data screen
def OnPlot3DHKL(event):
refList = data[1]['RefList']
FoMax = np.max(refList.