source: trunk/GSASIIconstrGUI.py @ 3079

Last change on this file since 3079 was 3079, checked in by vondreele, 4 years ago

implement rigid body reference vectors

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 93.9 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIconstrGUI - constraint GUI routines
3########### SVN repository information ###################
4# $Date: 2017-09-18 13:52:24 +0000 (Mon, 18 Sep 2017) $
5# $Author: vondreele $
6# $Revision: 3079 $
7# $URL: trunk/GSASIIconstrGUI.py $
8# $Id: GSASIIconstrGUI.py 3079 2017-09-18 13:52:24Z vondreele $
9########### SVN repository information ###################
10'''
11*GSASIIconstrGUI: Constraint GUI routines*
12------------------------------------------
13
14Used to define constraints and rigid bodies.
15
16'''
17import sys
18import wx
19import wx.grid as wg
20import random as ran
21import numpy as np
22import numpy.ma as ma
23import numpy.linalg as nl
24import os.path
25import GSASIIpath
26GSASIIpath.SetVersionNumber("$Revision: 3079 $")
27import GSASIIElem as G2elem
28import GSASIIElemGUI as G2elemGUI
29import GSASIIstrIO as G2stIO
30import GSASIImapvars as G2mv
31import GSASIImath as G2mth
32import GSASIIlattice as G2lat
33import GSASIIdataGUI as G2gd
34import GSASIIctrlGUI as G2G
35import GSASIIplot as G2plt
36import GSASIIobj as G2obj
37import GSASIIspc as G2spc
38VERY_LIGHT_GREY = wx.Colour(235,235,235)
39
40class ConstraintDialog(wx.Dialog):
41    '''Window to edit Constraint values
42    '''
43    def __init__(self,parent,title,text,data,separator='*',varname="",varyflag=False):
44        wx.Dialog.__init__(self,parent,-1,'Edit '+title, 
45            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
46        self.data = data[:]
47        self.newvar = [varname,varyflag]
48        panel = wx.Panel(self)
49        mainSizer = wx.BoxSizer(wx.VERTICAL)
50        topLabl = wx.StaticText(panel,-1,text)
51        mainSizer.Add((10,10),1)
52        mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
53        mainSizer.Add((10,10),1)
54        dataGridSizer = wx.FlexGridSizer(cols=3,hgap=2,vgap=2)
55        self.OkBtn = wx.Button(panel,wx.ID_OK)
56        for id in range(len(self.data)):
57            lbl1 = lbl = str(self.data[id][1])
58            if lbl[-1] != '=': lbl1 = lbl + ' ' + separator + ' '
59            name = wx.StaticText(panel,wx.ID_ANY,lbl1,
60                                 style=wx.ALIGN_RIGHT)
61            scale = G2G.ValidatedTxtCtrl(panel,self.data[id],0,
62                                          typeHint=float,
63                                          OKcontrol=self.DisableOK)
64            dataGridSizer.Add(name,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,5)
65            dataGridSizer.Add(scale,0,wx.RIGHT,3)
66            if ':' in lbl:
67                dataGridSizer.Add(
68                    wx.StaticText(panel,-1,G2obj.fmtVarDescr(lbl)),
69                    0,wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,3)
70            else:
71                dataGridSizer.Add((-1,-1))
72        if title == 'New Variable':
73            name = wx.StaticText(panel,wx.ID_ANY,"New variable's\nname (optional)",
74                                 style=wx.ALIGN_CENTER)
75            scale = G2G.ValidatedTxtCtrl(panel,self.newvar,0,
76                                          typeHint=str,notBlank=False)
77            dataGridSizer.Add(name,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,5)
78            dataGridSizer.Add(scale,0,wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,3)
79            self.refine = wx.CheckBox(panel,label='Refine?')
80            self.refine.SetValue(self.newvar[1]==True)
81            self.refine.Bind(wx.EVT_CHECKBOX, self.OnCheckBox)
82            dataGridSizer.Add(self.refine,0,wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,3)
83           
84        mainSizer.Add(dataGridSizer,0,wx.EXPAND)
85        self.OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
86        self.OkBtn.SetDefault()
87        cancelBtn = wx.Button(panel,wx.ID_CANCEL)
88        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
89        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
90        btnSizer.Add((20,20),1)
91        btnSizer.Add(self.OkBtn)
92        btnSizer.Add((20,20),1)
93        btnSizer.Add(cancelBtn)
94        btnSizer.Add((20,20),1)
95
96        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
97        panel.SetSizer(mainSizer)
98        panel.Fit()
99        self.Fit()
100        self.CenterOnParent()
101       
102    def DisableOK(self,setting):
103        if setting:
104            self.OkBtn.Enable()
105        else:
106            self.OkBtn.Disable()
107
108    def OnCheckBox(self,event):
109        self.newvar[1] = self.refine.GetValue()
110
111    def OnOk(self,event):
112        parent = self.GetParent()
113        parent.Raise()
114        self.EndModal(wx.ID_OK)             
115
116    def OnCancel(self,event):
117        parent = self.GetParent()
118        parent.Raise()
119        self.EndModal(wx.ID_CANCEL)             
120
121    def GetData(self):
122        return self.data
123       
124################################################################################
125#####  Constraints
126################################################################################           
127       
128def UpdateConstraints(G2frame,data):
129    '''Called when Constraints tree item is selected.
130    Displays the constraints in the data window
131    '''
132    if not data:
133        data.update({'Hist':[],'HAP':[],'Phase':[],'Global':[]})       #empty dict - fill it
134    if 'Global' not in data:                                            #patch
135        data['Global'] = []
136    # DEBUG code ########################################
137    #import GSASIIconstrGUI
138    #reload(GSASIIconstrGUI)
139    #reload(G2obj)
140    #reload(G2stIO)
141    #import GSASIIstrMain
142    #reload(GSASIIstrMain)   
143    #reload(G2mv)
144    #reload(G2gd)
145    ###################################################
146    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
147    G2obj.IndexAllIds(Histograms,Phases)
148    ##################################################################################
149    # patch: convert old-style (str) variables in constraints to G2VarObj objects
150    for key,value in data.items():
151        if key.startswith('_'): continue
152        j = 0
153        for cons in value:
154            #print cons             # DEBUG
155            for i in range(len(cons[:-3])):
156                if type(cons[i][1]) is str:
157                    cons[i][1] = G2obj.G2VarObj(cons[i][1])
158                    j += 1
159        if j:
160            print str(key) + ': '+str(j)+' variable(s) as strings converted to objects'
161    ##################################################################################
162    rigidbodyDict = G2frame.GPXtree.GetItemPyData(
163        G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Rigid bodies'))
164    rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
165    rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
166    badPhaseParms = ['Ax','Ay','Az','Amul','AI/A','Atype','SHorder','mV0','mV1','mV2','waveType','Vol','isMag',]
167    globalList = rbDict.keys()
168    globalList.sort()
169    try:
170        AtomDict = dict([Phases[phase]['pId'],Phases[phase]['Atoms']] for phase in Phases)
171    except KeyError:
172        G2frame.ErrorDialog('Constraint Error','Constraints cannot be set until a cycle of least squares'+
173                            'has been run.\nWe suggest you refine a scale factor.')
174        return
175
176    # create a list of the phase variables
177    Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable,MFtable,maxSSwave = G2stIO.GetPhaseData(Phases,rbIds=rbIds,Print=False)
178    phaseList = []
179    for item in phaseDict:
180        if item.split(':')[2] not in badPhaseParms:
181            phaseList.append(item)
182    phaseList.sort()
183    phaseAtNames = {}
184    phaseAtTypes = {}
185    TypeList = []
186    for item in phaseList:
187        Split = item.split(':')
188        if Split[2][:2] in ['AU','Af','dA','AM']:
189            Id = int(Split[0])
190            phaseAtNames[item] = AtomDict[Id][int(Split[3])][0]
191            phaseAtTypes[item] = AtomDict[Id][int(Split[3])][1]
192            if phaseAtTypes[item] not in TypeList:
193                TypeList.append(phaseAtTypes[item])
194        else:
195            phaseAtNames[item] = ''
196            phaseAtTypes[item] = ''
197             
198    # create a list of the hist*phase variables; include wildcards here
199    hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
200    hapList = [i for i in hapDict.keys() if i.split(':')[2] not in ('Type',)]
201    hapList.sort()
202    wildList = [] # list of variables with "*" for histogram number
203    for i in hapList:
204        s = i.split(':')
205        if s[1] == "": continue
206        s[1] = '*'
207        sj = ':'.join(s)
208        if sj not in wildList: wildList.append(sj)
209    #wildList.sort() # unneeded
210    hapList += wildList
211    histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
212    histList = []
213    for item in histDict:
214        if item.split(':')[2] not in ['Omega','Type','Chi','Phi',
215                                      'Azimuth','Gonio. radius',
216                                      'Lam1','Lam2','Back','Temperature','Pressure',
217                                      'FreePrm1','FreePrm2','FreePrm3',
218                                      ]:
219            histList.append(item)
220    histList.sort()
221    wildList = []
222    # for i in histList: # any reason to have this for hist constraints?
223    #     s = i.split(':')
224    #     if s[1] == "": continue
225    #     s[1] = '*'
226    #     sj = ':'.join(s)
227    #     if sj not in wildList: wildList.append(sj)
228    # histList += wildList
229    Indx = {}
230    G2frame.Page = [0,'phs']
231       
232    def FindEquivVarb(name,nameList):
233        'Creates a list of variables appropriate to constrain with name'
234        outList = []
235        #phlist = []
236        items = name.split(':')
237        namelist = [items[2],]
238        if 'dA' in name:
239            namelist = ['dAx','dAy','dAz']
240        elif 'AU' in name:
241            namelist = ['AUiso','AU11','AU22','AU33','AU12','AU13','AU23']
242        elif 'AM' in name:
243            namelist = ['AMx','AMy','AMz']
244        elif items[-1] in ['A0','A1','A2','A3','A4','A5']:
245            namelist = ['A0','A1','A2','A3','A4','A5']
246        elif items[-1] in ['D11','D22','D33','D12','D13','D23']:
247            namelist = ['D11','D22','D33','D12','D13','D23']
248        elif 'Tm' in name:
249            namelist = ['Tmin','Tmax']
250        elif 'RB' in name:
251            rbfx = 'RB'+items[2][2]
252            if 'T' in name and 'Tr' not in name:
253                namelist = [rbfx+'T11',rbfx+'T22',rbfx+'T33',rbfx+'T12',rbfx+'T13',rbfx+'T23']
254            if 'L' in name:
255                namelist = [rbfx+'L11',rbfx+'L22',rbfx+'L33',rbfx+'L12',rbfx+'L13',rbfx+'L23']
256            if 'S' in name:
257                namelist = [rbfx+'S12',rbfx+'S13',rbfx+'S21',rbfx+'S23',rbfx+'S31',rbfx+'S32',rbfx+'SAA',rbfx+'SBB']
258            if 'U' in name:
259                namelist = [rbfx+'U',]
260
261        for item in nameList:
262            keys = item.split(':')
263            #if keys[0] not in phlist:
264            #    phlist.append(keys[0])
265            if items[1] == '*' and keys[2] in namelist: # wildcard -- select only sequential options
266                keys[1] = '*'
267                mitem = ':'.join(keys)
268                if mitem == name: continue
269                if mitem not in outList: outList.append(mitem)
270            elif keys[2] in namelist and item != name:
271                outList.append(item)
272        return outList
273       
274    def SelectVarbs(page,FrstVarb,varList,legend,constType):
275        '''Select variables used in constraints after one variable has
276        been selected. This routine determines the appropriate variables to be
277        used based on the one that has been selected and asks for more to be added.
278
279        It then creates the constraint and adds it to the constraints list.
280       
281        Called from OnAddEquivalence, OnAddFunction & OnAddConstraint (all but
282        OnAddHold)
283
284        :param list page: defines calling page -- type of variables to be used
285        :parm GSASIIobj.G2VarObj FrstVarb: reference to first selected variable
286        :param list varList: list of other appropriate variables to select from
287        :param str legend: header for selection dialog
288        :param str constType: type of constraint to be generated
289        :returns: a constraint, as defined in
290          :ref:`GSASIIobj <Constraint_definitions_table>`
291        '''
292        choices = [[i]+list(G2obj.VarDescr(i)) for i in varList]
293        meaning = G2obj.getDescr(FrstVarb.name)
294        if not meaning:
295            meaning = "(no definition found!)"
296        l = str(FrstVarb).split(':')
297        # make lists of phases & histograms to iterate over
298        phaselist = [l[0]]
299        if l[0]:
300            phaselbl = ['phase #'+l[0]]
301            if len(Phases) > 1:
302                phaselist += ['all'] 
303                phaselbl += ['all phases']
304        else:
305            phaselbl = ['']
306        histlist = [l[1]]
307        if l[1] == '*':
308            pass
309        elif l[1]:
310            histlbl = ['histogram #'+l[1]]
311            if len(Histograms) > 1:
312                histlist += ['all']
313                histlbl += ['all histograms']
314                typ = Histograms[G2obj.LookupHistName(l[1])[0]]['Instrument Parameters'][0]['Type'][1]
315                i = 0
316                for hist in Histograms:
317                    if Histograms[hist]['Instrument Parameters'][0]['Type'][1] == typ: i += 1
318                if i > 1:
319                    histlist += ['all='+typ]
320                    histlbl += ['all '+typ+' histograms']
321        else:
322            histlbl = ['']
323        # make a list of equivalent parameter names
324        nameList = [FrstVarb.name]
325        for var in varList:
326            nam = var.split(":")[2]
327            if nam not in nameList: nameList += [nam]
328        # add "wild-card" names to the list of variables
329        if l[1] == '*':
330            pass
331        elif page[1] == 'phs':
332            if 'RB' in FrstVarb.name:
333                pass
334            elif FrstVarb.atom is None:
335                for nam in nameList:
336                    for ph,plbl in zip(phaselist,phaselbl):
337                        if plbl: plbl = 'For ' + plbl
338                        var = ph+"::"+nam
339                        if var == str(FrstVarb) or var in varList: continue
340                        varList += [var]
341                        choices.append([var,plbl,meaning])
342            else:
343                for nam in nameList:
344                    for ph,plbl in zip(phaselist,phaselbl):
345                        if plbl: plbl = ' in ' + plbl
346                        for atype in ['']+TypeList:
347                            if atype:
348                                albl = "For "+atype+" atoms"
349                                akey = "all="+atype                       
350                            else:
351                                albl = "For all atoms"
352                                akey = "all"
353                            var = ph+"::"+nam+":"+akey
354                            if var == str(FrstVarb) or var in varList: continue
355                            varList += [var]
356                            choices.append([var,albl+plbl,meaning])
357        elif page[1] == 'hap':
358            if FrstVarb.name == "Scale":
359                meaning = "Phase fraction"
360            for nam in nameList:
361                for ph,plbl in zip(phaselist,phaselbl):
362                    if plbl: plbl = 'For ' + plbl
363                    for hst,hlbl in zip(histlist,histlbl):
364                        if hlbl:
365                            if plbl:
366                                hlbl = ' in ' + hlbl
367                            else:
368                                hlbl = 'For ' + hlbl                               
369                            var = ph+":"+hst+":"+nam
370                            if var == str(FrstVarb) or var in varList: continue
371                            varList += [var]
372                            choices.append([var,plbl+hlbl,meaning])
373        elif page[1] == 'hst':
374            if FrstVarb.name == "Scale":
375                meaning = "Scale factor"
376            for nam in nameList:
377                for hst,hlbl in zip(histlist,histlbl):
378                    if hlbl:
379                        hlbl = 'For ' + hlbl                               
380                        var = ":"+hst+":"+nam
381                        if var == str(FrstVarb) or var in varList: continue
382                        varList += [var]
383                        choices.append([var,hlbl,meaning])
384        elif page[1] == 'glb' or page[1] == 'sym':
385            pass
386        else:
387            raise Exception, 'Unknown constraint page '+ page[1]                   
388        if len(choices):
389            l1 = l2 = 1
390            for i1,i2,i3 in choices:
391                l1 = max(l1,len(i1))
392                l2 = max(l2,len(i2))
393            fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}"
394            atchoices = [fmt.format(*i1) for i1 in choices] # reformat list as str with columns
395            dlg = G2G.G2MultiChoiceDialog(
396                G2frame,legend,
397                'Constrain '+str(FrstVarb)+' with...',atchoices,
398                toggle=False,size=(625,400),monoFont=True)
399            dlg.CenterOnParent()
400            res = dlg.ShowModal()
401            Selections = dlg.GetSelections()[:]
402            dlg.Destroy()
403            if res != wx.ID_OK: return []
404            if len(Selections) == 0:
405                dlg = wx.MessageDialog(
406                    G2frame,
407                    'No variables were selected to include with '+str(FrstVarb),
408                    'No variables')
409                dlg.CenterOnParent()
410                dlg.ShowModal()
411                dlg.Destroy()
412                return []
413        else:
414            dlg = wx.MessageDialog(
415                G2frame,
416                'There are no appropriate variables to include with '+str(FrstVarb),
417                'No variables')
418            dlg.CenterOnParent()
419            dlg.ShowModal()
420            dlg.Destroy()
421            return []
422        # now process the variables provided by the user
423        varbs = [str(FrstVarb),] # list of selected variables
424        for sel in Selections:
425            var = varList[sel]
426            # phase(s) included
427            l = var.split(':')
428            if l[0] == "all":
429                phlist = [str(Phases[phase]['pId']) for phase in Phases]
430            else:
431                phlist = [l[0]]
432            # histogram(s) included
433            if l[1] == "all":
434                hstlist = [str(Histograms[hist]['hId']) for hist in Histograms]
435            elif '=' in l[1]:
436                htyp = l[1].split('=')[1]
437                hstlist = [str(Histograms[hist]['hId']) for hist in Histograms if 
438                           Histograms[hist]['Instrument Parameters'][0]['Type'][1] == htyp]
439            else:
440                hstlist = [l[1]]
441            if len(l) == 3:
442                for ph in phlist:
443                    for hst in hstlist:
444                        var = ph + ":" + hst + ":" + l[2]
445                        if var in varbs: continue
446                        varbs.append(var)
447            else: # constraints with atoms or rigid bodies
448                if len(l) == 5: # rigid body parameter
449                    var = ':'.join(l)
450                    if var in varbs: continue
451                    varbs.append(var)
452                elif l[3] == "all":
453                    for ph in phlist:
454                        key = G2obj.LookupPhaseName(l[0])[0]
455                        for hst in hstlist: # should be blank
456                            for iatm,at in enumerate(Phases[key]['Atoms']):
457                                var = ph + ":" + hst + ":" + l[2] + ":" + str(iatm)
458                                if var in varbs: continue
459                                varbs.append(var)
460                elif '=' in l[3]:
461                    for ph in phlist:
462                        key = G2obj.LookupPhaseName(l[0])[0]
463                        cx,ct,cs,cia = Phases[key]['General']['AtomPtrs']
464                        for hst in hstlist: # should be blank
465                            atyp = l[3].split('=')[1]
466                            for iatm,at in enumerate(Phases[key]['Atoms']):
467                                if at[ct] != atyp: continue
468                                var = ph + ":" + hst + ":" + l[2] + ":" + str(iatm)
469                                if var in varbs: continue
470                                varbs.append(var)
471                else:
472                    for ph in phlist:
473                        key = G2obj.LookupPhaseName(l[0])[0]
474                        for hst in hstlist: # should be blank
475                            var = ph + ":" + hst + ":" + l[2] + ":" + l[3]
476                            if var in varbs: continue
477                            varbs.append(var)
478        if len(varbs) >= 1 or 'constraint' in constType:
479            constr = [[1.0,FrstVarb]]
480            for item in varbs[1:]:
481                constr += [[1.0,G2obj.G2VarObj(item)]]
482            if 'equivalence' in constType:
483                return [constr+[None,None,'e']]
484            elif 'function' in constType:
485                return [constr+[None,False,'f']]
486            elif 'constraint' in constType:
487                return [constr+[1.0,None,'c']]
488            else:
489                raise Exception,'Unknown constraint type: '+str(constType)
490        else:
491            dlg = wx.MessageDialog(
492                G2frame,
493                'There are no selected variables to include with '+str(FrstVarb),
494                'No variables')
495            dlg.CenterOnParent()
496            dlg.ShowModal()
497            dlg.Destroy()
498        return []
499
500    def CheckConstraints(constraintSet):
501        '''Check for errors in a set of constraints. Constraints based on symmetry (etc.)
502        are generated by running :func:`GSASIIstrIO.GetPhaseData`.
503        '''
504        G2mv.InitVars()   
505        constDictList,fixedList,ignored = G2stIO.ProcessConstraints(constraintSet)
506        # generate symmetry constraints to check for conflicts
507        rigidbodyDict = G2frame.GPXtree.GetItemPyData(   
508            G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Rigid bodies'))
509        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
510        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
511        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave = G2stIO.GetPhaseData(
512            Phases,RestraintDict=None,rbIds=rbIds,Print=False) # generates atom symmetry constraints
513        return G2mv.CheckConstraints('',constDictList,fixedList)
514
515    def CheckAddedConstraint(newcons):
516        '''Check a new constraint that has just been input.
517        If there is an error display a message and discard the last entry
518
519        Since the varylist is not available, no warning messages
520        should be generated here
521
522        :returns: True if constraint should be added
523        '''
524        allcons1 = []
525        for key in data:
526            if key.startswith('_'): continue
527            allcons1 += data[key]
528        allcons = allcons1[:]
529        allcons += newcons
530        if not len(allcons): return True
531        errmsg,warnmsg = CheckConstraints(allcons)
532        if errmsg:
533            res = G2frame.ErrorDialog('Constraint Error',
534                'Error with newly added constraint:\n'+errmsg+
535                '\nIgnoring newly added constraint',parent=G2frame)
536            # reset error status
537            errmsg,warnmsg = CheckConstraints(allcons1)
538            if errmsg:
539                print errmsg
540                print G2mv.VarRemapShow([],True)
541            return False
542        elif warnmsg:
543            print 'Unexpected contraint warning:\n',warnmsg
544        return True
545
546    def CheckChangedConstraint():
547        '''Check all constraints after an edit has been made.
548        If there is an error display a message and reject the change.
549
550        Since the varylist is not available, no warning messages
551        should be generated.
552       
553        :returns: True if the edit should be retained
554        '''
555        allcons = []
556        for key in data:
557            if key.startswith('_'): continue
558            allcons += data[key]
559        if not len(allcons): return True
560        errmsg,warnmsg = CheckConstraints(allcons)
561        if errmsg:
562            res = G2frame.ErrorDialog('Constraint Error',
563                'Error after editing constraint:\n'+errmsg+
564                '\nDiscarding last constraint edit',parent=G2frame)
565            # reset error status
566            errmsg,warnmsg = CheckConstraints(allcons1)
567            if errmsg:
568                print errmsg
569                print G2mv.VarRemapShow([],True)
570            return False
571        elif warnmsg:
572            print 'Unexpected contraint warning:\n',warnmsg
573        return True
574             
575    def PageSelection(page):
576        'Decode page reference'
577        if page[1] == "phs":
578            vartype = "phase"
579            varList = phaseList
580            constrDictEnt = 'Phase'
581        elif page[1] == "hap":
582            vartype = "Histogram*Phase"
583            varList = hapList
584            constrDictEnt = 'HAP'
585        elif page[1] == "hst":
586            vartype = "Histogram"
587            varList = histList
588            constrDictEnt = 'Hist'
589        elif page[1] == "glb":
590            vartype = "Global"
591            varList = globalList
592            constrDictEnt = 'Global'
593        elif page[1] == "sym":
594            return None,None,None
595        else:
596            raise Exception,'Should not happen!'
597        return vartype,varList,constrDictEnt
598
599    def OnAddHold(event):
600        '''Create a new Hold constraint
601
602        Hold constraints allows the user to select one variable (the list of available
603        variables depends on which tab is currently active).
604        '''
605        page = G2frame.Page
606        vartype,varList,constrDictEnt = PageSelection(page)
607        varList = G2obj.SortVariables(varList)
608        if vartype is None: return
609        title1 = "Hold "+vartype+" variable"
610        if not varList:
611            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
612                parent=G2frame)
613            return
614        l2 = l1 = 1
615        for i in varList:
616            l1 = max(l1,len(i))
617            loc,desc = G2obj.VarDescr(i)
618            l2 = max(l2,len(loc))
619        fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}"
620        varListlbl = [fmt.format(i,*G2obj.VarDescr(i)) for i in varList]
621        #varListlbl = ["("+i+") "+G2obj.fmtVarDescr(i) for i in varList]
622        legend = "Select variables to hold (Will not be varied, even if vary flag is set)"
623        dlg = G2G.G2MultiChoiceDialog(G2frame,
624            legend,title1,varListlbl,toggle=False,size=(625,400),monoFont=True)
625        dlg.CenterOnParent()
626        if dlg.ShowModal() == wx.ID_OK:
627            for sel in dlg.GetSelections():
628                Varb = varList[sel]
629                VarObj = G2obj.G2VarObj(Varb)
630                newcons = [[[0.0,VarObj],None,None,'h']]
631                if CheckAddedConstraint(newcons):
632                    data[constrDictEnt] += newcons
633        dlg.Destroy()
634        wx.CallAfter(OnPageChanged,None)
635       
636    def OnAddEquivalence(event):
637        '''add an Equivalence constraint'''
638        page = G2frame.Page
639        vartype,varList,constrDictEnt = PageSelection(page)
640        if vartype is None: return
641        title1 = "Setup equivalent "+vartype+" variables"
642        title2 = "Select additional "+vartype+" variable(s) to be equivalent with "
643        if not varList:
644            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
645                parent=G2frame)
646            return
647#        legend = "Select variables to make equivalent (only one of the variables will be varied when all are set to be varied)"
648        GetAddVars(page,title1,title2,varList,constrDictEnt,'equivalence')
649       
650    def OnAddAtomEquiv(event):
651        ''' Add equivalences between all parameters on atoms '''
652        page = G2frame.Page
653        vartype,varList,constrDictEnt = PageSelection(page)
654        if vartype is None: return
655        title1 = "Setup equivalent atom variables"
656        title2 = "Select additional atoms(s) to be equivalent with "
657        if not varList:
658            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
659                parent=G2frame)
660            return
661#        legend = "Select atoms to make equivalent (only one of the atom variables will be varied when all are set to be varied)"
662        GetAddAtomVars(page,title1,title2,varList,constrDictEnt,'equivalence')
663       
664    def OnAddRiding(event):
665        ''' Add riding equivalences between all parameters on atoms '''
666        page = G2frame.Page
667        vartype,varList,constrDictEnt = PageSelection(page)
668        if vartype is None: return
669        title1 = "Setup riding atoms "
670        title2 = "Select additional atoms(s) to ride on "
671        if not varList:
672            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
673                parent=G2frame)
674            return
675#        legend = "Select atoms to ride (only one of the atom variables will be varied when all are set to be varied)"
676        GetAddAtomVars(page,title1,title2,varList,constrDictEnt,'riding')
677   
678    def OnAddFunction(event):
679        '''add a Function (new variable) constraint'''
680        page = G2frame.Page
681        vartype,varList,constrDictEnt = PageSelection(page)
682        if vartype is None: return
683        title1 = "Setup new variable based on "+vartype+" variables"
684        title2 = "Include additional "+vartype+" variable(s) to be included with "
685        if not varList:
686            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
687                parent=G2frame)
688            return
689#        legend = "Select variables to include in a new variable (the new variable will be varied when all included variables are varied)"
690        GetAddVars(page,title1,title2,varList,constrDictEnt,'function')
691                       
692    def OnAddConstraint(event):
693        '''add a constraint equation to the constraints list'''
694        page = G2frame.Page
695        vartype,varList,constrDictEnt = PageSelection(page)
696        if vartype is None: return
697        title1 = "Setup constraint on "+vartype+" variables"
698        title2 = "Select additional "+vartype+" variable(s) to include in constraint with "
699        if not varList:
700            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
701                parent=G2frame)
702            return
703#        legend = "Select variables to include in a constraint equation (the values will be constrainted to equal a specified constant)"
704        GetAddVars(page,title1,title2,varList,constrDictEnt,'constraint')
705
706    def GetAddVars(page,title1,title2,varList,constrDictEnt,constType):
707        '''Get the variables to be added for OnAddEquivalence, OnAddFunction,
708        and OnAddConstraint. Then create and check the constraint.
709        '''
710        #varListlbl = ["("+i+") "+G2obj.fmtVarDescr(i) for i in varList]
711        if constType == 'equivalence':
712            omitVars = G2mv.GetDependentVars()
713        else:
714            omitVars = []
715        varList = G2obj.SortVariables([i for i in varList if i not in omitVars])
716        l2 = l1 = 1
717        for i in varList:
718            l1 = max(l1,len(i))
719            loc,desc = G2obj.VarDescr(i)
720            l2 = max(l2,len(loc))
721        fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}"
722        varListlbl = [fmt.format(i,*G2obj.VarDescr(i)) for i in varList]
723        dlg = G2G.G2SingleChoiceDialog(G2frame,'Select 1st variable:',
724            title1,varListlbl,monoFont=True,size=(625,400))
725        dlg.CenterOnParent()
726        if dlg.ShowModal() == wx.ID_OK:
727            if constType == 'equivalence':
728                omitVars = G2mv.GetDependentVars() + G2mv.GetIndependentVars()
729            sel = dlg.GetSelection()
730            FrstVarb = varList[sel]
731            VarObj = G2obj.G2VarObj(FrstVarb)
732            moreVarb = G2obj.SortVariables(
733                FindEquivVarb(FrstVarb,[i for i in varList if i not in omitVars]))
734            newcons = SelectVarbs(page,VarObj,moreVarb,title2+FrstVarb,constType)
735            if len(newcons) > 0:
736                if CheckAddedConstraint(newcons):
737                    data[constrDictEnt] += newcons
738        dlg.Destroy()
739        wx.CallAfter(OnPageChanged,None)
740                       
741    def FindNeighbors(phase,FrstName,AtNames):
742        General = phase['General']
743        cx,ct,cs,cia = General['AtomPtrs']
744        Atoms = phase['Atoms']
745        atNames = [atom[ct-1] for atom in Atoms]
746        Cell = General['Cell'][1:7]
747        Amat,Bmat = G2lat.cell2AB(Cell)
748        atTypes = General['AtomTypes']
749        Radii = np.array(General['BondRadii'])
750        AtInfo = dict(zip(atTypes,Radii)) #or General['BondRadii']
751        Orig = atNames.index(FrstName.split()[1])
752        OType = Atoms[Orig][ct]
753        XYZ = G2mth.getAtomXYZ(Atoms,cx)       
754        Neigh = []
755        Dx = np.inner(Amat,XYZ-XYZ[Orig]).T
756        dist = np.sqrt(np.sum(Dx**2,axis=1))
757        sumR = AtInfo[OType]+0.5    #H-atoms only!
758        IndB = ma.nonzero(ma.masked_greater(dist-0.85*sumR,0.))
759        for j in IndB[0]:
760            if j != Orig:
761                Neigh.append(AtNames[j])
762        return Neigh
763       
764    def GetAddAtomVars(page,title1,title2,varList,constrDictEnt,constType):
765        '''Get the atom variables to be added for OnAddAtomEquiv. Then create and
766        check the constraints. Riding for H atoms only.
767        '''
768        Atoms = {G2obj.VarDescr(i)[0]:[] for i in varList if 'Atom' in G2obj.VarDescr(i)[0]}
769        for item in varList:
770            atName = G2obj.VarDescr(item)[0]
771            if atName in Atoms:
772                Atoms[atName].append(item)
773        AtNames = Atoms.keys()
774        AtNames.sort()
775        dlg = G2G.G2SingleChoiceDialog(G2frame,'Select 1st atom:',
776            title1,AtNames,monoFont=True,size=(625,400))
777        dlg.CenterOnParent()
778        FrstAtom = ''
779        if dlg.ShowModal() == wx.ID_OK:
780            sel = dlg.GetSelection()
781            FrstAtom = AtNames[sel]
782            if 'riding' in constType:
783                phaseName = (FrstAtom.split(' in ')[1]).strip()
784                phase = Phases[phaseName]
785                AtNames = FindNeighbors(phase,FrstAtom,AtNames)
786            else:
787                AtNames.remove(FrstAtom)
788        dlg.Destroy()
789        if FrstAtom == '':
790            print 'no atom selected'
791            return
792        dlg = G2G.G2MultiChoiceDialog(
793            G2frame,title2+FrstAtom,
794            'Constrain '+str(FrstAtom)+' with...',AtNames,
795            toggle=False,size=(625,400),monoFont=True)
796        if dlg.ShowModal() == wx.ID_OK:
797            Selections = dlg.GetSelections()[:]
798        else:
799            print 'no target atom selected'
800            dlg.Destroy()
801            return
802        dlg.Destroy()
803        for name in Atoms[FrstAtom]:
804            newcons = []
805            constr = []
806            if 'riding' in constType:
807                if 'AUiso' in name:
808                    constr = [[1.0,G2obj.G2VarObj(name)]]
809                elif 'AU11' in name:
810                    pass
811                elif 'AU' not in name:
812                    constr = [[1.0,G2obj.G2VarObj(name)]]
813            else:
814                constr = [[1.0,G2obj.G2VarObj(name)]]
815            pref = ':'+name.rsplit(':',1)[0].split(':',1)[1]    #get stuff between phase id & atom id
816            for sel in Selections:
817                name2 = Atoms[AtNames[sel]][0]
818                pid = name2.split(':',1)[0]                     #get phase id for 2nd atom
819                id = name2.rsplit(':',1)[-1]                    #get atom no. for 2nd atom
820                if 'riding' in constType:
821                    pref = pid+pref
822                    if 'AUiso' in pref:
823                        parts = pref.split('AUiso')
824                        constr += [[1.2,G2obj.G2VarObj('%s:%s'%(parts[0]+'AUiso',id))]]
825                    elif 'AU' not in pref:
826                        constr += [[1.0,G2obj.G2VarObj('%s:%s'%(pref,id))]]
827                else:
828                    constr += [[1.0,G2obj.G2VarObj('%s:%s'%(pid+pref,id))]]
829            if not constr:
830                continue
831            if 'frac' in pref and 'riding' not in constType:
832                newcons = [constr+[1.0,None,'c']]
833            else:
834                newcons = [constr+[None,None,'e']]
835            if len(newcons) > 0:
836                if CheckAddedConstraint(newcons):
837                    data[constrDictEnt] += newcons
838        wx.CallAfter(OnPageChanged,None)
839                       
840    def MakeConstraintsSizer(name,pageDisplay):
841        '''Creates a sizer displaying all of the constraints entered of
842        the specified type.
843
844        :param str name: the type of constraints to be displayed ('HAP',
845          'Hist', 'Phase', 'Global', 'Sym-Generated')
846        :param wx.Panel pageDisplay: parent panel for sizer
847        :returns: wx.Sizer created by method
848        '''
849        if name == 'Sym-Generated':         #show symmetry generated constraints
850            Sizer1 =  wx.BoxSizer(wx.VERTICAL)
851            Sizer1.Add(wx.StaticText(pageDisplay,wx.ID_ANY,
852                                    'Equivalences generated based on cell/space group input'))
853            Sizer1.Add((-1,5))
854            Sizer = wx.FlexGridSizer(0,2,0,0)
855            Sizer1.Add(Sizer)
856            for sym in G2mv.GetSymEquiv():
857                Sizer.Add(wx.StaticText(pageDisplay,wx.ID_ANY,'EQUIV'),
858                           0,wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT,3)
859                Sizer.Add(wx.StaticText(pageDisplay,wx.ID_ANY,sym))
860                Sizer.Add((-1,-1))
861                Sizer.Add((-1,2))
862            return Sizer1
863        constSizer = wx.FlexGridSizer(0,6,0,0)
864        maxlen = 50 # characters before wrapping a constraint
865        for Id,item in enumerate(data[name]):
866            refineflag = False
867            helptext = ""
868            eqString = ['',]
869            problemItem = False
870            for term in item[:-3]:
871                if str(term[1]) in G2mv.problemVars:
872                    problemItem = True
873            if item[-1] == 'h': # Hold on variable
874                constSizer.Add((-1,-1),0)              # blank space for edit button
875                typeString = 'FIXED'
876                var = str(item[0][1])
877                varMean = G2obj.fmtVarDescr(var)
878                eqString[-1] =  var +'   '
879                helptext = "Prevents variable:\n"+ var + " ("+ varMean + ")\nfrom being changed"
880            elif isinstance(item[-1],str): # not true on original-style (2011?) constraints
881                constEdit = wx.Button(pageDisplay,wx.ID_ANY,'Edit',style=wx.BU_EXACTFIT)
882                constEdit.Bind(wx.EVT_BUTTON,OnConstEdit)
883                constSizer.Add(constEdit,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)            # edit button
884                Indx[constEdit.GetId()] = [Id,name]
885                if item[-1] == 'f':
886                    helptext = "A new variable"
887                    if item[-3]:
888                        helptext += " named "+str(item[-3])
889                    helptext += " is created from a linear combination of the following variables:\n"
890                    for term in item[:-3]:
891                        var = str(term[1])
892                        if len(eqString[-1]) > maxlen:
893                            eqString.append(' ')
894                        m = term[0]
895                        if eqString[-1] != '':
896                            if m >= 0:
897                                eqString[-1] += ' + '
898                            else:
899                                eqString[-1] += ' - '
900                                m = abs(m)
901                        eqString[-1] += '%.3f*%s '%(m,var)
902                        varMean = G2obj.fmtVarDescr(var)
903                        helptext += "\n" + var + " ("+ varMean + ")"
904                    if '_Explain' in data:
905                        if data['_Explain'].get(item[-3]):
906                            helptext += '\n\n'
907                            helptext += data['_Explain'][item[-3]]
908                    # typeString = 'NEWVAR'
909                    # if item[-3]:
910                    #     eqString[-1] += ' = '+item[-3]
911                    # else:
912                    #     eqString[-1] += ' = New Variable'
913                    if item[-3]:
914                        typeString = item[-3] + ' = '
915                    else:
916                        typeString = 'New Variable = '
917                    #print 'refine',item[-2]
918                    refineflag = True
919                elif item[-1] == 'c':
920                    helptext = "The following variables constrained to equal a constant:"
921                    for term in item[:-3]:
922                        var = str(term[1])
923                        if len(eqString[-1]) > maxlen:
924                            eqString.append(' ')
925                        if eqString[-1] != '':
926                            if term[0] > 0:
927                                eqString[-1] += ' + '
928                            else:
929                                eqString[-1] += ' - '
930                        eqString[-1] += '%.3f*%s '%(abs(term[0]),var)
931                        varMean = G2obj.fmtVarDescr(var)
932                        helptext += "\n" + var + " ("+ varMean + ")"
933                    typeString = 'CONST'
934                    eqString[-1] += ' = '+str(item[-3])
935                elif item[-1] == 'e':
936                    helptext = "The following variables are set to be equivalent, noting multipliers:"
937                    for term in item[:-3]:
938                        var = str(term[1])
939                        if term[0] == 0: term[0] = 1.0
940                        if len(eqString[-1]) > maxlen:
941                            eqString.append(' ')
942                        if eqString[-1] == '':
943                            eqString[-1] += var+' '
944                            first = term[0]
945                        else:
946                            eqString[-1] += ' = %.3f*%s '%(first/term[0],var)
947                        varMean = G2obj.fmtVarDescr(var)
948                        helptext += "\n" + var + " ("+ varMean + ")"
949                    typeString = 'EQUIV'
950                else:
951                    print 'Unexpected constraint',item
952               
953            else:
954                print 'Removing old-style constraints'
955                data[name] = []
956                return constSizer
957            constDel = wx.Button(pageDisplay,wx.ID_ANY,'Delete',style=wx.BU_EXACTFIT)
958            constDel.Bind(wx.EVT_BUTTON,OnConstDel)
959            Indx[constDel.GetId()] = [Id,name]
960            constSizer.Add(constDel,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)             # delete button
961            if helptext:
962                ch = G2G.HelpButton(pageDisplay,helptext)
963                constSizer.Add(ch,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)
964            else:
965                constSizer.Add((-1,-1))
966            if refineflag:
967                ch = G2G.G2CheckBox(pageDisplay,'',item,-2)
968                constSizer.Add(ch,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)
969            else:
970                constSizer.Add((-1,-1))               
971            constSizer.Add(wx.StaticText(pageDisplay,wx.ID_ANY,typeString),
972                           0,wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT,3)
973            if problemItem: eqString[-1] += ' -- Conflict: see console'
974            if len(eqString) > 1:
975                Eq = wx.BoxSizer(wx.VERTICAL)
976                for s in eqString:
977                    line = wx.StaticText(pageDisplay,wx.ID_ANY,s)
978                    if problemItem: line.SetBackgroundColour(wx.YELLOW)
979                    Eq.Add(line,0,wx.ALIGN_CENTER_VERTICAL)
980                Eq.Add((-1,4))
981            else:
982                Eq = wx.StaticText(pageDisplay,wx.ID_ANY,eqString[0])
983                if problemItem: Eq.SetBackgroundColour(wx.YELLOW)
984            constSizer.Add(Eq,1,wx.ALIGN_CENTER_VERTICAL)
985        return constSizer
986               
987    def OnConstDel(event):
988        'Delete a constraint'
989        Obj = event.GetEventObject()
990        Id,name = Indx[Obj.GetId()]
991        del(data[name][Id])
992        wx.CallAfter(OnPageChanged,None)
993       
994    def OnConstEdit(event):
995        '''Called to edit an individual contraint in response to a
996        click on its Edit button
997        '''
998        Obj = event.GetEventObject()
999        Id,name = Indx[Obj.GetId()]
1000        if data[name][Id][-1] == 'f':
1001            items = data[name][Id][:-3]
1002            constType = 'New Variable'
1003            if data[name][Id][-3]:
1004                varname = data[name][Id][-3]
1005            else:
1006                varname = ""
1007            lbl = 'Enter value for each term in constraint; sum = new variable'
1008            dlg = ConstraintDialog(G2frame,constType,lbl,items,
1009                                   varname=varname,varyflag=data[name][Id][-2])
1010        elif data[name][Id][-1] == 'c':
1011            items = data[name][Id][:-3]+[
1012                [data[name][Id][-3],'fixed value =']]
1013            constType = 'Constraint'
1014            lbl = 'Edit value for each term in constant constraint sum'
1015            dlg = ConstraintDialog(G2frame,constType,lbl,items)
1016        elif data[name][Id][-1] == 'e':
1017            items = data[name][Id][:-3]
1018            constType = 'Equivalence'
1019            lbl = 'The following terms are set to be equal:'
1020            dlg = ConstraintDialog(G2frame,constType,lbl,items,'/')
1021        else:
1022            return
1023        try:
1024            prev = data[name][Id][:]
1025            if dlg.ShowModal() == wx.ID_OK:
1026                result = dlg.GetData()
1027                for i in range(len(data[name][Id][:-3])):
1028                    if type(data[name][Id][i]) is tuple: # fix non-mutable construct
1029                        data[name][Id][i] = list(data[name][Id][i])
1030                    data[name][Id][i][0] = result[i][0]
1031                if data[name][Id][-1] == 'c':
1032                    data[name][Id][-3] = str(result[-1][0])
1033                elif data[name][Id][-1] == 'f':
1034                    # process the variable name to put in global form (::var)
1035                    varname = str(dlg.newvar[0]).strip().replace(' ','_')
1036                    if varname.startswith('::'):
1037                        varname = varname[2:]
1038                    varname = varname.replace(':',';')
1039                    if varname:
1040                        data[name][Id][-3] = varname
1041                    else:
1042                        data[name][Id][-3] = ''
1043                    data[name][Id][-2] = dlg.newvar[1]
1044                if not CheckChangedConstraint():
1045                    data[name][Id] = prev
1046        except:
1047            import traceback
1048            print traceback.format_exc()
1049        finally:
1050            dlg.Destroy()
1051        wx.CallAfter(OnPageChanged,None)
1052   
1053    def UpdateConstraintPanel(panel,typ):
1054        '''Update the contents of the selected Constraint
1055        notebook tab. Called in :func:`OnPageChanged`
1056        '''
1057        if panel.GetSizer(): panel.GetSizer().Clear(True)
1058        Siz = wx.BoxSizer(wx.VERTICAL)
1059        Siz.Add((5,5),0)
1060        Siz.Add(MakeConstraintsSizer(typ,panel),1,wx.EXPAND)
1061        panel.SetSizer(Siz,True)
1062        Size = Siz.GetMinSize()
1063        Size[0] += 40
1064        Size[1] = max(Size[1],450) + 20
1065        panel.SetSize(Size)
1066        panel.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1067        panel.Show()
1068
1069    def OnPageChanged(event):
1070        '''Called when a tab is pressed or when a "select tab" menu button is
1071        used (see RaisePage), or to refresh the current tab contents (event=None)
1072        '''
1073        if event:       #page change event!
1074            page = event.GetSelection()
1075        else: # called directly, get current page
1076            page = G2frame.constr.GetSelection()
1077        #G2frame.constr.SetSize(G2frame.dataWindow.GetClientSize())    #TODO -almost right
1078        G2frame.constr.ChangeSelection(page)
1079        text = G2frame.constr.GetPageText(page)
1080        G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_EQUIVALANCEATOMS,False)
1081#        G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_ADDRIDING,False)
1082        if text == 'Histogram/Phase':
1083            G2frame.Page = [page,'hap']
1084            UpdateConstraintPanel(HAPConstr,'HAP')
1085        elif text == 'Histogram':
1086            G2frame.Page = [page,'hst']
1087            UpdateConstraintPanel(HistConstr,'Hist')
1088        elif text == 'Phase':
1089            G2frame.Page = [page,'phs']
1090            G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_EQUIVALANCEATOMS,True)
1091#            G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_ADDRIDING,True)
1092            if 'DELETED' in str(PhaseConstr):   #seems to be no other way to do this (wx bug)
1093                if GSASIIpath.GetConfigValue('debug'):
1094                    print 'wx error: PhaseConstr not cleanly deleted after Refine'
1095                return
1096            UpdateConstraintPanel(PhaseConstr,'Phase')
1097        elif text == 'Global':
1098            G2frame.Page = [page,'glb']
1099            UpdateConstraintPanel(GlobalConstr,'Global')
1100        else:
1101            G2frame.Page = [page,'sym']
1102            UpdateConstraintPanel(SymConstr,'Sym-Generated')           
1103        G2frame.dataWindow.SetDataSize()
1104
1105    def RaisePage(event):
1106        'Respond to a "select tab" menu button'
1107        try:
1108            i = (G2G.wxID_CONSPHASE,
1109                 G2G.wxID_CONSHAP,
1110                 G2G.wxID_CONSHIST,
1111                 G2G.wxID_CONSGLOBAL,
1112                 G2G.wxID_CONSSYM,
1113                ).index(event.GetId())
1114            G2frame.constr.SetSelection(i)
1115            wx.CallAfter(OnPageChanged,None)
1116        except ValueError:
1117            print('Unexpected event in RaisePage')
1118
1119    def SetStatusLine(text):
1120        G2frame.GetStatusBar().SetStatusText(text,1)                                     
1121       
1122    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.ConstraintMenu)
1123    #G2frame.SetLabel(G2frame.GetLabel().split('||')[0]+' || '+'Constraints')
1124    G2frame.SetTitle('Constraints')
1125    SetStatusLine('')
1126   
1127    G2frame.Bind(wx.EVT_MENU, OnAddConstraint, id=G2G.wxID_CONSTRAINTADD)
1128    G2frame.Bind(wx.EVT_MENU, OnAddFunction, id=G2G.wxID_FUNCTADD)
1129    G2frame.Bind(wx.EVT_MENU, OnAddEquivalence, id=G2G.wxID_EQUIVADD)
1130    G2frame.Bind(wx.EVT_MENU, OnAddHold, id=G2G.wxID_HOLDADD)
1131    G2frame.Bind(wx.EVT_MENU, OnAddAtomEquiv, id=G2G.wxID_EQUIVALANCEATOMS)
1132#    G2frame.Bind(wx.EVT_MENU, OnAddRiding, id=G2G.wxID_ADDRIDING)
1133    # tab commands
1134    for id in (G2G.wxID_CONSPHASE,
1135               G2G.wxID_CONSHAP,
1136               G2G.wxID_CONSHIST,
1137               G2G.wxID_CONSGLOBAL,
1138               G2G.wxID_CONSSYM,
1139               ):
1140        G2frame.Bind(wx.EVT_MENU, RaisePage,id=id)
1141
1142    #G2frame.constr = G2G.GSNoteBook(parent=G2frame.dataWindow,size=G2frame.dataWindow.GetClientSize())
1143    G2frame.constr = G2G.GSNoteBook(parent=G2frame.dataWindow)
1144    G2frame.dataWindow.GetSizer().Add(G2frame.constr,1,wx.ALL|wx.EXPAND)
1145    # note that order of pages is hard-coded in RaisePage
1146    PhaseConstr = wx.ScrolledWindow(G2frame.constr)
1147    G2frame.constr.AddPage(PhaseConstr,'Phase')
1148    HAPConstr = wx.ScrolledWindow(G2frame.constr)
1149    G2frame.constr.AddPage(HAPConstr,'Histogram/Phase')
1150    HistConstr = wx.ScrolledWindow(G2frame.constr)
1151    G2frame.constr.AddPage(HistConstr,'Histogram')
1152    GlobalConstr = wx.ScrolledWindow(G2frame.constr)
1153    G2frame.constr.AddPage(GlobalConstr,'Global')
1154    SymConstr = wx.ScrolledWindow(G2frame.constr)
1155    G2frame.constr.AddPage(SymConstr,'Sym-Generated')
1156    wx.CallAfter(OnPageChanged,None)
1157    G2frame.constr.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged)
1158    # validate all the constrants -- should not see any errors here normally
1159    allcons = []
1160    for key in data:
1161        if key.startswith('_'): continue
1162        allcons += data[key]
1163    if not len(allcons): return
1164    errmsg,warnmsg = CheckConstraints(allcons)
1165    if errmsg:
1166        G2frame.ErrorDialog('Constraint Error',
1167                            'Error in constraints:\n'+errmsg+'\nCheck console output for more information',
1168                            parent=G2frame)
1169        print errmsg
1170        print G2mv.VarRemapShow([],True)
1171    elif warnmsg:
1172        print 'Unexpected contraint warning:\n',warnmsg
1173
1174################################################################################
1175#### Make nuclear-magnetic phase constraints - called by OnTransform in G2phsGUI
1176################################################################################       
1177       
1178def MagConstraints(G2frame,oldPhase,newPhase,Trans,Vec,atCodes):
1179    '''Add constraints for new magnetic phase created via transformation of old
1180    nuclear one
1181    NB: A = [G11,G22,G33,2*G12,2*G13,2*G23]
1182    '''
1183   
1184    def SetUniqAj(pId,iA,Aname,SGLaue):
1185        if SGLaue in ['4/m','4/mmm'] and iA in [0,1]:
1186            parm = '%d::%s'%(pId,'A0')
1187        elif SGLaue in ['m3','m3m'] and iA in [0,1,2]:
1188            parm = '%d::%s'%(pId,'A0')
1189        elif SGLaue in ['6/m','6/mmm','3m1', '31m', '3'] and iA in [0,1,3]:
1190            parm = '%d::%s'%(pId,'A0')
1191        elif SGLaue in ['3R', '3mR']:
1192            if ia in [0,1,2]:
1193                parm = '%d::%s'%(pId,'A0')
1194            else:
1195                parm = '%d::%s'%(pId,'A3')
1196        else:
1197            parm = '%d::%s'%(pId,Aname)
1198        return parm
1199       
1200    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
1201    UseList = newPhase['Histograms']
1202    detTrans = np.abs(nl.det(Trans))
1203    invTrans = nl.inv(Trans)
1204#    print 'invTrans',invTrans
1205    nAcof = G2lat.cell2A(newPhase['General']['Cell'][1:7])
1206   
1207    opId = oldPhase['pId']
1208    npId = newPhase['pId']
1209    cx,ct,cs,cia = newPhase['General']['AtomPtrs']
1210    nAtoms = newPhase['Atoms']
1211    oSGData = oldPhase['General']['SGData']
1212    nSGData = newPhase['General']['SGData']
1213    oAcof = G2lat.cell2A(oldPhase['General']['Cell'][1:7])
1214    nAcof = G2lat.cell2A(newPhase['General']['Cell'][1:7])
1215    item = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Constraints') 
1216    constraints = G2frame.GPXtree.GetItemPyData(item)
1217#    GSASIIpath.IPyBreak()
1218    parmDict = {}
1219    varyList = []
1220    for ia,code in enumerate(atCodes):
1221        atom = nAtoms[ia]
1222        siteSym = G2spc.SytSym(atom[cx:cx+3],nSGData)[0]
1223        CSX = G2spc.GetCSxinel(siteSym)
1224        item = code.split('+')[0]
1225        iat,opr = item.split(':')
1226        Nop = abs(int(opr))%100-1
1227        if '-' in opr:
1228            Nop *= -1
1229        Opr = oldPhase['General']['SGData']['SGOps'][abs(Nop)][0]
1230        if Nop < 0:         #inversion
1231            Opr *= -1
1232        XOpr = np.inner(Opr,Trans.T)
1233        names = ['dAx','dAy','dAz']
1234        for ix,name in enumerate(names):
1235            IndpCon = [1.0,G2obj.G2VarObj('%d::%s:%s'%(opId,name,iat))]
1236            DepCons = []
1237            for iop,opval in enumerate(XOpr[ix]):
1238                if opval and CSX[0][ix]:    #-opval from defn of dAx, etc.
1239                    DepCons.append([-opval,G2obj.G2VarObj('%d::%s:%d'%(npId,names[iop],ia))])
1240            if len(DepCons) == 1:
1241                constraints['Phase'].append([IndpCon,DepCons[0],None,None,'e'])
1242            elif len(DepCons) > 1:
1243                for Dep in DepCons:
1244                    Dep[0] *= -1
1245                constraints['Phase'].append([IndpCon]+DepCons+[0.0,None,'c'])
1246        for name in ['Afrac','AUiso']:
1247            IndpCon = [1.0,G2obj.G2VarObj('%d::%s:%s'%(opId,name,iat))]
1248            DepCons = [1.0,G2obj.G2VarObj('%d::%s:%d'%(npId,name,ia))]
1249            constraints['Phase'].append([IndpCon,DepCons,None,None,'e'])
1250        #how do I do Uij's for most Trans?
1251    Anames = [['A0','A3','A4'],['A3','A1','A5'],['A4','A5','A2']]
1252    As = ['A0','A1','A2','A3','A4','A5']
1253    Aids = [[0,0,'A0',-1],[1,1,'A1',-1],[2,2,'A2',-1],[0,1,'A3',2],[0,2,'A4',1],[1,2,'A5',0]]
1254    Axes = ['a','b','c']
1255    Holds = []
1256    for iA,Aid in enumerate(Aids):
1257        parm = SetUniqAj(opId,iA,Aid[2],oSGData['SGLaue'])
1258        parmDict[parm] = oAcof[iA]
1259        varyList.append(parm)
1260        IndpCon = [1.0,G2obj.G2VarObj(parm)]
1261        DepCons = []
1262        for iat in range(3):
1263            if nSGData['SGLaue'] in ['-1','2/m']:       #set holds
1264                if (abs(nAcof[iA]) < 1.e-8) and (abs(Trans[Aid[0],Aid[1]]) < 1.e-8):
1265                    if Axes[iat] != oSGData['SGUniq'] and oSGData['SGLaue'] != nSGData['SGLaue']:
1266                        HoldObj = G2obj.G2VarObj('%d::%s'%(npId,Aid[2]))
1267                        if not HoldObj in Holds: 
1268                            constraints['Phase'].append([[0.0,HoldObj],None,None,'h'])
1269                            Holds.append(HoldObj)
1270                            continue
1271#            print iA,Aid,iat,invTrans[iat][Aid[0]],invTrans[Aid[1]][iat],Anames[Aid[0]][Aid[1]],parm
1272            if abs(invTrans[iat,Aid[1]]) > 1.e-8 and abs(nAcof[iA]) > 1.e-8:
1273                parm = SetUniqAj(npId,iA,Anames[Aid[0]][Aid[1]],nSGData['SGLaue'])
1274                parmDict[parm] = nAcof[As.index(Aid[2])]
1275                if not parm in varyList:
1276                    varyList.append(parm)
1277                DepCons.append([Trans[Aid[0],Aid[0]]*Trans[Aid[1],Aid[1]],G2obj.G2VarObj(parm)])
1278        if len(DepCons) == 1:
1279            constraints['Phase'].append([IndpCon,DepCons[0],None,None,'e'])
1280        elif len(DepCons) > 1:       
1281            for Dep in DepCons:
1282                Dep[0] *= -1
1283            constraints['Phase'].append([IndpCon]+DepCons+[0.0,None,'c'])
1284#    constDict,fixedList,ignored = G2stIO.ProcessConstraints(constraints['Phase'])
1285#    groups,parmlist = G2mv.GroupConstraints(constDict)
1286#    G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList,parmDict)
1287#    print 'old',parmDict
1288#    G2mv.Dict2Map(parmDict,varyList)
1289#    print 'new',parmDict
1290    for hId,hist in enumerate(UseList):    #HAP - seems OK
1291        ohapkey = '%d:%d:'%(opId,hId)
1292        nhapkey = '%d:%d:'%(npId,hId)
1293        IndpCon = [1.0,G2obj.G2VarObj(ohapkey+'Scale')]
1294        DepCons = [detTrans,G2obj.G2VarObj(nhapkey+'Scale')]
1295        constraints['HAP'].append([IndpCon,DepCons,None,None,'e'])
1296        for name in ['Size;i','Mustrain;i']:
1297            IndpCon = [1.0,G2obj.G2VarObj(ohapkey+name)]
1298            DepCons = [1.0,G2obj.G2VarObj(nhapkey+name)]
1299            constraints['HAP'].append([IndpCon,DepCons,None,None,'e'])
1300       
1301################################################################################
1302#### Rigid bodies
1303################################################################################
1304
1305def UpdateRigidBodies(G2frame,data):
1306    '''Called when Rigid bodies tree item is selected.
1307    Displays the rigid bodies in the data window
1308    '''
1309    if not data.get('RBIds') or not data:
1310        data.update({'Vector':{'AtInfo':{}},'Residue':{'AtInfo':{}},
1311            'RBIds':{'Vector':[],'Residue':[]}})       #empty/bad dict - fill it
1312           
1313    global resList
1314    Indx = {}
1315    resList = []
1316    plotDefaults = {'oldxy':[0.,0.],'Quaternion':[0.,0.,0.,1.],'cameraPos':30.,'viewDir':[0,0,1],}
1317
1318    G2frame.rbBook = G2G.GSNoteBook(parent=G2frame.dataWindow)
1319    G2frame.dataWindow.GetSizer().Add(G2frame.rbBook,1,wx.ALL|wx.EXPAND)
1320    VectorRB = wx.ScrolledWindow(G2frame.rbBook)
1321    VectorRBDisplay = wx.Panel(VectorRB)
1322    G2frame.rbBook.AddPage(VectorRB,'Vector rigid bodies')
1323    ResidueRB = wx.ScrolledWindow(G2frame.rbBook)
1324    ResidueRBDisplay = wx.Panel(ResidueRB)
1325    G2frame.rbBook.AddPage(ResidueRB,'Residue rigid bodies')
1326   
1327    def OnPageChanged(event):
1328        global resList
1329        resList = []
1330        if event:       #page change event!
1331            page = event.GetSelection()
1332        else:
1333            page = G2frame.rbBook.GetSelection()
1334        #G2frame.rbBook.SetSize(G2frame.dataWindow.GetClientSize())    #TODO -almost right
1335        G2frame.rbBook.ChangeSelection(page)
1336        text = G2frame.rbBook.GetPageText(page)
1337        if text == 'Vector rigid bodies':
1338            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.VectorBodyMenu)
1339            G2frame.Bind(wx.EVT_MENU, AddVectorRB, id=G2G.wxID_VECTORBODYADD)
1340            G2frame.Page = [page,'vrb']
1341            UpdateVectorRB()
1342        elif text == 'Residue rigid bodies':
1343            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RigidBodyMenu)
1344            G2frame.Bind(wx.EVT_MENU, AddResidueRB, id=G2G.wxID_RIGIDBODYADD)
1345            G2frame.Bind(wx.EVT_MENU, OnImportRigidBody, id=G2G.wxID_RIGIDBODYIMPORT)
1346            G2frame.Bind(wx.EVT_MENU, OnDefineTorsSeq, id=G2G.wxID_RESIDUETORSSEQ) #enable only if residue RBs exist?
1347            G2frame.Page = [page,'rrb']
1348            UpdateResidueRB()
1349           
1350    def getMacroFile(macName):
1351        defDir = os.path.join(os.path.split(__file__)[0],'GSASIImacros')
1352        dlg = wx.FileDialog(G2frame,message='Choose '+macName+' rigid body macro file',
1353            defaultDir=defDir,defaultFile="",wildcard="GSAS-II macro file (*.mac)|*.mac",
1354            style=wx.OPEN | wx.CHANGE_DIR)
1355        try:
1356            if dlg.ShowModal() == wx.ID_OK:
1357                macfile = dlg.GetPath()
1358                macro = open(macfile,'Ur')
1359                head = macro.readline()
1360                if macName not in head:
1361                    print head
1362                    print '**** ERROR - wrong restraint macro file selected, try again ****'
1363                    macro = []
1364            else: # cancel was pressed
1365                macro = []
1366        finally:
1367            dlg.Destroy()
1368        return macro        #advanced past 1st line
1369       
1370    def getTextFile():
1371        dlg = wx.FileDialog(G2frame,'Choose rigid body text file', '.', '',
1372            "GSAS-II text file (*.txt)|*.txt|XYZ file (*.xyz)|*.xyz|"
1373            "Sybyl mol2 file (*.mol2)|*.mol2|PDB file (*.pdb;*.ent)|*.pdb;*.ent",
1374            wx.OPEN | wx.CHANGE_DIR)
1375        try:
1376            if dlg.ShowModal() == wx.ID_OK:
1377                txtfile = dlg.GetPath()
1378                ext = os.path.splitext(txtfile)[1]
1379                text = open(txtfile,'Ur')
1380            else: # cancel was pressed
1381                ext = ''
1382                text = []
1383        finally:
1384            dlg.Destroy()
1385        if 'ent' in ext:
1386            ext = '.pdb'
1387        return text,ext.lower()
1388       
1389    def OnImportRigidBody(event):
1390        page = G2frame.rbBook.GetSelection()
1391        if 'Vector' in G2frame.rbBook.GetPageText(page):
1392            pass
1393        elif 'Residue' in G2frame.rbBook.GetPageText(page):
1394            ImportResidueRB()
1395           
1396    def AddVectorRB(event):
1397        AtInfo = data['Vector']['AtInfo']
1398        dlg = G2G.MultiIntegerDialog(G2frame,'New Rigid Body',['No. atoms','No. translations'],[1,1])
1399        if dlg.ShowModal() == wx.ID_OK:
1400            nAtoms,nTrans = dlg.GetValues()
1401            rbId = ran.randint(0,sys.maxint)
1402            vecMag = [1.0 for i in range(nTrans)]
1403            vecRef = [False for i in range(nTrans)]
1404            vecVal = [np.zeros((nAtoms,3)) for j in range(nTrans)]
1405            rbTypes = ['C' for i in range(nAtoms)]
1406            Info = G2elem.GetAtomInfo('C')
1407            AtInfo['C'] = [Info['Drad'],Info['Color']]
1408            data['Vector'][rbId] = {'RBname':'UNKRB','VectMag':vecMag,'rbXYZ':np.zeros((nAtoms,3)),
1409                'rbRef':[0,1,2,False],'VectRef':vecRef,'rbTypes':rbTypes,'rbVect':vecVal,'useCount':0}
1410            data['RBIds']['Vector'].append(rbId)
1411        dlg.Destroy()
1412        UpdateVectorRB()
1413       
1414    def AddResidueRB(event):
1415        AtInfo = data['Residue']['AtInfo']
1416        macro = getMacroFile('rigid body')
1417        if not macro:
1418            return
1419        macStr = macro.readline()
1420        while macStr:
1421            items = macStr.split()
1422            if 'I' == items[0]:
1423                rbId = ran.randint(0,sys.maxint)
1424                rbName = items[1]
1425                rbTypes = []
1426                rbXYZ = []
1427                rbSeq = []
1428                atNames = []
1429                nAtms,nSeq,nOrig,mRef,nRef = [int(items[i]) for i in [2,3,4,5,6]]
1430                for iAtm in range(nAtms):
1431                    macStr = macro.readline().split()
1432                    atName = macStr[0]
1433                    atType = macStr[1]
1434                    atNames.append(atName)
1435                    rbXYZ.append([float(macStr[i]) for i in [2,3,4]])
1436                    rbTypes.append(atType)
1437                    if atType not in AtInfo:
1438                        Info = G2elem.GetAtomInfo(atType)
1439                        AtInfo[atType] = [Info['Drad'],Info['Color']]
1440                rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[nOrig-1])
1441                for iSeq in range(nSeq):
1442                    macStr = macro.readline().split()
1443                    mSeq = int(macStr[0])
1444                    for jSeq in range(mSeq):
1445                        macStr = macro.readline().split()
1446                        iBeg = int(macStr[0])-1
1447                        iFin = int(macStr[1])-1
1448                        angle = 0.0
1449                        nMove = int(macStr[2])
1450                        iMove = [int(macStr[i])-1 for i in range(3,nMove+3)]
1451                        rbSeq.append([iBeg,iFin,angle,iMove])
1452                data['Residue'][rbId] = {'RBname':rbName,'rbXYZ':rbXYZ,'rbTypes':rbTypes,
1453                    'atNames':atNames,'rbRef':[nOrig-1,mRef-1,nRef-1,True],'rbSeq':rbSeq,
1454                    'SelSeq':[0,0],'useCount':0}
1455                data['RBIds']['Residue'].append(rbId)
1456                print 'Rigid body '+rbName+' added'
1457            macStr = macro.readline()
1458        macro.close()
1459        UpdateResidueRB()
1460       
1461    def ImportResidueRB():
1462        AtInfo = data['Residue']['AtInfo']
1463        text,ext = getTextFile()
1464        if not text:
1465            return
1466        rbId = ran.randint(0,sys.maxint)
1467        rbTypes = []
1468        rbXYZ = []
1469        atNames = []
1470        txtStr = text.readline()
1471        if 'xyz' in ext:
1472            txtStr = text.readline()
1473            txtStr = text.readline()
1474        elif 'mol2' in ext:
1475            while 'ATOM' not in txtStr:
1476                txtStr = text.readline()
1477            txtStr = text.readline()
1478        elif 'pdb' in ext:
1479            while 'ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]:
1480                txtStr = text.readline()
1481                #print txtStr
1482        items = txtStr.split()
1483        while len(items):
1484            if 'txt' in ext:
1485                atName = items[0]
1486                atType = items[1]
1487                rbXYZ.append([float(items[i]) for i in [2,3,4]])
1488            elif 'xyz' in ext:
1489                atType = items[0]
1490                rbXYZ.append([float(items[i]) for i in [1,2,3]])
1491                atName = atType+str(len(rbXYZ))
1492            elif 'mol2' in ext:
1493                atType = items[1]
1494                atName = items[1]+items[0]
1495                rbXYZ.append([float(items[i]) for i in [2,3,4]])
1496            elif 'pdb' in ext:
1497                atType = items[-1]
1498                atName = items[2]
1499                xyz = txtStr[30:55].split()                   
1500                rbXYZ.append([float(x) for x in xyz])
1501            atNames.append(atName)
1502            rbTypes.append(atType)
1503            if atType not in AtInfo:
1504                Info = G2elem.GetAtomInfo(atType)
1505                AtInfo[atType] = [Info['Drad'],Info['Color']]
1506            txtStr = text.readline()
1507            if 'mol2' in ext and 'BOND' in txtStr:
1508                break
1509            if 'pdb' in ext and ('ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]):
1510                break
1511            items = txtStr.split()
1512        if len(atNames) < 3:
1513            G2G.G2MessageBox(G2frame,'Not enough atoms in rigid body; must be 3 or more')
1514        else:
1515            rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[0])
1516            Xxyz = rbXYZ[1]
1517            X = Xxyz/np.sqrt(np.sum(Xxyz**2))
1518            Yxyz = rbXYZ[2]
1519            Y = Yxyz/np.sqrt(np.sum(Yxyz**2))
1520            Mat = G2mth.getRBTransMat(X,Y)
1521            rbXYZ = np.inner(Mat,rbXYZ).T
1522            data['Residue'][rbId] = {'RBname':'UNKRB','rbXYZ':rbXYZ,'rbTypes':rbTypes,
1523                'atNames':atNames,'rbRef':[0,1,2,False],'rbSeq':[],'SelSeq':[0,0],'useCount':0}
1524            data['RBIds']['Residue'].append(rbId)
1525            print 'Rigid body UNKRB added'
1526        text.close()
1527        UpdateResidueRB()
1528       
1529    def FindNeighbors(Orig,XYZ,atTypes,atNames,AtInfo):
1530        Radii = []
1531        for Atype in atTypes:
1532            Radii.append(AtInfo[Atype][0])
1533        Radii = np.array(Radii)
1534        Neigh = []
1535        Dx = XYZ-XYZ[Orig]
1536        dist = np.sqrt(np.sum(Dx**2,axis=1))
1537        sumR = Radii[Orig]+Radii
1538        IndB = ma.nonzero(ma.masked_greater(dist-0.85*sumR,0.))
1539        for j in IndB[0]:
1540            if j != Orig and atTypes[j] != 'H':
1541                Neigh.append(atNames[j])
1542        return Neigh
1543       
1544    def FindAllNeighbors(XYZ,atTypes,atNames,AtInfo):
1545        NeighDict = {}
1546        for iat,xyz in enumerate(atNames):
1547            NeighDict[atNames[iat]] = FindNeighbors(iat,XYZ,atTypes,atNames,AtInfo)
1548        return NeighDict
1549       
1550    def FindRiding(Orig,Pivot,NeighDict):
1551        riding = [Orig,Pivot]
1552        iAdd = 1
1553        new = True
1554        while new:
1555            newAtms = NeighDict[riding[iAdd]]
1556            for At in newAtms:
1557                new = False
1558                if At not in riding:
1559                    riding.append(At)
1560                    new = True
1561            iAdd += 1
1562            if iAdd < len(riding):
1563                new = True
1564        return riding[2:]
1565                       
1566    def OnDefineTorsSeq(event):
1567        rbKeys = data['Residue'].keys()
1568        rbKeys.remove('AtInfo')
1569        rbNames = [data['Residue'][k]['RBname'] for k in rbKeys]
1570        rbIds = dict(zip(rbNames,rbKeys))
1571        rbNames.sort()
1572        rbId = 0
1573        if len(rbNames) == 0:
1574            print 'There are no rigid bodies defined'
1575            G2frame.ErrorDialog('No rigid bodies','There are no rigid bodies defined',
1576                                parent=G2frame)
1577            return
1578        elif len(rbNames) > 1:
1579            dlg = wx.SingleChoiceDialog(G2frame,'Select rigid body for torsion sequence','Torsion sequence',rbNames)
1580            if dlg.ShowModal() == wx.ID_OK:
1581                sel = dlg.GetSelection()
1582                rbId = rbIds[rbNames[sel]]
1583                rbData = data['Residue'][rbId]
1584            dlg.Destroy()
1585        else:
1586            rbId = rbIds[rbNames[0]]
1587            rbData = data['Residue'][rbId]
1588        if not len(rbData):
1589            return
1590        atNames = rbData['atNames']
1591        AtInfo = data['Residue']['AtInfo']
1592        atTypes = rbData['rbTypes']
1593        XYZ = rbData['rbXYZ']
1594        neighDict = FindAllNeighbors(XYZ,atTypes,atNames,AtInfo)
1595        TargList = []           
1596        dlg = wx.SingleChoiceDialog(G2frame,'Select origin atom for torsion sequence','Origin atom',rbData['atNames'])
1597        if dlg.ShowModal() == wx.ID_OK:
1598            Orig = dlg.GetSelection()
1599            TargList = neighDict[atNames[Orig]]
1600        dlg.Destroy()
1601        if not len(TargList):
1602            return
1603        dlg = wx.SingleChoiceDialog(G2frame,'Select pivot atom for torsion sequence','Pivot atom',TargList)
1604        if dlg.ShowModal() == wx.ID_OK:
1605            Piv = atNames.index(TargList[dlg.GetSelection()])
1606            riding = FindRiding(atNames[Orig],atNames[Piv],neighDict)
1607            Riding = []
1608            for atm in riding:
1609                Riding.append(atNames.index(atm))
1610            rbData['rbSeq'].append([Orig,Piv,0.0,Riding])           
1611        dlg.Destroy()
1612        UpdateResidueRB()
1613
1614    def UpdateVectorRB(Scroll=0):
1615        AtInfo = data['Vector']['AtInfo']
1616        refChoice = {}
1617        if 'DELETED' in str(G2frame.GetStatusBar()):   #seems to be no other way to do this (wx bug)
1618            if GSASIIpath.GetConfigValue('debug'):
1619                print 'wx error: Rigid Body/Status not cleanly deleted after Refine'
1620            return
1621        SetStatusLine(' You may use e.g. "c60" or "s60" for a vector entry')
1622        def rbNameSizer(rbId,rbData):
1623
1624            def OnRBName(event):
1625                event.Skip()
1626                Obj = event.GetEventObject()
1627                rbData['RBname'] = Obj.GetValue()
1628               
1629            def OnDelRB(event):
1630                Obj = event.GetEventObject()
1631                rbId = Indx[Obj.GetId()]
1632                if rbId in data['Vector']:
1633                    del data['Vector'][rbId]
1634                    data['RBIds']['Vector'].remove(rbId)
1635                    rbData['useCount'] -= 1
1636                wx.CallAfter(UpdateVectorRB)
1637               
1638            def OnPlotRB(event):
1639                Obj = event.GetEventObject()
1640                Obj.SetValue(False)
1641                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,rbData,plotDefaults)
1642           
1643            nameSizer = wx.BoxSizer(wx.HORIZONTAL)
1644            nameSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Rigid body name: '),
1645                0,wx.ALIGN_CENTER_VERTICAL)
1646            RBname = wx.TextCtrl(VectorRBDisplay,-1,rbData['RBname'])
1647            Indx[RBname.GetId()] = rbId
1648            RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName)
1649            RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName)
1650            nameSizer.Add(RBname,0,wx.ALIGN_CENTER_VERTICAL)
1651            nameSizer.Add((5,0),)
1652            plotRB = wx.CheckBox(VectorRBDisplay,-1,'Plot?')
1653            Indx[plotRB.GetId()] = rbId
1654            plotRB.Bind(wx.EVT_CHECKBOX,OnPlotRB)
1655            nameSizer.Add(plotRB,0,wx.ALIGN_CENTER_VERTICAL)
1656            nameSizer.Add((5,0),)
1657            if not rbData['useCount']:
1658                delRB = wx.CheckBox(VectorRBDisplay,-1,'Delete?')
1659                Indx[delRB.GetId()] = rbId
1660                delRB.Bind(wx.EVT_CHECKBOX,OnDelRB)
1661                nameSizer.Add(delRB,0,wx.ALIGN_CENTER_VERTICAL)
1662            return nameSizer
1663           
1664        def rbRefAtmSizer(rbId,rbData):
1665           
1666            def OnRefSel(event):
1667                Obj = event.GetEventObject()
1668                iref = Indx[Obj.GetId()]
1669                sel = Obj.GetValue()
1670                rbData['rbRef'][iref] = atNames.index(sel)
1671                FillRefChoice(rbId,rbData)
1672           
1673            refAtmSizer = wx.BoxSizer(wx.HORIZONTAL)
1674            atNames = [name+str(i) for i,name in enumerate(rbData['rbTypes'])]
1675            rbRef = rbData.get('rbRef',[0,1,2,False])
1676            rbData['rbRef'] = rbRef
1677            if rbData['useCount']:
1678                refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1,
1679                    'Orientation reference atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \
1680                     atNames[rbRef[1]],atNames[rbRef[2]])),0)
1681            else:
1682                refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1,
1683                    'Orientation reference atoms A-B-C: '),0,wx.ALIGN_CENTER_VERTICAL)
1684                for i in range(3):
1685                    choices = [atNames[j] for j in refChoice[rbId][i]]
1686                    refSel = wx.ComboBox(VectorRBDisplay,-1,value='',
1687                        choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN)
1688                    refSel.SetValue(atNames[rbRef[i]])
1689                    refSel.Bind(wx.EVT_COMBOBOX, OnRefSel)
1690                    Indx[refSel.GetId()] = i
1691                    refAtmSizer.Add(refSel,0,wx.ALIGN_CENTER_VERTICAL)
1692            return refAtmSizer
1693                       
1694        def rbVectMag(rbId,imag,rbData):
1695           
1696            def OnRBVectorMag(event):
1697                event.Skip()
1698                Obj = event.GetEventObject()
1699                rbId,imag = Indx[Obj.GetId()]
1700                try:
1701                    val = float(Obj.GetValue())
1702                    if val <= 0.:
1703                        raise ValueError
1704                    rbData['VectMag'][imag] = val
1705                except ValueError:
1706                    pass
1707                Obj.SetValue('%8.4f'%(val))
1708                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1709                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbId],plotDefaults)
1710               
1711            def OnRBVectorRef(event):
1712                Obj = event.GetEventObject()
1713                rbId,imag = Indx[Obj.GetId()]
1714                rbData['VectRef'][imag] = Obj.GetValue()
1715                       
1716            magSizer = wx.wx.BoxSizer(wx.HORIZONTAL)
1717            magSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Translation magnitude: '),
1718                0,wx.ALIGN_CENTER_VERTICAL)
1719            magValue = wx.TextCtrl(VectorRBDisplay,-1,'%8.4f'%(rbData['VectMag'][imag]))
1720            Indx[magValue.GetId()] = [rbId,imag]
1721            magValue.Bind(wx.EVT_TEXT_ENTER,OnRBVectorMag)
1722            magValue.Bind(wx.EVT_KILL_FOCUS,OnRBVectorMag)
1723            magSizer.Add(magValue,0,wx.ALIGN_CENTER_VERTICAL)
1724            magSizer.Add((5,0),)
1725            magref = wx.CheckBox(VectorRBDisplay,-1,label=' Refine?') 
1726            magref.SetValue(rbData['VectRef'][imag])
1727            magref.Bind(wx.EVT_CHECKBOX,OnRBVectorRef)
1728            Indx[magref.GetId()] = [rbId,imag]
1729            magSizer.Add(magref,0,wx.ALIGN_CENTER_VERTICAL)
1730            return magSizer
1731           
1732        def rbVectors(rbId,imag,mag,XYZ,rbData):
1733
1734            def TypeSelect(event):
1735                AtInfo = data['Vector']['AtInfo']
1736                r,c = event.GetRow(),event.GetCol()
1737                if vecGrid.GetColLabelValue(c) == 'Type':
1738                    PE = G2elemGUI.PickElement(G2frame,oneOnly=True)
1739                    if PE.ShowModal() == wx.ID_OK:
1740                        if PE.Elem != 'None':
1741                            El = PE.Elem.strip().lower().capitalize()
1742                            if El not in AtInfo:
1743                                Info = G2elem.GetAtomInfo(El)
1744                                AtInfo[El] = [Info['Drad'],Info['Color']]
1745                            rbData['rbTypes'][r] = El
1746                            vecGrid.SetCellValue(r,c,El)
1747                    PE.Destroy()
1748                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1749
1750            def ChangeCell(event):
1751                r,c =  event.GetRow(),event.GetCol()
1752                if r >= 0 and (0 <= c < 3):
1753                    try:
1754                        val = float(vecGrid.GetCellValue(r,c))
1755                        rbData['rbVect'][imag][r][c] = val
1756                    except ValueError:
1757                        pass
1758                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbId],plotDefaults)
1759                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1760
1761            vecSizer = wx.BoxSizer()
1762            Types = 3*[wg.GRID_VALUE_FLOAT+':10,5',]+[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',]
1763            colLabels = ['Vector x','Vector y','Vector z','Type','Cart x','Cart y','Cart z']
1764            table = []
1765            rowLabels = []
1766            for ivec,xyz in enumerate(rbData['rbVect'][imag]):
1767                table.append(list(xyz)+[rbData['rbTypes'][ivec],]+list(XYZ[ivec]))
1768                rowLabels.append(str(ivec))
1769            vecTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1770            vecGrid = G2G.GSGrid(VectorRBDisplay)
1771            vecGrid.SetTable(vecTable, True)
1772            vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell)
1773            if not imag:
1774                vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect)
1775            attr = wx.grid.GridCellAttr()
1776            attr.SetEditor(G2G.GridFractionEditor(vecGrid))
1777            for c in range(3):
1778                vecGrid.SetColAttr(c, attr)
1779            for row in range(vecTable.GetNumberRows()):
1780                if imag:
1781                    vecGrid.SetCellStyle(row,3,VERY_LIGHT_GREY,True)                   
1782                for col in [4,5,6]:
1783                    vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True)
1784#            vecGrid.SetScrollRate(0,0)
1785            vecGrid.AutoSizeColumns(False)
1786            vecSizer.Add(vecGrid)
1787            return vecSizer
1788       
1789        def FillRefChoice(rbId,rbData):
1790            choiceIds = [i for i in range(len(rbData['rbTypes']))]
1791           
1792            rbRef = rbData.get('rbRef',[-1,-1,-1,False])
1793            for i in range(3):
1794                choiceIds.remove(rbRef[i])
1795            refChoice[rbId] = [choiceIds[:],choiceIds[:],choiceIds[:]]
1796            for i in range(3):
1797                refChoice[rbId][i].append(rbRef[i])
1798                refChoice[rbId][i].sort()     
1799           
1800        if VectorRB.GetSizer(): VectorRB.GetSizer().Clear(True)
1801        VectorRBSizer = wx.BoxSizer(wx.VERTICAL)
1802        for rbId in data['RBIds']['Vector']:
1803            if rbId != 'AtInfo':
1804                rbData = data['Vector'][rbId]
1805                FillRefChoice(rbId,rbData)
1806                VectorRBSizer.Add(rbNameSizer(rbId,rbData),0)
1807                VectorRBSizer.Add(rbRefAtmSizer(rbId,rbData),0)
1808                XYZ = np.array([[0.,0.,0.] for Ty in rbData['rbTypes']])
1809                for imag,mag in enumerate(rbData['VectMag']):
1810                    XYZ += mag*rbData['rbVect'][imag]
1811                    VectorRBSizer.Add(rbVectMag(rbId,imag,rbData),0)
1812                    VectorRBSizer.Add(rbVectors(rbId,imag,mag,XYZ,rbData),0)
1813                VectorRBSizer.Add((5,5),0)
1814                data['Vector'][rbId]['rbXYZ'] = XYZ       
1815        VectorRBSizer.Layout()   
1816        VectorRBDisplay.SetSizer(VectorRBSizer,True)
1817        Size = VectorRBSizer.GetMinSize()
1818        Size[0] += 40
1819        Size[1] = max(Size[1],450) + 20
1820        VectorRBDisplay.SetSize(Size)
1821        VectorRB.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1822        VectorRB.Scroll(0,Scroll)
1823       
1824    def UpdateResidueRB():
1825        AtInfo = data['Residue']['AtInfo']
1826        refChoice = {}
1827        RefObjs = []
1828
1829        def rbNameSizer(rbId,rbData):
1830
1831            def OnRBName(event):
1832                Obj = event.GetEventObject()
1833                rbData['RBname'] = Obj.GetValue()
1834               
1835            def OnDelRB(event):
1836                Obj = event.GetEventObject()
1837                rbId = Indx[Obj.GetId()]
1838                if rbId in data['Residue']: 
1839                    del data['Residue'][rbId]
1840                    data['RBIds']['Residue'].remove(rbId)
1841                wx.CallAfter(UpdateResidueRB)
1842               
1843            def OnStripH(event):
1844                Obj = event.GetEventObject()
1845                rbId = Indx[Obj.GetId()]
1846                if rbId in data['Residue']:
1847                    newNames = []
1848                    newTypes = []
1849                    newXYZ = []
1850                    for i,atype in enumerate(rbData['rbTypes']):
1851                        if atype != 'H':
1852                            newNames.append(rbData['atNames'][i])
1853                            newTypes.append(rbData['rbTypes'][i])
1854                            newXYZ.append(rbData['rbXYZ'][i])
1855                    rbData['atNames'] = newNames
1856                    rbData['rbTypes'] = newTypes
1857                    rbData['rbXYZ'] = newXYZ
1858                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1859                wx.CallAfter(UpdateResidueRB)
1860                   
1861            def OnPlotRB(event):
1862                Obj = event.GetEventObject()
1863                Obj.SetValue(False)
1864                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1865           
1866            nameSizer = wx.BoxSizer(wx.HORIZONTAL)
1867            nameSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Residue name: '),
1868                0,wx.ALIGN_CENTER_VERTICAL)
1869            RBname = wx.TextCtrl(ResidueRBDisplay,-1,rbData['RBname'])
1870            Indx[RBname.GetId()] = rbId
1871            RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName)
1872            RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName)
1873            nameSizer.Add(RBname,0,wx.ALIGN_CENTER_VERTICAL)
1874            nameSizer.Add((5,0),)
1875            plotRB = wx.CheckBox(ResidueRBDisplay,-1,'Plot?')
1876            Indx[plotRB.GetId()] = rbId
1877            plotRB.Bind(wx.EVT_CHECKBOX,OnPlotRB)
1878            nameSizer.Add(plotRB,0,wx.ALIGN_CENTER_VERTICAL)
1879            nameSizer.Add((5,0),)
1880            if not rbData['useCount']:
1881                delRB = wx.CheckBox(ResidueRBDisplay,-1,'Delete?')
1882                Indx[delRB.GetId()] = rbId
1883                delRB.Bind(wx.EVT_CHECKBOX,OnDelRB)
1884                nameSizer.Add(delRB,0,wx.ALIGN_CENTER_VERTICAL)
1885                if 'H'  in rbData['rbTypes']:
1886                    stripH = wx.CheckBox(ResidueRBDisplay,-1,'Strip H-atoms?')
1887                    Indx[stripH.GetId()] = rbId
1888                    stripH.Bind(wx.EVT_CHECKBOX,OnStripH)
1889                    nameSizer.Add(stripH,0,wx.ALIGN_CENTER_VERTICAL)
1890            return nameSizer
1891           
1892        def rbResidues(rbId,rbData):
1893           
1894            def TypeSelect(event):
1895                AtInfo = data['Residue']['AtInfo']
1896                r,c = event.GetRow(),event.GetCol()
1897                if vecGrid.GetColLabelValue(c) == 'Type':
1898                    PE = G2elemGUI.PickElement(G2frame,oneOnly=True)
1899                    if PE.ShowModal() == wx.ID_OK:
1900                        if PE.Elem != 'None':
1901                            El = PE.Elem.strip().lower().capitalize()
1902                            if El not in AtInfo:
1903                                Info = G2elem.GetAtomInfo(El)
1904                                AtInfo[El] = [Info['Drad']['Color']]
1905                            rbData['rbTypes'][r] = El
1906                            vecGrid.SetCellValue(r,c,El)
1907                    PE.Destroy()
1908
1909            def ChangeCell(event):
1910                r,c =  event.GetRow(),event.GetCol()
1911                if r >= 0 and (0 <= c < 3):
1912                    try:
1913                        val = float(vecGrid.GetCellValue(r,c))
1914                        rbData['rbXYZ'][r][c] = val
1915                    except ValueError:
1916                        pass
1917                       
1918            def RowSelect(event):
1919                r,c =  event.GetRow(),event.GetCol()
1920                if c < 0:                   #only row clicks
1921                    for vecgrid in resList:
1922                        vecgrid.ClearSelection()
1923                    vecGrid.SelectRow(r,True)
1924
1925            def OnRefSel(event):
1926               
1927                Obj = event.GetEventObject()
1928                iref,res,jref = Indx[Obj.GetId()]
1929                sel = Obj.GetValue()
1930                ind = atNames.index(sel)
1931                if rbData['rbTypes'][ind] == 'H':
1932                    G2G.G2MessageBox(G2frame,'You should not select an H-atom for rigid body orientation')
1933                rbData['rbRef'][iref] = ind
1934                FillRefChoice(rbId,rbData)
1935                for i,ref in enumerate(RefObjs[jref]):
1936                    ref.SetItems([atNames[j] for j in refChoice[rbId][i]])
1937                    ref.SetValue(atNames[rbData['rbRef'][i]])                   
1938                rbXYZ = rbData['rbXYZ']
1939                if not iref:     #origin change
1940                    rbXYZ -= rbXYZ[ind]
1941                #TODO - transform all atom XYZ by axis choices
1942                Xxyz = rbXYZ[rbData['rbRef'][1]]
1943                X = Xxyz/np.sqrt(np.sum(Xxyz**2))
1944                Yxyz = rbXYZ[rbData['rbRef'][2]]
1945                Y = Yxyz/np.sqrt(np.sum(Yxyz**2))
1946                Mat = G2mth.getRBTransMat(X,Y)
1947                rbXYZ = np.inner(Mat,rbXYZ).T
1948                rbData['rbXYZ'] = rbXYZ
1949                res.ClearSelection()
1950                resTable = res.GetTable()
1951                for r in range(res.GetNumberRows()):
1952                    row = resTable.GetRowValues(r)
1953                    row[2:4] = rbXYZ[r]
1954                    resTable.SetRowValues(r,row)
1955                res.ForceRefresh()
1956                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1957               
1958            Types = 2*[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',]
1959            colLabels = ['Name','Type','Cart x','Cart y','Cart z']
1960            table = []
1961            rowLabels = []
1962            for ivec,xyz in enumerate(rbData['rbXYZ']):
1963                table.append([rbData['atNames'][ivec],]+[rbData['rbTypes'][ivec],]+list(xyz))
1964                rowLabels.append(str(ivec))
1965            vecTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1966            vecGrid = G2G.GSGrid(ResidueRBDisplay)
1967            Indx[vecGrid.GetId()] = rbId
1968            resList.append(vecGrid)
1969            vecGrid.SetTable(vecTable, True)
1970            vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell)
1971            vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect)
1972            vecGrid.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK, RowSelect)
1973            attr = wx.grid.GridCellAttr()
1974            attr.SetEditor(G2G.GridFractionEditor(vecGrid))
1975            for c in range(3):
1976                vecGrid.SetColAttr(c, attr)
1977            for row in range(vecTable.GetNumberRows()):
1978                for col in range(5):
1979                    vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True)
1980#            vecGrid.SetScrollRate(0,0)
1981            vecGrid.AutoSizeColumns(False)
1982            vecSizer = wx.BoxSizer()
1983            vecSizer.Add(vecGrid)
1984           
1985            refAtmSizer = wx.BoxSizer(wx.HORIZONTAL)
1986            atNames = rbData['atNames']
1987            rbRef = rbData['rbRef']
1988            if rbData['rbRef'][3] or rbData['useCount']:
1989                refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
1990                    'Orientation reference non-H atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \
1991                     atNames[rbRef[1]],atNames[rbRef[2]])),0)
1992            else:
1993                refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
1994                    'Orientation reference non-H atoms A-B-C: '),0,wx.ALIGN_CENTER_VERTICAL)
1995                refObj = [0,0,0]
1996                for i in range(3):
1997                    choices = [atNames[j] for j in refChoice[rbId][i]]
1998                    refSel = wx.ComboBox(ResidueRBDisplay,-1,value='',
1999                        choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN)
2000                    refSel.SetValue(atNames[rbRef[i]])
2001                    refSel.Bind(wx.EVT_COMBOBOX, OnRefSel)
2002                    Indx[refSel.GetId()] = [i,vecGrid,len(RefObjs)]
2003                    refObj[i] = refSel
2004                    refAtmSizer.Add(refSel,0,wx.ALIGN_CENTER_VERTICAL)
2005                RefObjs.append(refObj)
2006           
2007            mainSizer = wx.BoxSizer(wx.VERTICAL)
2008            mainSizer.Add(refAtmSizer)
2009            mainSizer.Add(vecSizer)
2010            return mainSizer
2011           
2012        def SeqSizer(angSlide,rbId,iSeq,Seq,atNames):
2013           
2014            def ChangeAngle(event):
2015                event.Skip()
2016                Obj = event.GetEventObject()
2017                rbId,Seq = Indx[Obj.GetId()][:2]
2018                val = Seq[2]
2019                try:
2020                    val = float(Obj.GetValue())
2021                    Seq[2] = val
2022                except ValueError:
2023                    pass
2024                Obj.SetValue('%8.2f'%(val))
2025                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,data['Residue'][rbId],plotDefaults)
2026               
2027            def OnRadBtn(event):
2028                Obj = event.GetEventObject()
2029                Seq,iSeq,angId = Indx[Obj.GetId()]
2030                data['Residue'][rbId]['SelSeq'] = [iSeq,angId]
2031                angSlide.SetValue(int(100*Seq[2]))
2032               
2033            def OnDelBtn(event):
2034                Obj = event.GetEventObject()
2035                rbId,Seq = Indx[Obj.GetId()]
2036                data['Residue'][rbId]['rbSeq'].remove(Seq)       
2037                wx.CallAfter(UpdateResidueRB)
2038           
2039            seqSizer = wx.FlexGridSizer(0,5,2,2)
2040            seqSizer.AddGrowableCol(3,0)
2041            iBeg,iFin,angle,iMove = Seq
2042            ang = wx.TextCtrl(ResidueRBDisplay,-1,'%8.2f'%(angle),size=(50,20))
2043            if not iSeq:
2044                radBt = wx.RadioButton(ResidueRBDisplay,-1,'',style=wx.RB_GROUP)
2045                data['Residue'][rbId]['SelSeq'] = [iSeq,ang.GetId()]
2046            else:
2047                radBt = wx.RadioButton(ResidueRBDisplay,-1,'')
2048            radBt.Bind(wx.EVT_RADIOBUTTON,OnRadBtn)                   
2049            seqSizer.Add(radBt)
2050            delBt = wx.RadioButton(ResidueRBDisplay,-1,'')
2051            delBt.Bind(wx.EVT_RADIOBUTTON,OnDelBtn)
2052            seqSizer.Add(delBt)
2053            bond = wx.TextCtrl(ResidueRBDisplay,-1,'%s %s'%(atNames[iBeg],atNames[iFin]),size=(50,20))
2054            seqSizer.Add(bond,0,wx.ALIGN_CENTER_VERTICAL)
2055            Indx[radBt.GetId()] = [Seq,iSeq,ang.GetId()]
2056            Indx[delBt.GetId()] = [rbId,Seq]
2057            Indx[ang.GetId()] = [rbId,Seq,ang]
2058            ang.Bind(wx.EVT_TEXT_ENTER,ChangeAngle)
2059            ang.Bind(wx.EVT_KILL_FOCUS,ChangeAngle)
2060            seqSizer.Add(ang,0,wx.ALIGN_CENTER_VERTICAL)
2061            atms = ''
2062            for i in iMove:   
2063                atms += ' %s,'%(atNames[i])
2064            moves = wx.TextCtrl(ResidueRBDisplay,-1,atms[:-1],size=(200,20))
2065            seqSizer.Add(moves,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.RIGHT)
2066            return seqSizer
2067           
2068        def SlideSizer():
2069           
2070            def OnSlider(event):
2071                Obj = event.GetEventObject()
2072                rbData = Indx[Obj.GetId()]
2073                iSeq,angId = rbData['SelSeq']
2074                val = float(Obj.GetValue())/100.
2075                rbData['rbSeq'][iSeq][2] = val
2076                Indx[angId][2].SetValue('%8.2f'%(val))
2077                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
2078           
2079            slideSizer = wx.BoxSizer(wx.HORIZONTAL)
2080            slideSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Selected torsion angle:'),0)
2081            iSeq,angId = rbData['SelSeq']
2082            angSlide = wx.Slider(ResidueRBDisplay,-1,
2083                int(100*rbData['rbSeq'][iSeq][2]),0,36000,size=(200,20),
2084                style=wx.SL_HORIZONTAL)
2085            angSlide.Bind(wx.EVT_SLIDER, OnSlider)
2086            Indx[angSlide.GetId()] = rbData
2087            slideSizer.Add(angSlide,0)           
2088            return slideSizer,angSlide
2089           
2090        def FillRefChoice(rbId,rbData):
2091            choiceIds = [i for i in range(len(rbData['atNames']))]
2092            for seq in rbData['rbSeq']:
2093                for i in seq[3]:
2094                    try:
2095                        choiceIds.remove(i)
2096                    except ValueError:
2097                        pass
2098            rbRef = rbData['rbRef']
2099            for i in range(3):
2100                try:
2101                    choiceIds.remove(rbRef[i])
2102                except ValueError:
2103                    pass
2104            refChoice[rbId] = [choiceIds[:],choiceIds[:],choiceIds[:]]
2105            for i in range(3):
2106                refChoice[rbId][i].append(rbRef[i])
2107                refChoice[rbId][i].sort()     
2108           
2109        ResidueRBDisplay.DestroyChildren()
2110        ResidueRBSizer = wx.BoxSizer(wx.VERTICAL)
2111        for rbId in data['RBIds']['Residue']:
2112            rbData = data['Residue'][rbId]
2113            if len(rbData['rbXYZ']) < 3:    #patch - skip around bad RBs with too few atoms
2114                continue
2115            FillRefChoice(rbId,rbData)
2116            ResidueRBSizer.Add(rbNameSizer(rbId,rbData),0)
2117            ResidueRBSizer.Add(rbResidues(rbId,rbData),0)
2118            ResidueRBSizer.Add((5,5),0)
2119            if rbData['rbSeq']:
2120                slideSizer,angSlide = SlideSizer()
2121            if len(rbData['rbSeq']):
2122                ResidueRBSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
2123                    'Sel  Del  Bond             Angle      Riding atoms'),
2124                    0,wx.ALIGN_CENTER_VERTICAL)                       
2125            for iSeq,Seq in enumerate(rbData['rbSeq']):
2126                ResidueRBSizer.Add(SeqSizer(angSlide,rbId,iSeq,Seq,rbData['atNames']))
2127            if rbData['rbSeq']:
2128                ResidueRBSizer.Add(slideSizer,)
2129            ResidueRBSizer.Add(wx.StaticText(ResidueRBDisplay,-1,70*'-'))
2130
2131        ResidueRBSizer.Add((5,25),)
2132        ResidueRBSizer.Layout()   
2133        ResidueRBDisplay.SetSizer(ResidueRBSizer,True)
2134        Size = ResidueRBSizer.GetMinSize()
2135        Size[0] += 40
2136        Size[1] = max(Size[1],450) + 20
2137        ResidueRBDisplay.SetSize(Size)
2138        ResidueRB.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
2139       
2140    def SetStatusLine(text):
2141        G2frame.GetStatusBar().SetStatusText(text,1)                                     
2142
2143    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RigidBodyMenu)
2144    G2frame.SetTitle('Rigid bodies')
2145    SetStatusLine('')
2146    UpdateVectorRB()
2147    G2frame.rbBook.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged)
2148    wx.CallAfter(OnPageChanged,None)
Note: See TracBrowser for help on using the repository browser.