source: trunk/GSASIIrestrGUI.py @ 3682

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

eliminate auto refresh og General after looking at spin ops
implement setting bond & angle restraints from CCDC MOGUL csv file

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 94.9 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIrestr - restraint GUI routines
3########### SVN repository information ###################
4# $Date: 2018-10-18 20:01:22 +0000 (Thu, 18 Oct 2018) $
5# $Author: vondreele $
6# $Revision: 3682 $
7# $URL: trunk/GSASIIrestrGUI.py $
8# $Id: GSASIIrestrGUI.py 3682 2018-10-18 20:01:22Z vondreele $
9########### SVN repository information ###################
10'''
11*GSASIIrestrGUI: Restraint GUI routines*
12----------------------------------------
13
14Used to define restraints.
15
16'''
17from __future__ import division, print_function
18import wx
19import wx.grid as wg
20import numpy as np
21import numpy.ma as ma
22import os.path
23import GSASIIpath
24GSASIIpath.SetVersionNumber("$Revision: 3682 $")
25import GSASIImath as G2mth
26import GSASIIlattice as G2lat
27import GSASIIspc as G2spc
28import GSASIIdataGUI as G2gd
29import GSASIIplot as G2plt
30import GSASIIdata as G2data
31import GSASIIctrlGUI as G2G
32import GSASIIphsGUI as G2phsGUI
33
34WACV = wx.ALIGN_CENTER_VERTICAL
35VERY_LIGHT_GREY = wx.Colour(235,235,235)
36TabSelectionIdDict = {}
37
38################################################################################
39#####  Restraints
40################################################################################           
41def GetSelectedRows(widget):
42    '''Returns a list of selected rows. Rows can be selected, blocks of cells
43    or individual cells can be selected. The column for selected cells is ignored.
44    '''
45    rows = widget.GetSelectedRows()
46    if not rows:
47        top = widget.GetSelectionBlockTopLeft()
48        bot = widget.GetSelectionBlockBottomRight()
49        if top and bot:
50            rows = range(top[0][0],bot[0][0]+1)
51    if not rows:
52        rows = sorted(list(set([cell[0] for cell in widget.GetSelectedCells()])))
53    return rows
54       
55def UpdateRestraints(G2frame,data,phaseName):
56    '''Respond to selection of the Restraints item on the
57    data tree
58    '''
59#    global Pages
60   
61    def getMacroFile(macName):
62        defDir = os.path.join(os.path.split(__file__)[0],'GSASIImacros')
63        dlg = wx.FileDialog(G2frame,message='Choose '+macName+' restraint macro file',
64            defaultDir=defDir,defaultFile="",wildcard="GSAS-II macro file (*.mac)|*.mac",
65            style=wx.FD_OPEN | wx.FD_CHANGE_DIR)
66        try:
67            macro = ''
68            if dlg.ShowModal() == wx.ID_OK:
69                macfile = dlg.GetPath()
70                macro = open(macfile,'Ur')
71                head = macro.readline()
72                if macName not in head:
73                    print (head)
74                    print ('**** ERROR - wrong restraint macro file selected, try again ****')
75                    macro = []
76        finally:
77            dlg.Destroy()
78        return macro        #advanced past 1st line
79       
80    def getMOGULFile():
81        dlg = wx.FileDialog(G2frame,message='Choose MOGUL csv file',
82            defaultDir='.',defaultFile="",wildcard="MOGUL csv file (*.csv)|*.csv",
83            style=wx.FD_OPEN | wx.FD_CHANGE_DIR)
84        try:
85            mogul = ''
86            if dlg.ShowModal() == wx.ID_OK:
87                csvfile = dlg.GetPath()
88                mogul = open(csvfile,'Ur')
89                head = mogul.readline()
90                if 'Type' not in head:
91                    print (head)
92                    print ('**** ERROR - not a MOGUL csv file selected, try again ****')
93                    mogul = []
94        finally:
95            dlg.Destroy()
96        return mogul        #advanced past 1st line
97       
98    def OnPlotAARestraint(event):
99        page = G2frame.restrBook.GetSelection()
100        if 'Torsion' in G2frame.restrBook.GetPageText(page):
101            torNames = []
102            torNames += list(restrData['Torsion']['Coeff'].keys())
103            dlg = wx.SingleChoiceDialog(G2frame,'Select','Torsion data',torNames)
104            try:
105                if dlg.ShowModal() == wx.ID_OK:
106                    torName = torNames[dlg.GetSelection()]
107                    torsion = G2data.torsionDist[torName]
108                    torCoeff = restrData['Torsion']['Coeff'][torName]
109                    torList = restrData['Torsion']['Torsions']
110                    Names = []
111                    Angles = []
112                    for i,[indx,ops,cofName,esd] in enumerate(torList):
113                        if cofName == torName:
114                            atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
115                            name = '('+atoms[2][1]+atoms[2][0].strip()+atoms[2][2]+')'
116                            for atom in atoms:
117                                name += '  '+atom[3]
118                            XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
119                            angle = G2mth.getRestTorsion(XYZ,Amat)
120                            Angles.append(angle)
121                            Names.append(name) 
122                    G2plt.PlotTorsion(G2frame,phaseName,torsion,torName,Names,np.array(Angles),torCoeff)
123            finally:
124                dlg.Destroy()
125           
126        elif 'Rama' in G2frame.restrBook.GetPageText(page):
127            ramaNames = ['All',]
128            ramaNames += list(restrData['Rama']['Coeff'].keys())
129            dlg = wx.SingleChoiceDialog(G2frame,'Select','Ramachandran data',ramaNames)
130            try:
131                if dlg.ShowModal() == wx.ID_OK:
132                    ramaName = ramaNames[dlg.GetSelection()]
133                    rama = G2data.ramachandranDist[ramaName]
134                    ramaCoeff = []
135                    if ramaName != 'All':
136                        ramaCoeff = restrData['Rama']['Coeff'][ramaName]
137                    ramaList = restrData['Rama']['Ramas']
138                    Names = []
139                    PhiPsi = []
140                    for i,[indx,ops,cofName,esd] in enumerate(ramaList):
141                        if cofName == ramaName or (ramaName == 'All' and '-1' in cofName):
142                            atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
143                            name = '('+atoms[3][1]+atoms[3][0].strip()+atoms[3][2]+')'
144                            for atom in atoms:
145                                name += '  '+atom[3]
146                            XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
147                            phi,psi = G2mth.getRestRama(XYZ,Amat)
148                            PhiPsi.append([phi,psi])
149                            Names.append(name) 
150                    G2plt.PlotRama(G2frame,phaseName,rama,ramaName,Names,np.array(PhiPsi),ramaCoeff)
151            finally:
152                dlg.Destroy()
153           
154    def OnAddRestraint(event):
155        page = G2frame.restrBook.GetSelection()
156        if 'Bond' in G2frame.restrBook.GetPageText(page):
157            AddBondRestraint(restrData['Bond'])
158        elif 'Angle' in G2frame.restrBook.GetPageText(page):
159            AddAngleRestraint(restrData['Angle'])
160        elif 'Plane' in G2frame.restrBook.GetPageText(page):
161            AddPlaneRestraint(restrData['Plane'])
162        elif 'Chem' in G2frame.restrBook.GetPageText(page):
163            AddChemCompRestraint(restrData['ChemComp'])
164        elif 'Texture' in G2frame.restrBook.GetPageText(page):
165            AddTextureRestraint(restrData['Texture'])
166           
167    def OnAddAARestraint(event):
168        page = G2frame.restrBook.GetSelection()
169        if 'Bond' in G2frame.restrBook.GetPageText(page):
170            AddAABondRestraint(restrData['Bond'])
171        elif 'Angle' in G2frame.restrBook.GetPageText(page):
172            AddAAAngleRestraint(restrData['Angle'])
173        elif 'Plane' in G2frame.restrBook.GetPageText(page):
174            AddAAPlaneRestraint(restrData['Plane'])
175        elif 'Chiral' in G2frame.restrBook.GetPageText(page):
176            AddAAChiralRestraint(restrData['Chiral'])
177        elif 'Torsion' in G2frame.restrBook.GetPageText(page):
178            AddAATorsionRestraint(restrData['Torsion'])
179        elif 'Rama' in G2frame.restrBook.GetPageText(page):
180            AddAARamaRestraint(restrData['Rama'])
181           
182    def OnUseMogul(event):
183        page = G2frame.restrBook.GetSelection()
184        if 'Bond' in G2frame.restrBook.GetPageText(page):
185            AddMogulBondRestraint(restrData['Bond'])
186        elif 'Angle' in G2frame.restrBook.GetPageText(page):
187            AddMogulAngleRestraint(restrData['Angle'])
188           
189    def makeChains(Names,Ids):
190        Chains = {}
191        atoms = zip(Names,Ids)
192        for name,Id in atoms:
193            items = name.split(' ',2)
194            rnum,res = items[0].split(':')
195            rnum = int(rnum)
196            if items[1] not in Chains:
197                Residues = {}
198                Chains[items[1]] = Residues
199            if rnum not in Residues:
200                Residues[rnum] = [[],[]]
201            if items[2][3] in [' ','A']:
202                Residues[rnum][0].append([res,items[2],Id])
203            if items[2][3] in [' ','B']:
204                Residues[rnum][1].append([res,items[2],Id])
205        return Chains
206       
207    def AddBondRestraint(bondRestData):
208        Lists = {'origin':[],'target':[]}
209        for listName in ['origin','target']:
210            dlg = wx.MultiChoiceDialog(G2frame,'Bond restraint '+listName+' for '+General['Name'],
211                    'Select bond restraint '+listName+' atoms',Names)
212            if dlg.ShowModal() == wx.ID_OK:
213                sel = dlg.GetSelections()
214                for x in sel:
215                    if 'all' in Names[x]:
216                        allType = Types[x]
217                        for name,Type,coords,Id in zip(Names,Types,Coords,Ids):
218                            if Type == allType and 'all' not in name:
219                                Lists[listName].append([Id,Type,coords])
220                    else:
221                        Lists[listName].append([Ids[x],Types[x],Coords[x],])
222            else:
223                break
224        if len(Lists['origin']) and len(Lists['target']):
225            bond = 1.54
226            dlg = G2G.SingleFloatDialog(G2frame,'Distance','Enter restraint distance for bond',bond,[0.01,4.],'%.4f')
227            if dlg.ShowModal() == wx.ID_OK:
228                bond = dlg.GetValue()
229            dlg.Destroy()
230        Factor = bondRestData['Range']
231        indices = (-2,-1,0,1,2)
232        Units = np.array([[h,k,l] for h in indices for k in indices for l in indices])
233        origAtoms = Lists['origin']
234        targAtoms = Lists['target']
235        dlg = wx.ProgressDialog("Generating bond restraints","Processed origin atoms",len(origAtoms), 
236            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_REMAINING_TIME)
237        try:
238            Norig = 0
239            for Oid,Otype,Ocoord in origAtoms:
240                Norig += 1
241                dlg.Update(Norig)
242                for Tid,Ttype,Tcoord in targAtoms:
243                    if 'macro' in General['Type']:
244                        result = [[Tcoord,1,[0,0,0]],]
245                    else:
246                        result = G2spc.GenAtom(Tcoord,SGData,False,Move=False)
247                    for Txyz,Top,Tunit,Spn in result:
248                        Dx = (Txyz-np.array(Ocoord))+Units
249                        dx = np.inner(Amat,Dx)
250                        dist = ma.masked_less(np.sqrt(np.sum(dx**2,axis=0)),bond/Factor)
251                        IndB = ma.nonzero(ma.masked_greater(dist,bond*Factor))
252                        if np.any(IndB):
253                            for indb in IndB:
254                                for i in range(len(indb)):
255                                    unit = Units[indb][i]+Tunit
256                                    if np.any(unit):
257                                        Topstr = '%d+%d,%d,%d'%(Top,unit[0],unit[1],unit[2])
258                                    else:
259                                        Topstr = str(Top)
260                                    newBond = [[Oid,Tid],['1',Topstr],bond,0.01]
261                                    if newBond not in bondRestData['Bonds']:
262                                        bondRestData['Bonds'].append(newBond)
263        finally:
264            dlg.Destroy()
265        UpdateBondRestr(bondRestData)               
266
267    def AddAABondRestraint(bondRestData):
268        macro = getMacroFile('bond')
269        if not macro:
270            return
271        macStr = macro.readline()
272        atoms = zip(Names,Coords,Ids)
273        Factor = bondRestData['Range']
274        while macStr:
275            items = macStr.split()
276            if 'F' in items[0]:
277                restrData['Bond']['wtFactor'] = float(items[1])
278            elif 'S' in items[0]:
279                oIds = []
280                oCoords = []
281                oDis = []
282                tIds = []
283                tCoords = []
284                tDis = []
285                res = items[1]
286                dist = float(items[2])
287                esd = float(items[3])
288                oAtm,tAtm = items[4:6]
289                for Name,coords,Id in atoms:
290                    names = Name.split(' ',2)
291                    if res == '*' or res in names[0]:
292                        if oAtm.ljust(3) == names[2][:3]:
293                            oIds.append(Id)
294                            oCoords.append(np.array(coords))
295                            oDis.append(names[2][3])
296                        if tAtm.ljust(3) == names[2][:3]:
297                            tIds.append(Id)
298                            tCoords.append(np.array(coords))
299                            tDis.append(names[2][3])
300                for i,[oId,oCoord,odis] in enumerate(zip(oIds,oCoords,oDis)):
301                    for tId,tCoord,tdis in zip(tIds,tCoords,tDis)[i:]:
302                        if odis+tdis in ['AB','BA']:
303                            continue
304                        obsd = np.sqrt(np.sum(np.inner(Amat,tCoord-oCoord)**2))
305                        if dist/Factor < obsd < dist*Factor:
306                            newBond = [[oId,tId],['1','1'],dist,esd]
307                            if newBond not in bondRestData['Bonds']:
308                                bondRestData['Bonds'].append(newBond)             
309            macStr = macro.readline()
310        macro.close()
311        print(' Found %d bond restraints'%len(bondRestData['Bonds']))
312        UpdateBondRestr(bondRestData)
313
314    def AddMogulBondRestraint(bondRestData):
315        mogul = getMOGULFile()
316        for line in mogul:
317            items = line.split(',')
318            if 'bond' == items[0]:
319                oName,tName = items[2].split()
320                oInd = Names.index(oName)
321                tInd = Names.index(tName)
322                if items[3] != 'No hits':
323                    dist = float(items[6])
324                    esd = float(items[7])
325                else:
326                    dist = float(items[5])
327                    esd = 0.02
328                newBond = [[Ids[oInd],Ids[tInd]],['1','1'],dist,esd]
329                if newBond not in bondRestData['Bonds']:
330                    bondRestData['Bonds'].append(newBond)             
331        UpdateBondRestr(bondRestData)
332           
333    def AddAngleRestraint(angleRestData):
334        Radii = dict(zip(General['AtomTypes'],zip(General['BondRadii'],General['AngleRadii'])))
335        Lists = {'A-atom':[],'B-atom':[],'C-atom':[]}
336        for listName in ['A-atom','B-atom']:
337            dlg = wx.MultiChoiceDialog(G2frame,'Select '+listName+' for angle A-B-C for '+General['Name']                                                                           ,
338                    'Select angle restraint '+listName,Names)
339            if dlg.ShowModal() == wx.ID_OK:
340                sel = dlg.GetSelections()
341                for x in sel:
342                    if 'all' in Names[x]:
343                        allType = Types[x]
344                        for name,Type,coords,Id in zip(Names,Types,Coords,Ids):
345                            if Type == allType and 'all' not in name:
346                                if 'A' in listName:
347                                    Lists[listName].append(Type)
348                                else:
349                                    Lists[listName].append([Id,Type,coords])
350                    else:
351                        if 'A' in listName:
352                            Lists[listName].append(Types[x])
353                        else:
354                            Lists[listName].append([Ids[x],Types[x],Coords[x],])
355            else:
356                break
357            targAtoms = [[Ids[x+iBeg],Types[x+iBeg],Coords[x+iBeg]] for x in range(len(Names[iBeg:]))]
358        if len(Lists['B-atom']):
359            value = 109.54
360            dlg = G2G.SingleFloatDialog(G2frame,'Angle','Enter restraint angle ',value,[30.,180.],'%.2f')
361            if dlg.ShowModal() == wx.ID_OK:
362                value = dlg.GetValue()
363            dlg.Destroy()
364
365        Factor = angleRestData['Range']
366        indices = (-2,-1,0,1,2)
367        Units = np.array([[h,k,l] for h in indices for k in indices for l in indices])
368        VectA = []
369        for Oid,Otype,Ocoord in Lists['B-atom']:
370            IndBlist = []
371            VectB = []
372            for Tid,Ttype,Tcoord in targAtoms:
373                result = G2spc.GenAtom(Tcoord,SGData,False,Move=False)
374                BsumR = (Radii[Otype][0]+Radii[Ttype][0])*Factor
375                AsumR = (Radii[Otype][1]+Radii[Ttype][1])*Factor
376                for Txyz,Top,Tunit,Spn in result:
377                    Dx = (Txyz-Ocoord)+Units
378                    dx = np.inner(Amat,Dx)
379                    dist = ma.masked_less(np.sqrt(np.sum(dx**2,axis=0)),0.5)
380                    IndB = ma.nonzero(ma.masked_greater(dist,BsumR))
381                    if np.any(IndB):
382                        for indb in IndB:
383                            for i in range(len(indb)):
384                                if str(dx.T[indb][i]) not in IndBlist:
385                                    IndBlist.append(str(dx.T[indb][i]))
386                                    unit = Units[indb][i]+Tunit
387                                if np.any(unit):
388                                    Topstr = '%d+%d,%d,%d'%(Top,unit[0],unit[1],unit[2])
389                                else:
390                                    Topstr = str(Top)
391                                Dist = ma.getdata(dist[indb])[i]
392                                if (Dist-AsumR) <= 0.:
393                                    VectB.append([Oid,'1',Ocoord,Ttype,Tid,Topstr,Tcoord,Dist])
394            VectA.append(VectB)
395        for Vects in VectA:
396            for i,vecta in enumerate(Vects):                   
397                for vectb in Vects[:i]:
398                    if vecta[3] in Lists['A-atom']:
399                        ids = [vecta[4],vecta[0],vectb[4]]
400                        ops = [vecta[5],vecta[1],vectb[5]]
401                        angle = [ids,ops,value,1.0]
402                        if angle not in angleRestData['Angles']:
403                            angleRestData['Angles'].append(angle)
404        UpdateAngleRestr(angleRestData)               
405
406    def AddAAAngleRestraint(angleRestData):
407        macro = getMacroFile('angle')
408        if not macro:
409            return
410        Chains = makeChains(Names,Ids)           
411        macStr = macro.readline()
412        while macStr:
413            items = macStr.split()
414            if 'F' in items[0]:
415                restrData['Angle']['wtFactor'] = float(items[1])
416            elif 'S' in items[0]:
417                Res = items[1]
418                Value = float(items[2])
419                Esd = float(items[3])
420                Atms = items[4:7]
421                pAtms = ['','','']
422                for i,atm in enumerate(Atms):
423                    if '+' in atm:
424                        pAtms[i] = atm.strip('+')
425                ids = [0,0,0]
426                chains = list(Chains.keys())
427                chains.sort()
428                for chain in chains:
429                    residues = list(Chains[chain].keys())
430                    residues.sort()
431                    for residue in residues:
432                        for ires in [0,1]:
433                            if Res != '*':  #works with disordered res
434                                for res,name,Id in Chains[chain][residue][ires]:
435                                    if Res == res:
436                                        try:
437                                            ipos = Atms.index(name[:3].strip())
438                                            ids[ipos] = Id
439                                        except ValueError:
440                                            continue
441                            else:
442                                try:
443                                    for res,name,Id in Chains[chain][residue][ires]:
444                                        try:
445                                            ipos = Atms.index(name[:3].strip())
446                                            ids[ipos] = Id
447                                        except ValueError:
448                                            continue
449                                    for res,name,Id in Chains[chain][residue+1][ires]:
450                                        try:
451                                            ipos = pAtms.index(name[:3].strip())
452                                            ids[ipos] = Id
453                                        except ValueError:
454                                            continue
455                                except KeyError:
456                                    continue
457                            if all(ids):
458                                angle = [list(ids),['1','1','1'],Value,Esd]
459                                if angle not in angleRestData['Angles']:
460                                    angleRestData['Angles'].append(angle)
461                                ids = [0,0,0]
462            macStr = macro.readline()
463        macro.close()
464        print(' Found %d angle restraints'%len(angleRestData['Angles']))
465        UpdateAngleRestr(angleRestData)               
466       
467    def AddMogulAngleRestraint(angleRestData):
468        mogul = getMOGULFile()
469        for line in mogul:
470            items = line.split(',')
471            if 'angle' == items[0]:
472                aName,bName,cName = items[2].split()
473                aInd = Names.index(aName)
474                bInd = Names.index(bName)
475                cInd = Names.index(cName)
476                if items[3] != 'No hits':
477                    angle = float(items[6])
478                    esd = float(items[7])
479                else:
480                    angle = float(items[5])
481                    esd = 2.00
482                newAngle = [[Ids[aInd],Ids[bInd],Ids[cInd]],['1','1','1'],angle,esd]
483                if newAngle not in angleRestData['Angles']:
484                    angleRestData['Angles'].append(newAngle)             
485        UpdateAngleRestr(angleRestData)               
486
487    def AddPlaneRestraint(restrData):
488        ids = []
489        dlg = wx.MultiChoiceDialog(G2frame,'Select 4 or more atoms for plane in '+General['Name'],
490                'Select 4+ atoms',Names[iBeg:])
491        if dlg.ShowModal() == wx.ID_OK:
492            sel = dlg.GetSelections()
493            if len(sel) > 3:
494                for x in sel:
495                    ids.append(Ids[x+iBeg])
496                ops = ['1' for i in range(len(sel))]
497                plane = [ids,ops,0.0,0.01]
498                if plane not in restrData['Planes']:
499                    restrData['Planes'].append(plane)
500            else:
501                print ('**** ERROR - not enough atoms for a plane restraint - try again ****')
502        UpdatePlaneRestr(restrData)               
503
504    def AddAAPlaneRestraint(planeRestData):
505        macro = getMacroFile('plane')
506        if not macro:
507            return
508        Chains = makeChains(Names,Ids)           
509        macStr = macro.readline()
510        while macStr:
511            items = macStr.split()
512            if 'F' in items[0]:
513                restrData['Plane']['wtFactor'] = float(items[1])
514            elif 'S' in items[0]:
515                Res = items[1]
516                Esd = float(items[2])
517                Atms = items[3:]
518                pAtms = ['' for i in Atms]
519                for i,atm in enumerate(Atms):
520                    if '+' in atm:
521                        pAtms[i] = atm.strip('+')
522                ids = [0,]*len(Atms)
523                ops = ['1' for i in range(len(Atms))]
524                chains = list(Chains.keys())
525                chains.sort()
526                for chain in chains:
527                    residues = list(Chains[chain].keys())
528                    residues.sort()
529                    for residue in residues:
530                        for ires in [0,1]:
531                            if residue == residues[-1] and Res == '*':
532                                continue
533                            if Res != '*':  #works with disordered res
534                                for res,name,Id in Chains[chain][residue][ires]:
535                                    if Res == res:
536                                        try:
537                                            ipos = Atms.index(name[:3].strip())
538                                            ids[ipos] = Id
539                                        except ValueError:
540                                            continue
541                            else:
542                                try:
543                                    for res,name,Id in Chains[chain][residue][ires]:
544                                        try:
545                                            ipos = Atms.index(name[:3].strip())
546                                            ids[ipos] = Id
547                                        except ValueError:
548                                            continue
549                                    for res,name,Id in Chains[chain][residue+1][ires]:
550                                        try:
551                                            ipos = pAtms.index(name[:3].strip())
552                                            ids[ipos] = Id
553                                        except ValueError:
554                                            continue
555                                except KeyError:
556                                    continue
557                            if all(ids):
558                                plane = [list(ids),ops,0.0,Esd]
559                                if plane not in planeRestData['Planes']:
560                                    planeRestData['Planes'].append(plane)
561                                ids = [0,]*len(Atms)
562            macStr = macro.readline()
563        macro.close()
564        print(' Found %d plane restraints'%len(planeRestData['Planes']))
565        UpdatePlaneRestr(planeRestData)               
566
567    def AddAAChiralRestraint(chiralRestData):
568        macro = getMacroFile('chiral')
569        if not macro:
570            return
571        Chains = makeChains(Names,Ids)           
572        macStr = macro.readline()
573        while macStr:
574            items = macStr.split()
575            if 'F' in items[0]:
576                restrData['Chiral']['wtFactor'] = float(items[1])
577            elif 'S' in items[0]:
578                Res = items[1]
579                Value = float(items[2])
580                Esd = float(items[3])
581                Atms = items[4:8]
582                ids = [0,0,0,0]
583                chains = list(Chains.keys())
584                chains.sort()
585                for chain in chains:
586                    residues = list(Chains[chain].keys())
587                    residues.sort()
588                    for residue in residues:
589                        for ires in [0,1]:
590                            if residue == residues[-1] and Res == '*':
591                                continue
592                            if Res != '*':  #works with disordered res
593                                for res,name,Id in Chains[chain][residue][ires]:
594                                    if Res == res:
595                                        try:
596                                            ipos = Atms.index(name[:3].strip())
597                                            ids[ipos] = Id
598                                        except ValueError:
599                                            continue
600                            else:
601                                try:
602                                    for res,name,Id in Chains[chain][residue][ires]:
603                                        try:
604                                            ipos = Atms.index(name[:3].strip())
605                                            ids[ipos] = Id
606                                        except ValueError:
607                                            continue
608                                except KeyError:
609                                    continue
610                            if all(ids):
611                                chiral = [list(ids),['1','1','1','1'],Value,Esd]
612                                if chiral not in chiralRestData['Volumes']:
613                                    chiralRestData['Volumes'].append(chiral)
614                                ids = [0,0,0,0]
615            macStr = macro.readline()
616        macro.close()
617        print(' Found %d chiral volumes restraints'%len(chiralRestData['Volumes']))
618        UpdateChiralRestr(chiralRestData)               
619       
620    def AddAATorsionRestraint(torsionRestData):
621        macro = getMacroFile('torsion')
622        if not macro:
623            return
624        Chains = makeChains(Names,Ids)           
625        macStr = macro.readline()[:-1]
626        while macStr:
627            items = macStr.split()
628            if 'F' in items[0]:
629                restrData['Torsion']['wtFactor'] = float(items[1])
630            elif 'A' in items[0]:
631                name = items[10]
632                coeff = np.zeros(9)
633                for i,item in enumerate(items[1:10]):
634                    coeff[i] = float(item)
635                torsionRestData['Coeff'][name] = coeff
636            elif 'S' in items[0]:
637                Name = items[1]
638                Res = items[2]
639                Esd = float(items[3])
640                Atms = items[4:8]
641                pAtms = ['','','','']
642                for i,atm in enumerate(Atms):
643                    if '+' in atm:
644                        pAtms[i] = atm.strip('+')
645                ids = [0,0,0,0]
646                chains = list(Chains.keys())
647                chains.sort()
648                for chain in chains:
649                    residues = list(Chains[chain].keys())
650                    residues.sort()
651                    for residue in residues:
652                        for ires in [0,1]:
653                            if residue == residues[-1] and Res == '*':
654                                continue
655                            if Res != '*':  #works with disordered res
656                                for res,name,Id in Chains[chain][residue][ires]:
657                                    if Res == res:
658                                        try:
659                                            ipos = Atms.index(name[:3].strip())
660                                            ids[ipos] = Id
661                                        except ValueError:
662                                            continue
663                            else:
664                                try:
665                                    for res,name,Id in Chains[chain][residue][ires]:
666                                        try:
667                                            ipos = Atms.index(name[:3].strip())
668                                            ids[ipos] = Id
669                                        except ValueError:
670                                            continue
671                                    for res,name,Id in Chains[chain][residue+1][ires]:
672                                        try:
673                                            ipos = pAtms.index(name[:3].strip())
674                                            ids[ipos] = Id
675                                        except ValueError:
676                                            continue
677                                except KeyError:
678                                    continue
679                            if all(ids):
680                                torsion = [list(ids),['1','1','1','1'],Name,Esd]
681                                if torsion not in torsionRestData['Torsions']:
682                                    torsionRestData['Torsions'].append(torsion)
683                                ids = [0,0,0,0]                           
684            macStr = macro.readline()
685        macro.close()
686        print(' Found %d torsion restraints'%len(torsionRestData['Torsions']))
687        UpdateTorsionRestr(torsionRestData)                       
688       
689    def AddAARamaRestraint(ramaRestData):
690        macro = getMacroFile('Ramachandran')
691        if not macro:
692            return
693        Chains = makeChains(Names,Ids)           
694        macStr = macro.readline()
695        while macStr:
696            items = macStr.split()
697            if 'F' in items[0]:
698                restrData['Rama']['wtFactor'] = float(items[1])
699            elif 'A' in items[0]:
700                nTerms = int(items[1])
701                name = items[2]
702                coeff = np.zeros((nTerms,6))
703                for i in range(nTerms):
704                    macStr = macro.readline()
705                    items = macStr.split()
706                    for j,val in enumerate(items):
707                        coeff[i][j] = float(val)
708                ramaRestData['Coeff'][name] = coeff
709            elif 'S' in items[0]:
710                Name = items[1]
711                Res = items[2]
712                Esd = float(items[3])
713                Atms = items[4:9]
714                mAtms = ['','','','','']
715                pAtms = ['','','','','']
716                for i,atm in enumerate(Atms):
717                    if '+' in atm:
718                        pAtms[i] = atm.strip('+')
719                    elif '-' in atm:
720                        mAtms[i] = atm.strip('-')
721                ids = [0,0,0,0,0]
722                chains = list(Chains.keys())
723                chains.sort()
724                for chain in chains:
725                    residues = list(Chains[chain].keys())
726                    residues.sort()
727                    if not (any(mAtms) or any(pAtms)):
728                        for residue in residues:
729                            for ires in [0,1]:
730                                for res,name,Id in Chains[chain][residue][ires]:
731                                    if Res == res:
732                                        try:
733                                            ipos = Atms.index(name[:3].strip())
734                                            ids[ipos] = Id
735                                        except ValueError:
736                                            continue
737                                if all(ids):
738                                    rama = [list(ids),['1','1','1','1','1'],Name,Esd]
739                                    if rama not in ramaRestData['Ramas']:
740                                        ramaRestData['Ramas'].append(rama)
741                                    ids = [0,0,0,0,0]
742                    else:
743                        for ires,residue in enumerate(residues[1:-1]):
744                            for jres in [0,1]:
745                                try:
746                                    for res,name,Id in Chains[chain][residue-1][jres]:
747                                        try:
748                                            ipos = mAtms.index(name[:3].strip())
749                                            ids[ipos] = Id
750                                        except ValueError:
751                                            continue
752                                    for res,name,Id in Chains[chain][residue+1][jres]:
753                                        try:
754                                            ipos = pAtms.index(name[:3].strip())
755                                            ids[ipos] = Id
756                                        except ValueError:
757                                            continue
758                                    for res,name,Id in Chains[chain][residue][jres]:
759                                        if Res == res:
760                                            try:
761                                                ipos = Atms.index(name[:3].strip())
762                                                ids[ipos] = Id
763                                            except ValueError:
764                                                continue
765                                    if all(ids):
766                                        rama = [list(ids),['1','1','1','1','1'],Name,Esd]
767                                        if rama not in ramaRestData['Ramas']:
768                                            ramaRestData['Ramas'].append(rama)
769                                        ids = [0,0,0,0,0]
770                                except KeyError:
771                                    continue
772            macStr = macro.readline()
773        macro.close()
774        print(' Found %d Ramachandran restraints'%len(ramaRestData['Ramas']))
775        UpdateRamaRestr(ramaRestData)
776       
777    def AddChemCompRestraint(chemcompRestData):
778        ids = []
779        factors = []
780        dlg = wx.MultiChoiceDialog(G2frame,'Select atoms for chemical restraint in '+General['Name'],
781                'Select atoms',Names)
782        if dlg.ShowModal() == wx.ID_OK:
783            sel = dlg.GetSelections()
784            for x in sel:
785                if 'all' in Names[x]:
786                    allType = Types[x]
787                    for name,Type,Id in zip(Names,Types,Ids):
788                        if Type == allType and 'all' not in name:
789                            ids.append(Id)
790                            factors.append(1.0)
791                else:
792                    ids.append(Ids[x])
793                    factors.append(1.0)
794            dlg.Destroy()
795            if len(ids) > 0:
796                value = 1.0
797                dlg = G2G.SingleFloatDialog(G2frame,'Cell sum','Enter unit cell sum ',value,[-1.e6,1.e6],'%.2f')
798                if dlg.ShowModal() == wx.ID_OK:
799                    value = dlg.GetValue()               
800                    comp = [ids,factors,value,0.01]
801                    if comp not in chemcompRestData['Sites']:
802                        chemcompRestData['Sites'].append(comp)
803                UpdateChemcompRestr(chemcompRestData)
804            else:
805                print ('**** ERROR - not enough atoms for a composition restraint - try again ****')
806       
807    def AddTextureRestraint(textureRestData):
808        dlg = wx.TextEntryDialog(G2frame,'Enter h k l for pole figure restraint','Enter HKL','')
809        if dlg.ShowModal() == wx.ID_OK:
810            text = dlg.GetValue()
811            vals = text.split()
812            try:
813                hkl = [int(vals[i]) for i in range(3)]
814                texture = [hkl,24,0.01,False,1.0]
815                if texture not in textureRestData['HKLs']:
816                        textureRestData['HKLs'].append(texture)
817                UpdateTextureRestr(textureRestData)
818            except (ValueError,IndexError):
819                print ('**** ERROR - bad input of h k l - try again ****')
820        dlg.Destroy()
821               
822    def WtBox(wind,restData):
823        if 'Range' not in restData: restData['Range'] = 1.1     #patch
824       
825        def OnUseData(event):
826            Obj = event.GetEventObject()
827            restData['Use'] = Obj.GetValue()
828
829        wtBox = wx.BoxSizer(wx.HORIZONTAL)
830        wtBox.Add(wx.StaticText(wind,-1,'Phase '+phaseName+' Restraint weight factor: '),0,WACV)
831        wtfactor = G2G.ValidatedTxtCtrl(wind,restData,'wtFactor',nDig=(10,2),typeHint=float)
832        wtBox.Add(wtfactor,0,WACV)
833        useData = wx.CheckBox(wind,-1,label=' Use?')
834        useData.Bind(wx.EVT_CHECKBOX, OnUseData)
835        useData.SetValue(restData['Use'])       
836        wtBox.Add(useData,0,WACV)
837        if 'Bonds' in restData or 'Angles' in restData:
838            wtBox.Add(wx.StaticText(wind,-1,'  Search range: '),0,WACV)
839            sRange = G2G.ValidatedTxtCtrl(wind,restData,'Range',nDig=(10,2),typeHint=float)
840            wtBox.Add(sRange,0,WACV)
841            wtBox.Add(wx.StaticText(wind,-1,' x sum(atom radii)'),0,WACV)
842        return wtBox
843       
844    def OnRowSelect(event):
845        r,c =  event.GetRow(),event.GetCol()
846        Obj = event.GetEventObject()
847        if r < 0 and c < 0:
848            if Obj.IsSelection():
849                Obj.ClearSelection()
850            else:
851                for row in range(Obj.GetNumberRows()):
852                    Obj.SelectRow(row,True)
853        elif c < 0:                   #only row clicks
854            if event.ControlDown():                   
855                if r in Obj.GetSelectedRows():
856                    Obj.DeselectRow(r)
857                else:
858                    Obj.SelectRow(r,True)
859            elif event.ShiftDown():
860                indxs = Obj.GetSelectedRows()
861                Obj.ClearSelection()
862                ibeg = 0
863                if indxs:
864                    ibeg = indxs[-1]
865                for row in range(ibeg,r+1):
866                    Obj.SelectRow(row,True)
867            else:
868                Obj.ClearSelection()
869                Obj.SelectRow(r,True)
870               
871                   
872    def UpdateBondRestr(bondRestData):
873       
874        def OnCellChange(event):
875            r,c =  event.GetRow(),event.GetCol()
876            try:
877                new = float(bondTable.GetValue(r,c))
878                if new <= 0.:
879                    raise ValueError
880                bondRestData['Bonds'][r][c] = new
881            except ValueError:
882                pass           
883            wx.CallAfter(UpdateBondRestr,bondRestData)               
884
885        def OnChangeValue(event):
886            rows = GetSelectedRows(Bonds)
887            if not rows:
888                return
889            Bonds.ClearSelection()
890            val = bondList[rows[0]][2]
891            dlg = G2G.SingleFloatDialog(G2frame,'New value','Enter new value for bond',val,[0.,5.],'%.4f')
892            if dlg.ShowModal() == wx.ID_OK:
893                parm = dlg.GetValue()
894                for r in rows:
895                    bondRestData['Bonds'][r][2] = parm
896            dlg.Destroy()
897            UpdateBondRestr(bondRestData)               
898
899        def OnChangeEsd(event):
900            rows = GetSelectedRows(Bonds)
901            if not rows:
902                return
903            Bonds.ClearSelection()
904            val = bondList[rows[0]][3]
905            dlg = G2G.SingleFloatDialog(G2frame,'New value','Enter new esd for bond',val,[0.,1.],'%.4f')
906            if dlg.ShowModal() == wx.ID_OK:
907                parm = dlg.GetValue()
908                for r in rows:
909                    bondRestData['Bonds'][r][3] = parm
910            dlg.Destroy()
911            UpdateBondRestr(bondRestData)               
912                               
913        def OnDeleteRestraint(event):
914            rows = GetSelectedRows(Bonds)
915            if not rows:
916                return
917            Bonds.ClearSelection()
918            rows.sort()
919            rows.reverse()
920            for row in rows:
921                bondList.remove(bondList[row])
922            UpdateBondRestr(bondRestData)               
923           
924        if BondRestr.GetSizer(): BondRestr.GetSizer().Clear(True)
925        mainSizer = wx.BoxSizer(wx.VERTICAL)
926        mainSizer.Add((5,5),0)
927        mainSizer.Add(WtBox(BondRestr,bondRestData),0,WACV)
928
929        bondList = bondRestData['Bonds']
930        if len(bondList) and len(bondList[0]) == 6:   #patch
931            bondList = bondRestData['Bonds'] = []
932        if len(bondList):
933            table = []
934            rowLabels = []
935            bad = []
936            chisq = 0.
937            Types = [wg.GRID_VALUE_STRING,]+4*[wg.GRID_VALUE_FLOAT+':10,3',]
938            if 'macro' in General['Type']:
939                colLabels = ['(res) A - (res) B','calc','target','esd','delt/sig']
940                for i,[indx,ops,obs,esd] in enumerate(bondList):
941                    try:
942                        atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
943                        name = ''
944                        for atom in atoms:
945                            name += '('+atom[1]+atom[0].strip()+atom[2]+') '+atom[3]+' - '
946                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
947                        calc = G2mth.getRestDist(XYZ,Amat)
948                        chisq += bondRestData['wtFactor']*((obs-calc)/esd)**2
949                        table.append([name[:-3],calc,obs,esd,(obs-calc)/esd])
950                        rowLabels.append(str(i))               
951                    except KeyError:
952                        print ('**** WARNING - missing atom - restraint deleted ****')
953                        bad.append(i)
954            else:
955                colLabels = ['A+SymOp - B+SymOp','calc','target','esd','delt/sig']
956                for i,[indx,ops,obs,esd] in enumerate(bondList):
957                    try:
958                        names = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,ct-1)
959                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
960                        XYZ = G2mth.getSyXYZ(XYZ,ops,SGData)
961                        calc = G2mth.getRestDist(XYZ,Amat)
962                        chisq += bondRestData['wtFactor']*((obs-calc)/esd)**2
963                        table.append([names[0]+'+('+ops[0]+') - '+names[1]+'+('+ops[1]+')',calc,obs,esd,(obs-calc)/esd])
964                        rowLabels.append(str(i))
965                    except KeyError:
966                        print ('**** WARNING - missing atom - restraint deleted ****')
967                        bad.append(i)
968            if len(bad):
969                bad.reverse()
970                for ibad in bad:
971                    del bondList[ibad]
972            if len(bondList):
973                bondTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
974                Bonds = G2G.GSGrid(BondRestr)
975                Bonds.SetTable(bondTable, True)
976                Bonds.AutoSizeColumns(False)
977                for r in range(len(bondList)):
978                    for c in [0,1,4]:
979                        Bonds.SetReadOnly(r,c,True)
980                        Bonds.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
981                Bonds.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
982                if 'phoenix' in wx.version():
983                    Bonds.Bind(wg.EVT_GRID_CELL_CHANGED, OnCellChange)
984                else:
985                    Bonds.Bind(wg.EVT_GRID_CELL_CHANGE, OnCellChange)
986                G2frame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2G.wxID_RESTDELETE)
987                G2frame.Bind(wx.EVT_MENU, OnChangeValue, id=G2G.wxID_RESRCHANGEVAL)
988                G2frame.Bind(wx.EVT_MENU, OnChangeEsd, id=G2G.wxID_RESTCHANGEESD)
989                mainSizer.Add(wx.StaticText(BondRestr,-1,
990                    'Bond restraints: sum(wt*(delt/sig)^2) =    %.2f, mean(wt*(delt/sig)^2) =    %.2f'    \
991                    %(chisq,chisq/len(bondList))),0,WACV)
992                Bonds.SetScrollRate(10,10)
993                Bonds.SetMinSize((-1,300))
994                mainSizer.Add(Bonds,1,wx.EXPAND,1)
995            else:
996                mainSizer.Add(wx.StaticText(BondRestr,-1,'No bond distance restraints for this phase'),0,)
997        else:
998            mainSizer.Add(wx.StaticText(BondRestr,-1,'No bond distance restraints for this phase'),0,)
999
1000        wx.CallAfter(G2phsGUI.SetPhaseWindow,BondRestr,mainSizer,Scroll=0)
1001       
1002    def UpdateAngleRestr(angleRestData):
1003       
1004        def OnCellChange(event):
1005            r,c =  event.GetRow(),event.GetCol()
1006            try:
1007                new = float(angleTable.GetValue(r,c))
1008                if new <= 0. or new > 180.:
1009                    raise ValueError
1010                angleRestData['Angles'][r][c] = new
1011            except ValueError:
1012                pass           
1013            wx.CallAfter(UpdateAngleRestr,angleRestData)               
1014           
1015        def OnChangeValue(event):
1016            rows = GetSelectedRows(Angles)
1017            if not rows:
1018                return
1019            Angles.ClearSelection()
1020            val = angleList[rows[0]][2]
1021            dlg = G2G.SingleFloatDialog(G2frame,'New value','Enter new value for angle',val,[0.,360.],'%.2f')
1022            if dlg.ShowModal() == wx.ID_OK:
1023                parm = dlg.GetValue()
1024                for r in rows:
1025                    angleRestData['Angles'][r][2] = parm
1026            dlg.Destroy()
1027            UpdateAngleRestr(angleRestData)               
1028
1029        def OnChangeEsd(event):
1030            rows = GetSelectedRows(Angles)
1031            if not rows:
1032                return
1033            Angles.ClearSelection()
1034            val = angleList[rows[0]][3]
1035            dlg = G2G.SingleFloatDialog(G2frame,'New value','Enter new esd for angle',val,[0.,5.],'%.2f')
1036            if dlg.ShowModal() == wx.ID_OK:
1037                parm = dlg.GetValue()
1038                for r in rows:
1039                    angleRestData['Angles'][r][3] = parm
1040            dlg.Destroy()
1041            UpdateAngleRestr(angleRestData)               
1042                                           
1043        def OnDeleteRestraint(event):
1044            rows = GetSelectedRows(Angles)
1045            if not rows:
1046                return
1047            rows.sort()
1048            rows.reverse()
1049            for row in rows:
1050                angleList.remove(angleList[row])
1051            UpdateAngleRestr(angleRestData)               
1052           
1053        if AngleRestr.GetSizer():
1054            AngleRestr.GetSizer().Clear(True)
1055        mainSizer = wx.BoxSizer(wx.VERTICAL)
1056        mainSizer.Add((5,5),0)
1057        mainSizer.Add(WtBox(AngleRestr,angleRestData),0,WACV)
1058
1059        angleList = angleRestData['Angles']
1060        if len(angleList):
1061            table = []
1062            rowLabels = []
1063            bad = []
1064            chisq = 0.
1065            Types = [wg.GRID_VALUE_STRING,]+4*[wg.GRID_VALUE_FLOAT+':10,2',]
1066            if 'macro' in General['Type']:
1067                colLabels = ['(res) A - (res) B - (res) C','calc','target','esd','delt/sig']
1068                for i,[indx,ops,obs,esd] in enumerate(angleList):
1069                    try:
1070                        atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
1071                        name = ''
1072                        for atom in atoms:
1073                            name += '('+atom[1]+atom[0].strip()+atom[2]+') '+atom[3]+' - '
1074                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
1075                        calc = G2mth.getRestAngle(XYZ,Amat)
1076                        chisq += angleRestData['wtFactor']*((obs-calc)/esd)**2
1077                        table.append([name[:-3],calc,obs,esd,(obs-calc)/esd])
1078                        rowLabels.append(str(i))                               
1079                    except KeyError:
1080                        print ('**** WARNING - missing atom - restraint deleted ****')
1081                        bad.append(i)
1082            else:
1083                colLabels = ['A+SymOp - B+SymOp - C+SymOp','calc','target','esd','delt/sig']
1084                for i,[indx,ops,obs,esd] in enumerate(angleList):
1085                    try:
1086                        atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,ct-1)
1087                        name = atoms[0]+'+('+ops[0]+') - '+atoms[1]+'+('+ops[1]+') - '+atoms[2]+ \
1088                        '+('+ops[2]+')'
1089                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
1090                        XYZ = G2mth.getSyXYZ(XYZ,ops,SGData)
1091                        calc = G2mth.getRestAngle(XYZ,Amat)
1092                        chisq += angleRestData['wtFactor']*((obs-calc)/esd)**2
1093                        table.append([name,calc,obs,esd,(obs-calc)/esd])
1094                        rowLabels.append(str(i))
1095                    except KeyError:
1096                        print ('**** WARNING - missing atom - restraint deleted ****')
1097                        bad.append(i)
1098            if len(bad):
1099                bad.reverse()
1100                for ibad in bad:
1101                    del angleList[ibad]
1102            if len(angleList):
1103                angleTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1104                Angles = G2G.GSGrid(AngleRestr)
1105                Angles.SetTable(angleTable, True)
1106                Angles.AutoSizeColumns(False)
1107                for r in range(len(angleList)):
1108                    for c in [0,1,4]:
1109                        Angles.SetReadOnly(r,c,True)
1110                        Angles.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1111                Angles.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
1112                if 'phoenix' in wx.version():
1113                    Angles.Bind(wg.EVT_GRID_CELL_CHANGED, OnCellChange)
1114                else:
1115                    Angles.Bind(wg.EVT_GRID_CELL_CHANGE, OnCellChange)
1116                G2frame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2G.wxID_RESTDELETE)
1117                G2frame.Bind(wx.EVT_MENU, OnChangeValue, id=G2G.wxID_RESRCHANGEVAL)
1118                G2frame.Bind(wx.EVT_MENU, OnChangeEsd, id=G2G.wxID_RESTCHANGEESD)
1119                mainSizer.Add(wx.StaticText(AngleRestr,-1,
1120                    'Angle restraints: sum(wt*(delt/sig)^2) =    %.2f, mean(wt*(delt/sig)^2) =    %.2f'    \
1121                    %(chisq,chisq/len(angleList))),0,WACV)
1122                Angles.SetScrollRate(10,10)
1123                Angles.SetMinSize((-1,300))
1124                mainSizer.Add(Angles,1,wx.EXPAND,1)
1125            else:
1126                mainSizer.Add(wx.StaticText(AngleRestr,-1,'No bond angle restraints for this phase'),0,)
1127        else:
1128            mainSizer.Add(wx.StaticText(AngleRestr,-1,'No bond angle restraints for this phase'),0,)
1129        wx.CallAfter(G2phsGUI.SetPhaseWindow,AngleRestr,mainSizer,Scroll=0)
1130   
1131    def UpdatePlaneRestr(planeRestData):
1132       
1133        items = G2frame.dataWindow.RestraintEdit.GetMenuItems()
1134        for item in items:
1135            if item.GetLabel() in ['Change value']:
1136                item.Enable(False)
1137
1138        def OnCellChange(event):
1139            r,c =  event.GetRow(),event.GetCol()
1140            try:
1141                new = float(planeTable.GetValue(r,c))
1142                if new <= 0.:
1143                    raise ValueError
1144                planeRestData['Planes'][r][c] = new
1145            except ValueError:
1146                pass           
1147            wx.CallAfter(UpdatePlaneRestr,planeRestData)               
1148           
1149        def OnChangeEsd(event):
1150            rows = GetSelectedRows(Planes)
1151            if not rows:
1152                return
1153            Planes.ClearSelection()
1154            val = planeList[rows[0]][3]
1155            dlg = G2G.SingleFloatDialog(G2frame,'New value','Enter new esd for plane',val,[0.,5.],'%.2f')
1156            if dlg.ShowModal() == wx.ID_OK:
1157                parm = dlg.GetValue()
1158                for r in rows:
1159                    planeRestData['Planes'][r][3] = parm
1160            dlg.Destroy()
1161            UpdatePlaneRestr(planeRestData)               
1162                                           
1163        def OnDeleteRestraint(event):
1164            rows = GetSelectedRows(Planes)
1165            if not rows:
1166                return
1167            rows.sort()
1168            rows.reverse()
1169            for row in rows:
1170                planeList.remove(planeList[row])
1171            UpdatePlaneRestr(planeRestData)               
1172           
1173        if PlaneRestr.GetSizer():
1174            PlaneRestr.GetSizer().Clear(True)
1175        mainSizer = wx.BoxSizer(wx.VERTICAL)
1176        mainSizer.Add((5,5),0)
1177        mainSizer.Add(WtBox(PlaneRestr,planeRestData),0,WACV)
1178
1179        planeList = planeRestData['Planes']
1180        if len(planeList):
1181            table = []
1182            rowLabels = []
1183            bad = []
1184            chisq = 0.
1185            Types = [wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,2',]
1186            if 'macro' in General['Type']:
1187                colLabels = ['(res) atom','calc','target','esd']
1188                for i,[indx,ops,obs,esd] in enumerate(planeList):
1189                    try:
1190                        atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
1191                        name = ''
1192                        for a,atom in enumerate(atoms):
1193                            name += '('+atom[1]+atom[0].strip()+atom[2]+') '+atom[3]+' - '
1194                            if (a+1)%3 == 0:
1195                                name += '\n'
1196                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
1197                        calc = G2mth.getRestPlane(XYZ,Amat)
1198                        chisq += planeRestData['wtFactor']*((calc)/esd)**2
1199                        table.append([name[:-3],calc,obs,esd])
1200                        rowLabels.append(str(i))
1201                    except KeyError:
1202                        print ('**** WARNING - missing atom - restraint deleted ****')
1203                        bad.append(i)
1204            else:                               
1205                colLabels = ['atom+SymOp','calc','target','esd']
1206                for i,[indx,ops,obs,esd] in enumerate(planeList):
1207                    try:
1208                        atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,ct-1)
1209                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
1210                        XYZ = G2mth.getSyXYZ(XYZ,ops,SGData)
1211                        calc = G2mth.getRestPlane(XYZ,Amat)
1212                        chisq += planeRestData['wtFactor']*((calc)/esd)**2
1213                        name = ''
1214                        for a,atom in enumerate(atoms):
1215                            name += atom+'+ ('+ops[a]+'),'
1216                            if (a+1)%3 == 0:
1217                                name += '\n'
1218                        table.append([name[:-1],calc,obs,esd])
1219                        rowLabels.append(str(i))
1220                    except KeyError:
1221                        print ('**** WARNING - missing atom - restraint deleted ****')
1222                        bad.append(i)
1223            if len(bad):
1224                bad.reverse()
1225                for ibad in bad:
1226                    del planeList[ibad]
1227            if len(planeList):
1228                planeTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1229                Planes = G2G.GSGrid(PlaneRestr)
1230                Planes.SetTable(planeTable, True)
1231                Planes.AutoSizeColumns(False)
1232                Planes.AutoSizeRows(False)
1233                for r in range(len(planeList)):
1234                    for c in range(3):
1235                        Planes.SetReadOnly(r,c,True)
1236                        Planes.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1237                Planes.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
1238                if 'phoenix' in wx.version():
1239                    Planes.Bind(wg.EVT_GRID_CELL_CHANGED, OnCellChange)
1240                else:
1241                    Planes.Bind(wg.EVT_GRID_CELL_CHANGE, OnCellChange)
1242                G2frame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2G.wxID_RESTDELETE)
1243                G2frame.Bind(wx.EVT_MENU, OnChangeEsd, id=G2G.wxID_RESTCHANGEESD)
1244                mainSizer.Add(wx.StaticText(PlaneRestr,-1,
1245                    'Plane restraints: sum(wt*(delt/sig)^2) =    %.2f, mean(wt*(delt/sig)^2) =    %.2f'    \
1246                    %(chisq,chisq/len(planeList))),0,WACV)
1247                Planes.SetScrollRate(10,10)
1248                Planes.SetMinSize((-1,300))
1249                mainSizer.Add(Planes,1,wx.EXPAND,1)
1250            else:
1251                mainSizer.Add(wx.StaticText(PlaneRestr,-1,'No plane restraints for this phase'),0,)
1252        else:
1253            mainSizer.Add(wx.StaticText(PlaneRestr,-1,'No plane restraints for this phase'),0,)
1254
1255        G2phsGUI.SetPhaseWindow(PlaneRestr,mainSizer,Scroll=0)
1256   
1257    def UpdateChiralRestr(chiralRestData):
1258
1259        def OnCellChange(event):
1260            r,c =  event.GetRow(),event.GetCol()
1261            try:
1262                new = float(volumeTable.GetValue(r,c))
1263                if new <= 0.:
1264                    raise ValueError
1265                chiralRestData['Volumes'][r][c] = new
1266            except ValueError:
1267                pass           
1268            wx.CallAfter(UpdateChiralRestr,chiralRestData)               
1269           
1270        def OnDeleteRestraint(event):
1271            rows = GetSelectedRows(Volumes)
1272            if not rows:
1273                return
1274            rows.sort()
1275            rows.reverse()
1276            for row in rows:
1277                volumeList.remove(volumeList[row])
1278            UpdateChiralRestr(chiralRestData)               
1279           
1280        def OnChangeValue(event):
1281            rows = GetSelectedRows(Volumes)
1282            if not rows:
1283                return
1284            Volumes.ClearSelection()
1285            val = volumeList[rows[0]][2]
1286            dlg = G2G.SingleFloatDialog(G2frame,'New value','Enter new value for chiral volume',val,[0.,360.],'%.2f')
1287            if dlg.ShowModal() == wx.ID_OK:
1288                parm = dlg.GetValue()
1289                for r in rows:
1290                    chiralRestData['Volumes'][r][2] = parm
1291            dlg.Destroy()
1292            UpdateChiralRestr(chiralRestData)               
1293
1294        def OnChangeEsd(event):
1295            rows = GetSelectedRows(Volumes)
1296            if not rows:
1297                return
1298            Volumes.ClearSelection()
1299            val = volumeList[rows[0]][3]
1300            dlg = G2G.SingleFloatDialog(G2frame,'New value','Enter new esd for chiral volume',val,[0.,5.],'%.2f')
1301            if dlg.ShowModal() == wx.ID_OK:
1302                parm = dlg.GetValue()
1303                for r in rows:
1304                    chiralRestData['Volumes'][r][3] = parm
1305            dlg.Destroy()
1306            UpdateChiralRestr(chiralRestData)               
1307                                           
1308        if ChiralRestr.GetSizer(): ChiralRestr.GetSizer().Clear(True)
1309        mainSizer = wx.BoxSizer(wx.VERTICAL)
1310        mainSizer.Add((5,5),0)
1311        mainSizer.Add(WtBox(ChiralRestr,chiralRestData),0,WACV)
1312
1313        volumeList = chiralRestData['Volumes']
1314        if len(volumeList):
1315            table = []
1316            rowLabels = []
1317            bad = []
1318            chisq = 0.
1319            Types = [wg.GRID_VALUE_STRING,]+4*[wg.GRID_VALUE_FLOAT+':10,2',]
1320            if 'macro' in General['Type']:
1321                colLabels = ['(res) O (res) A (res) B (res) C','calc','target','esd','delt/sig']
1322                for i,[indx,ops,obs,esd] in enumerate(volumeList):
1323                    try:
1324                        atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
1325                        name = ''
1326                        for atom in atoms:
1327                            name += '('+atom[1]+atom[0].strip()+atom[2]+') '+atom[3]+' '
1328                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
1329                        calc = G2mth.getRestChiral(XYZ,Amat)
1330                        chisq += chiralRestData['wtFactor']*((obs-calc)/esd)**2
1331                        table.append([name,calc,obs,esd,(obs-calc)/esd])
1332                        rowLabels.append(str(i))
1333                    except KeyError:
1334                        print ('**** WARNING - missing atom - restraint deleted ****')
1335                        bad.append(i)
1336            else:
1337                colLabels = ['O+SymOp  A+SymOp  B+SymOp  C+SymOp)','calc','target','esd','delt/sig']
1338                for i,[indx,ops,obs,esd] in enumerate(volumeList):
1339                    try:
1340                        atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,ct-1)
1341                        name = atoms[0]+'+('+ops[0]+') '+atoms[1]+'+('+ops[1]+') '+atoms[2]+ \
1342                            '+('+ops[2]+') '+atoms[3]+'+('+ops[3]+')'
1343                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
1344                        XYZ = G2mth.getSyXYZ(XYZ,ops,SGData)
1345                        calc = G2mth.getRestChiral(XYZ,Amat)
1346                        chisq += chiralRestData['wtFactor']*((obs-calc)/esd)**2
1347                        table.append([name,calc,obs,esd,(obs-calc)/esd])
1348                        rowLabels.append(str(i))
1349                    except KeyError:
1350                        print ('**** WARNING - missing atom - restraint deleted ****')
1351                        bad.append(i)
1352            if len(bad):
1353                bad.reverse()
1354                for ibad in bad:
1355                    del volumeList[ibad]
1356            if len(volumeList):
1357                volumeTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1358                Volumes = G2G.GSGrid(ChiralRestr)
1359                Volumes.SetTable(volumeTable, True)
1360                Volumes.AutoSizeColumns(False)
1361                for r in range(len(volumeList)):
1362                    for c in [0,1,4]:
1363                        Volumes.SetReadOnly(r,c,True)
1364                        Volumes.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1365                Volumes.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
1366                if 'phoenix' in wx.version():
1367                    Volumes.Bind(wg.EVT_GRID_CELL_CHANGED, OnCellChange)
1368                else:
1369                    Volumes.Bind(wg.EVT_GRID_CELL_CHANGE, OnCellChange)
1370                G2frame.Bind(wx.EVT_MENU, OnChangeValue, id=G2G.wxID_RESRCHANGEVAL)
1371                G2frame.Bind(wx.EVT_MENU, OnChangeEsd, id=G2G.wxID_RESTCHANGEESD)
1372                mainSizer.Add(wx.StaticText(ChiralRestr,-1,
1373                    'Chiral volume restraints: sum(wt*(delt/sig)^2) =    %.2f, mean(wt*(delt/sig)^2) =    %.2f'    \
1374                    %(chisq,chisq/len(volumeList))),0,WACV)
1375                Volumes.SetScrollRate(10,10)
1376                Volumes.SetMinSize((-1,300))
1377                mainSizer.Add(Volumes,1,wx.EXPAND,1)
1378            else:
1379                mainSizer.Add(wx.StaticText(ChiralRestr,-1,'No chiral volume restraints for this phase'),0,)
1380        else:
1381            mainSizer.Add(wx.StaticText(ChiralRestr,-1,'No chiral volume restraints for this phase'),0,)
1382
1383        G2phsGUI.SetPhaseWindow(ChiralRestr,mainSizer,Scroll=0)
1384   
1385    def UpdateTorsionRestr(torsionRestData):
1386
1387        def OnCellChange(event):
1388            r,c =  event.GetRow(),event.GetCol()
1389            try:
1390                new = float(torsionTable.GetValue(r,c))
1391                if new <= 0. or new > 5.:
1392                    raise ValueError
1393                torsionRestData['Torsions'][r][3] = new     #only esd is editable
1394            except ValueError:
1395                pass           
1396            wx.CallAfter(UpdateTorsionRestr,torsionRestData)               
1397           
1398        def OnDeleteRestraint(event):
1399            rows = GetSelectedRows(TorsionRestr.Torsions)
1400            if not rows:
1401                return
1402            rows.sort()
1403            rows.reverse()
1404            for row in rows:
1405                torsionList.remove(torsionList[row])
1406            wx.CallAfter(UpdateTorsionRestr,torsionRestData)               
1407           
1408        def OnChangeEsd(event):
1409            rows = GetSelectedRows(TorsionRestr.Torsions)
1410            if not rows:
1411                return
1412            TorsionRestr.Torsions.ClearSelection()
1413            val = torsionList[rows[0]][4]
1414            dlg = G2G.SingleFloatDialog(G2frame,'New value','Enter new esd for torsion restraints',val,[0.,5.],'%.2f')
1415            if dlg.ShowModal() == wx.ID_OK:
1416                parm = dlg.GetValue()
1417                for r in rows:
1418                    torsionRestData['Torsions'][r][4] = parm
1419            dlg.Destroy()
1420            wx.CallAfter(UpdateTorsionRestr,torsionRestData) 
1421
1422        def coeffSizer():               
1423            table = []
1424            rowLabels = []
1425            Types = 9*[wg.GRID_VALUE_FLOAT+':10,4',]
1426            colLabels = ['Mag A','Pos A','Width A','Mag B','Pos B','Width B','Mag C','Pos C','Width C']
1427            for item in coeffDict:
1428                rowLabels.append(item)
1429                table.append(coeffDict[item])
1430            coeffTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1431            Coeff = G2G.GSGrid(TorsionRestr)
1432            Coeff.SetScrollRate(0,10)
1433            Coeff.SetTable(coeffTable, True)
1434            Coeff.AutoSizeColumns(False)
1435            for r in range(len(coeffDict)):
1436                for c in range(9):
1437                    Coeff.SetReadOnly(r,c,True)
1438                    Coeff.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1439            Coeff.SetMaxSize((-1,200))
1440            return Coeff
1441                                           
1442        if TorsionRestr.GetSizer(): TorsionRestr.GetSizer().Clear(True)
1443        mainSizer = wx.BoxSizer(wx.VERTICAL)
1444        mainSizer.Add((5,5),0)
1445        mainSizer.Add(WtBox(TorsionRestr,torsionRestData),0,WACV)
1446       
1447        coeffDict = torsionRestData['Coeff']
1448        torsionList = torsionRestData['Torsions']
1449        if len(coeffDict):
1450            mainSizer.Add((5,5))
1451            mainSizer.Add(wx.StaticText(TorsionRestr,-1,'Torsion function coefficients:'),0,WACV)
1452            mainSizer.Add(coeffSizer(),1,wx.EXPAND,1)       
1453       
1454        if len(torsionList):
1455            mainSizer.Add(wx.StaticText(TorsionRestr,-1,'Torsion restraints:'),0,WACV)
1456            table = []
1457            rowLabels = []
1458            bad = []
1459            chisq = 0.
1460            Types = 2*[wg.GRID_VALUE_STRING,]+4*[wg.GRID_VALUE_FLOAT+':10,2',]
1461            if 'macro' in General['Type']:
1462                colLabels = ['(res) A  B  C  D','coef name','torsion','obs E','restr','esd']
1463                for i,[indx,ops,cofName,esd] in enumerate(torsionList):
1464                    try:
1465                        atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
1466                        name = '('+atoms[2][1]+atoms[2][0].strip()+atoms[2][2]+')'
1467                        for atom in atoms:
1468                            name += '  '+atom[3]
1469                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
1470                        tor = G2mth.getRestTorsion(XYZ,Amat)
1471                        restr,calc = G2mth.calcTorsionEnergy(tor,coeffDict[cofName])
1472                        chisq += torsionRestData['wtFactor']*(restr/esd)**2
1473                        table.append([name,cofName,tor,calc,restr,esd])
1474                        rowLabels.append(str(i))
1475                    except KeyError:
1476                        print ('**** WARNING - missing atom - restraint deleted ****')
1477                        bad.append(i)
1478            if len(bad):
1479                bad.reverse()
1480                for ibad in bad:
1481                    del torsionList[ibad]
1482            torsionTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1483            TorsionRestr.Torsions = G2G.GSGrid(TorsionRestr)
1484            TorsionRestr.Torsions.SetTable(torsionTable, True)
1485            TorsionRestr.Torsions.AutoSizeColumns(False)
1486            for r in range(len(torsionList)):
1487                for c in range(5):
1488                    TorsionRestr.Torsions.SetReadOnly(r,c,True)
1489                    TorsionRestr.Torsions.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1490            TorsionRestr.Torsions.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
1491            if 'phoenix' in wx.version():
1492                TorsionRestr.Torsions.Bind(wg.EVT_GRID_CELL_CHANGED, OnCellChange)
1493            else:
1494                TorsionRestr.Torsions.Bind(wg.EVT_GRID_CELL_CHANGE, OnCellChange)
1495            G2frame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2G.wxID_RESTDELETE)
1496            G2frame.Bind(wx.EVT_MENU, OnChangeEsd, id=G2G.wxID_RESTCHANGEESD)
1497            mainSizer.Add(wx.StaticText(TorsionRestr,-1,
1498                'Torsion restraints: sum(wt*(delt/sig)^2) =    %.2f, mean(wt*(delt/sig)^2) =    %.2f'    \
1499                %(chisq,chisq/len(torsionList))),0,WACV)
1500            TorsionRestr.Torsions.SetScrollRate(10,10)
1501            TorsionRestr.Torsions.SetMinSize((-1,300))
1502            mainSizer.Add(TorsionRestr.Torsions,1,wx.EXPAND,1)
1503               
1504        else:
1505            mainSizer.Add(wx.StaticText(TorsionRestr,-1,'No torsion restraints for this phase'),0,)
1506
1507        G2phsGUI.SetPhaseWindow(TorsionRestr,mainSizer,Scroll=0)
1508
1509    def UpdateRamaRestr(ramaRestData):
1510
1511        def OnCellChange(event):
1512            r,c =  event.GetRow(),event.GetCol()
1513            try:
1514                new = float(ramaTable.GetValue(r,c))
1515                if new <= 0. or new > 5.:
1516                    raise ValueError
1517                ramaRestData['Ramas'][r][4] = new     #only esd is editable
1518            except ValueError:
1519                pass           
1520            wx.CallAfter(UpdateRamaRestr,ramaRestData)               
1521           
1522        def OnDeleteRestraint(event):
1523            rows = GetSelectedRows(RamaRestr.Ramas)
1524            if not rows:
1525                return
1526            rows.sort()
1527            rows.reverse()
1528            for row in rows:
1529                ramaList.remove(ramaList[row])
1530            UpdateRamaRestr(ramaRestData)               
1531           
1532        def OnChangeEsd(event):
1533            rows = GetSelectedRows(RamaRestr.Ramas)
1534            if not rows:
1535                return
1536            RamaRestr.Ramas.ClearSelection()
1537            val = ramaList[rows[0]][4]
1538            dlg = G2G.SingleFloatDialog(G2frame,'New value','Enter new esd for energy',val,[0.,5.],'%.2f')
1539            if dlg.ShowModal() == wx.ID_OK:
1540                parm = dlg.GetValue()
1541                for r in rows:
1542                    ramaRestData['Ramas'][r][4] = parm
1543            dlg.Destroy()
1544            UpdateRamaRestr(ramaRestData)
1545
1546        def coeffSizer():
1547            table = []
1548            rowLabels = []
1549            Types = 6*[wg.GRID_VALUE_FLOAT+':10,4',]
1550            colLabels = ['Mag','Pos phi','Pos psi','sig(phi)','sig(psi)','sig(cov)']
1551            for item in coeffDict:
1552                for i,term in enumerate(coeffDict[item]):
1553                    rowLabels.append(item+' term:'+str(i))
1554                    table.append(term)
1555            coeffTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1556            Coeff = G2G.GSGrid(RamaRestr)
1557            Coeff.SetScrollRate(0,10)
1558            Coeff.SetTable(coeffTable, True)
1559            Coeff.AutoSizeColumns(False)
1560            for r in range(Coeff.GetNumberRows()):
1561                for c in range(6):
1562                    Coeff.SetReadOnly(r,c,True)
1563                    Coeff.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1564            Coeff.SetMaxSize((-1,200))
1565            return Coeff
1566                                                   
1567        if RamaRestr.GetSizer(): RamaRestr.GetSizer().Clear(True)
1568        mainSizer = wx.BoxSizer(wx.VERTICAL)
1569        mainSizer.Add((5,5),0)
1570        mainSizer.Add(WtBox(RamaRestr,ramaRestData),0,WACV)
1571        ramaList = ramaRestData['Ramas']
1572        coeffDict = ramaRestData['Coeff']
1573        if len(coeffDict):
1574            mainSizer.Add(wx.StaticText(RamaRestr,-1,'Ramachandran function coefficients:'),0,WACV)
1575            mainSizer.Add(coeffSizer(),1,wx.EXPAND,1)
1576           
1577        if len(ramaList):
1578            mainSizer.Add(wx.StaticText(RamaRestr,-1,'Ramachandran restraints:'),0,WACV)
1579            table = []
1580            rowLabels = []
1581            bad = []
1582            chisq = 0.
1583            Types = 2*[wg.GRID_VALUE_STRING,]+5*[wg.GRID_VALUE_FLOAT+':10,2',]
1584            if 'macro' in General['Type']:
1585                colLabels = ['(res) A  B  C  D  E','coef name','phi','psi','obs E','restr','esd']
1586                for i,[indx,ops,cofName,esd] in enumerate(ramaList):
1587                    try:
1588                        atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
1589                        name = '('+atoms[3][1]+atoms[3][0].strip()+atoms[3][2]+')'
1590                        for atom in atoms:
1591                            name += '  '+atom[3]
1592                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
1593                        phi,psi = G2mth.getRestRama(XYZ,Amat)
1594                        restr,calc = G2mth.calcRamaEnergy(phi,psi,coeffDict[cofName])
1595                        chisq += ramaRestData['wtFactor']*(restr/esd)**2
1596                        table.append([name,cofName,phi,psi,calc,restr,esd])
1597                        rowLabels.append(str(i))
1598                    except KeyError:
1599                        print ('**** WARNING - missing atom - restraint deleted ****')
1600                        bad.append(i)
1601            if len(bad):
1602                bad.reverse()
1603                for ibad in bad:
1604                    del ramaList[ibad]
1605            if len(ramaList):
1606                ramaTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1607                RamaRestr.Ramas = G2G.GSGrid(RamaRestr)
1608                RamaRestr.Ramas.SetTable(ramaTable, True)
1609                RamaRestr.Ramas.AutoSizeColumns(False)
1610                for r in range(len(ramaList)):
1611                    for c in range(6):
1612                        RamaRestr.Ramas.SetReadOnly(r,c,True)
1613                        RamaRestr.Ramas.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1614                RamaRestr.Ramas.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
1615                if 'phoenix' in wx.version():
1616                    RamaRestr.Ramas.Bind(wg.EVT_GRID_CELL_CHANGED, OnCellChange)
1617                else:
1618                    RamaRestr.Ramas.Bind(wg.EVT_GRID_CELL_CHANGE, OnCellChange)
1619                G2frame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2G.wxID_RESTDELETE)
1620                G2frame.Bind(wx.EVT_MENU, OnChangeEsd, id=G2G.wxID_RESTCHANGEESD)
1621                mainSizer.Add(wx.StaticText(RamaRestr,-1,
1622                    'Ramachandran restraints: sum(wt*(delt/sig)^2) =    %.2f, mean(wt*(delt/sig)^2) =    %.2f'    \
1623                    %(chisq,chisq/len(ramaList))),0,WACV)
1624                RamaRestr.Ramas.SetScrollRate(10,10)
1625                RamaRestr.Ramas.SetMinSize((-1,300))
1626                mainSizer.Add(RamaRestr.Ramas,1,wx.EXPAND,1)
1627        else:
1628            mainSizer.Add(wx.StaticText(RamaRestr,-1,'No Ramachandran restraints for this phase'),0,)
1629
1630        G2phsGUI.SetPhaseWindow(RamaRestr,mainSizer,Scroll=0)
1631
1632    def UpdateChemcompRestr(chemcompRestData):
1633       
1634        def OnCellChange(event):
1635            r,c =  event.GetRow(),event.GetCol()
1636            rowLabl = ChemComps.GetRowLabelValue(r)
1637            row = int(rowLabl.split(':')[1])
1638            if 'Restr' in rowLabl:
1639                try:
1640                    new = float(chemcompTable.GetValue(r,c))
1641                    chemcompRestData['Sites'][row][c-2] = new         #obsd or esd
1642                except ValueError:
1643                    pass
1644            else:
1645                try:
1646                    new = float(chemcompTable.GetValue(r,c))
1647                    id = int(rowLabl.split(':')[2])
1648                    chemcompRestData['Sites'][row][1][id] = new     #only factor
1649                except ValueError:
1650                    pass
1651            wx.CallAfter(UpdateChemcompRestr,chemcompRestData)               
1652           
1653        def OnDeleteRestraint(event):
1654            #rows = GetSelectedRows()
1655            rows = ChemComps.GetSelectedRows()
1656            if not rows:
1657                return
1658            rowLabl = ChemComps.GetRowLabelValue(r)
1659            row = int(rowLabl.split(':')[1])
1660            if 'Restr' in rowLabl:
1661                del chemcompList[row]
1662            else:
1663                term = int(rowLabl.split(':')[2])
1664                del chemcompList[row][0][term]
1665                del chemcompList[row][1][term]
1666            UpdateChemcompRestr(chemcompRestData)               
1667           
1668        def OnChangeValue(event):
1669            rows = GetSelectedRows(ChemComps)
1670            if not rows:
1671                return
1672            ChemComps.ClearSelection()
1673            dlg = G2G.SingleFloatDialog(G2frame,'New value',
1674                'Enter new value for restraint multiplier',1.0,[-1.e6,1.e6],'%.2f')
1675            if dlg.ShowModal() == wx.ID_OK:
1676                parm = dlg.GetValue()
1677                for r in rows:
1678                    rowLabl = ChemComps.GetRowLabelValue(r)
1679                    if 'term' in rowLabl:
1680                        items = rowLabl.split(':')
1681                        chemcompRestData['Sites'][int(items[1])][1][int(items[2])] = parm
1682            dlg.Destroy()
1683            UpdateChemcompRestr(chemcompRestData)               
1684
1685        if ChemCompRestr.GetSizer(): ChemCompRestr.GetSizer().Clear(True)
1686        mainSizer = wx.BoxSizer(wx.VERTICAL)
1687        mainSizer.Add((5,5),0)
1688        mainSizer.Add(WtBox(ChemCompRestr,chemcompRestData),0,WACV)
1689        mainSizer.Add(wx.StaticText(ChemCompRestr,-1, 
1690            'NB: The chemical restraint sum is over the unit cell contents'),0,WACV)
1691        mainSizer.Add((5,5),0)
1692
1693        chemcompList = chemcompRestData['Sites']
1694        if len(chemcompList):
1695            table = []
1696            rowLabels = []
1697            bad = []
1698            chisq = 0.
1699            Types = [wg.GRID_VALUE_STRING,]+5*[wg.GRID_VALUE_FLOAT+':10,2',]
1700            colLabels = ['Atoms','mul*frac','factor','calc','target','esd']
1701            for i,[indx,factors,obs,esd] in enumerate(chemcompList):
1702                try:
1703                    atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,ct-1)
1704                    mul = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cs+1))
1705                    frac = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cs-1))
1706                    mulfrac = mul*frac
1707                    calcs = mul*frac*factors
1708                    chisq += chemcompRestData['wtFactor']*((obs-np.sum(calcs))/esd)**2
1709                    for iatm,[atom,mf,fr,clc] in enumerate(zip(atoms,mulfrac,factors,calcs)):
1710                        table.append([atom,mf,fr,clc,'',''])
1711                        rowLabels.append('term:'+str(i)+':'+str(iatm))
1712                    table.append(['Sum','','',np.sum(calcs),obs,esd])
1713                    rowLabels.append('Restr:'+str(i))
1714                except KeyError:
1715                    print ('**** WARNING - missing atom - restraint deleted ****')
1716                    bad.append(i)
1717            if len(bad):
1718                bad.reverse()
1719                for ibad in bad:
1720                    del chemcompList[ibad]
1721            if len(chemcompList):
1722                chemcompTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1723                ChemComps = G2G.GSGrid(ChemCompRestr)
1724                ChemComps.SetTable(chemcompTable, True)
1725                ChemComps.AutoSizeColumns(False)
1726                for r in range(chemcompTable.GetNumberRows()):
1727                    for c in range(2):
1728                        ChemComps.SetReadOnly(r,c,True)
1729                        ChemComps.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1730                    if 'Restr' in ChemComps.GetRowLabelValue(r):
1731                        for c in range(4):
1732                            ChemComps.SetReadOnly(r,c,True)
1733                            ChemComps.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1734                        for c in [1,2]:
1735                            ChemComps.SetCellTextColour(r,c,VERY_LIGHT_GREY)
1736                    else:
1737                        for c in [3,4,5]:
1738                            ChemComps.SetReadOnly(r,c,True)
1739                            ChemComps.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1740                        for c in [4,5]:
1741                            ChemComps.SetCellTextColour(r,c,VERY_LIGHT_GREY)
1742                           
1743                ChemComps.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
1744                if 'phoenix' in wx.version():
1745                    ChemComps.Bind(wg.EVT_GRID_CELL_CHANGED, OnCellChange)
1746                else:
1747                    ChemComps.Bind(wg.EVT_GRID_CELL_CHANGE, OnCellChange)
1748                G2frame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2G.wxID_RESTDELETE)
1749                G2frame.Bind(wx.EVT_MENU, OnChangeValue, id=G2G.wxID_RESRCHANGEVAL)
1750                mainSizer.Add(wx.StaticText(ChemCompRestr,-1,
1751                    'Chemical composition restraints: sum(wt*(delt/sig)^2) =    %.2f, mean(wt*(delt/sig)^2) =    %.2f'    \
1752                    %(chisq,chisq/len(chemcompList))),0,WACV)
1753                mainSizer.Add(ChemComps,0,)
1754            else:
1755                mainSizer.Add(wx.StaticText(ChemCompRestr,-1,'No chemical composition restraints for this phase'),0,)
1756        else:
1757            mainSizer.Add(wx.StaticText(ChemCompRestr,-1,'No chemical composition restraints for this phase'),0,)
1758
1759        G2phsGUI.SetPhaseWindow(ChemCompRestr,mainSizer,Scroll=0)
1760           
1761    def UpdateTextureRestr(textureRestData):
1762           
1763        def OnDeleteRestraint(event):
1764            rows = GetSelectedRows(Textures)
1765            if not rows:
1766                return
1767            rows.sort()
1768            rows.reverse()
1769            for row in rows:
1770                textureList.remove(textureList[row])
1771            wx.CallAfter(UpdateTextureRestr,textureRestData)               
1772           
1773        def OnCellChange(event):
1774            r,c = event.GetRow(),event.GetCol()
1775            try:
1776                if c == 1:  #grid size
1777                    new = int(textureTable.GetValue(r,c))
1778                    if new < 6 or new > 24:
1779                        raise ValueError
1780                elif c in [2,4]:   #esds
1781                    new = float(textureTable.GetValue(r,c))
1782                    if new < -1. or new > 2.:
1783                        raise ValueError
1784                else:
1785                    new = textureTable.GetValue(r,c)
1786                textureRestData['HKLs'][r][c] = new
1787            except ValueError:
1788                pass           
1789            wx.CallAfter(UpdateTextureRestr,textureRestData)               
1790
1791        if TextureRestr.GetSizer(): TextureRestr.GetSizer().Clear(True)
1792        mainSizer = wx.BoxSizer(wx.VERTICAL)
1793        mainSizer.Add((5,5),0)
1794        mainSizer.Add(WtBox(TextureRestr,textureRestData),0,WACV)
1795        mainSizer.Add(wx.StaticText(TextureRestr,-1, 
1796            'NB: The texture restraints suppress negative pole figure values for the selected HKLs\n'
1797            '    "unit esd" gives a bias toward a flatter polefigure'),0,WACV)
1798        mainSizer.Add((5,5),0)
1799
1800        textureList = textureRestData['HKLs']
1801        if len(textureList):
1802            table = []
1803            rowLabels = []
1804            Types = [wg.GRID_VALUE_STRING,wg.GRID_VALUE_LONG,wg.GRID_VALUE_FLOAT+':10,2',
1805                wg.GRID_VALUE_BOOL,wg.GRID_VALUE_FLOAT+':10,2']
1806            colLabels = ['HKL','grid','neg esd','use unit?','unit esd']
1807            for i,[hkl,grid,esd1,ifesd2,esd2] in enumerate(textureList):
1808                table.append(['%d %d %d'%(hkl[0],hkl[1],hkl[2]),grid,esd1,ifesd2,esd2])
1809                rowLabels.append(str(i))
1810            textureTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1811            Textures = G2G.GSGrid(TextureRestr)
1812            Textures.SetTable(textureTable, True)
1813            Textures.AutoSizeColumns(False)
1814            for r in range(len(textureList)):
1815                Textures.SetReadOnly(r,0,True)
1816                Textures.SetCellStyle(r,0,VERY_LIGHT_GREY,True)
1817                if not textureTable.GetValue(r,3):
1818                    Textures.SetReadOnly(r,4,True)
1819                    Textures.SetCellStyle(r,4,VERY_LIGHT_GREY,True)
1820                    Textures.SetCellTextColour(r,4,VERY_LIGHT_GREY)
1821            Textures.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
1822            if 'phoenix' in wx.version():
1823                Textures.Bind(wg.EVT_GRID_CELL_CHANGED, OnCellChange)
1824            else:
1825                Textures.Bind(wg.EVT_GRID_CELL_CHANGE, OnCellChange)
1826            G2frame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2G.wxID_RESTDELETE)
1827            mainSizer.Add(Textures,0,)
1828        else:
1829            mainSizer.Add(wx.StaticText(TextureRestr,-1,'No texture restraints for this phase'),0,)
1830        G2phsGUI.SetPhaseWindow(TextureRestr,mainSizer,Scroll=0)
1831           
1832    def OnPageChanged(event):
1833        page = event.GetSelection()
1834        #G2frame.restrBook.SetSize(G2frame.dataWindow.GetClientSize())    #TODO -almost right
1835        text = G2frame.restrBook.GetPageText(page)
1836        G2frame.dataWindow.RestraintEdit.SetLabel(G2G.wxID_RESRCHANGEVAL,'Change value')
1837        G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_USEMOGUL,False)
1838        if text == 'Bond':
1839            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RestraintMenu)
1840            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESTRAINTADD,True)
1841            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESRCHANGEVAL,True)
1842            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_USEMOGUL,True)
1843            bondRestData = restrData['Bond']
1844            UpdateBondRestr(bondRestData)
1845        elif text == 'Angle':
1846            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RestraintMenu)
1847            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESTRAINTADD,True)
1848            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESRCHANGEVAL,True)
1849            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_USEMOGUL,True)
1850            angleRestData = restrData['Angle']
1851            UpdateAngleRestr(angleRestData)
1852        elif text == 'Plane':
1853            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RestraintMenu)
1854            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESTRAINTADD,True)
1855            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESRCHANGEVAL,False)
1856            planeRestData = restrData['Plane']
1857            UpdatePlaneRestr(planeRestData)
1858        elif text == 'Chiral':
1859            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RestraintMenu)
1860            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESTRAINTADD,False)
1861            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESRCHANGEVAL,True)
1862            chiralRestData = restrData['Chiral']
1863            UpdateChiralRestr(chiralRestData)
1864        elif text == 'Torsion':
1865            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RestraintMenu)
1866            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESTRAINTADD,False)
1867            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESRCHANGEVAL,False)
1868            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_AARESTRAINTPLOT,True)
1869            torsionRestData = restrData['Torsion']
1870            UpdateTorsionRestr(torsionRestData)
1871        elif text == 'Ramachandran':
1872            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RestraintMenu)
1873            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESTRAINTADD,False)
1874            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESRCHANGEVAL,False)
1875            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_AARESTRAINTPLOT,True)
1876            ramaRestData = restrData['Rama']
1877            UpdateRamaRestr(ramaRestData)
1878            wx.CallAfter(G2plt.PlotRama,G2frame,phaseName,rama,ramaName)
1879        elif text == 'Chem. comp.':
1880            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RestraintMenu)
1881            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESTRAINTADD,True)
1882            G2frame.dataWindow.RestraintEdit.SetLabel(G2G.wxID_RESRCHANGEVAL,'Change factor')
1883            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESRCHANGEVAL,True)
1884            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESTCHANGEESD,False)
1885            chemcompRestData = restrData['ChemComp']
1886            UpdateChemcompRestr(chemcompRestData)
1887        elif text == 'Texture':
1888            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RestraintMenu)
1889            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESTRAINTADD,True)
1890            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESRCHANGEVAL,True)
1891            textureRestData = restrData['Texture']
1892            UpdateTextureRestr(textureRestData)
1893        event.Skip()
1894
1895#    def RaisePage(event):
1896#        'Respond to a "select tab" menu button'
1897#        # class PseudoEvent(object):
1898#        #     def __init__(self,page): self.page = page
1899#        #     def Skip(self): pass
1900#        #     def GetSelection(self): return self.page
1901#        try:
1902#            i = tabIndex.get(event.GetId())
1903#            G2frame.restrBook.SetSelection(i)
1904#            #OnPageChanged(PseudoEvent(i))
1905#        except ValueError:
1906#            print('Unexpected event in RaisePage')
1907#
1908    def OnSelectPage(event):
1909        'Called when an item is selected from the Select page menu'
1910        # lookup the menu item that called us and get its text
1911        tabname = TabSelectionIdDict.get(event.GetId())
1912        if not tabname:
1913            print ('Warning: menu item not in dict! id= %d'%event.GetId())
1914            return
1915        # find the matching tab
1916        for PageNum in range(G2frame.restrBook.GetPageCount()):
1917            if tabname == G2frame.restrBook.GetPageText(PageNum):
1918                G2frame.restrBook.SetSelection(PageNum)
1919                return
1920        else:
1921            print ("Warning: tab "+tabname+" was not found")
1922
1923    # UpdateRestraints execution starts here
1924    try:
1925        phasedata = G2frame.GetPhaseData()[phaseName]
1926    except KeyError:        #delete unknown or previously deleted phases from Restraints
1927        rId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Restraints')
1928        pId = G2gd.GetGPXtreeItemId(G2frame,rId,phaseName)
1929        G2frame.GPXtree.Delete(pId)
1930        print('Unknown phase '+phaseName+' is deleted from Restraints')
1931        return
1932    restrData = data[phaseName]
1933    if 'Bond' not in restrData:
1934        restrData['Bond'] = {'wtFactor':1.0,'Range':1.1,'Bonds':[],'Use':True}
1935    if 'Angle' not in restrData:
1936        restrData['Angle'] = {'wtFactor':1.0,'Range':0.85,'Angles':[],'Use':True}
1937    if 'Plane' not in restrData:
1938        restrData['Plane'] = {'wtFactor':1.0,'Planes':[],'Use':True}
1939    if 'Chiral' not in restrData:
1940        restrData['Chiral'] = {'wtFactor':1.0,'Volumes':[],'Use':True}
1941    if 'Torsion' not in restrData:
1942        restrData['Torsion'] = {'wtFactor':1.0,'Coeff':{},'Torsions':[],'Use':True}
1943    if 'Rama' not in restrData:
1944        restrData['Rama'] = {'wtFactor':1.0,'Coeff':{},'Ramas':[],'Use':True}
1945    if 'Texture' not in restrData:
1946        restrData['Texture'] = {'wtFactor':1.0,'HKLs':[],'Use':True}
1947    if 'ChemComp' not in restrData:
1948        restrData['ChemComp'] = {'wtFactor':1.0,'Sites':[],'Use':True}
1949    General = phasedata['General']
1950    Cell = General['Cell'][1:7]          #skip flag & volume   
1951    Amat,Bmat = G2lat.cell2AB(Cell)
1952    SGData = General['SGData']
1953    cx,ct,cs,cia = General['AtomPtrs']
1954    Atoms = phasedata['Atoms']
1955    AtLookUp = G2mth.FillAtomLookUp(Atoms,cia+8)
1956    if 'macro' in General['Type']:
1957        Names = [atom[0]+':'+atom[1]+atom[2]+' '+atom[3].ljust(4) for atom in Atoms]
1958        Ids = []
1959        Coords = []
1960        Types = []
1961    else:   
1962        Names = ['all '+ name for name in General['AtomTypes']]
1963        iBeg = len(Names)
1964        Types = [name for name in General['AtomTypes']]
1965        Coords = [ [] for type in Types]
1966        Ids = [ 0 for type in Types]
1967        Names += [atom[ct-1] for atom in Atoms]
1968    Types += [atom[ct] for atom in Atoms]
1969    Coords += [atom[cx:cx+3] for atom in Atoms]
1970    Ids += [atom[cia+8] for atom in Atoms]
1971    rama = G2data.ramachandranDist['All']
1972    ramaName = 'All'
1973    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RestraintMenu)   
1974    G2frame.Bind(wx.EVT_MENU, OnAddRestraint, id=G2G.wxID_RESTRAINTADD)
1975    G2frame.Bind(wx.EVT_MENU, OnUseMogul, id=G2G.wxID_USEMOGUL)
1976    G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_USEMOGUL,True)
1977    if 'macro' in phasedata['General']['Type']:
1978        G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_AARESTRAINTADD,True)
1979        G2frame.Bind(wx.EVT_MENU, OnAddAARestraint, id=G2G.wxID_AARESTRAINTADD)
1980        G2frame.Bind(wx.EVT_MENU, OnPlotAARestraint, id=G2G.wxID_AARESTRAINTPLOT)
1981   
1982    # GUI defined here
1983    G2frame.restrBook = G2G.GSNoteBook(parent=G2frame.dataWindow)
1984    G2frame.dataWindow.GetSizer().Add(G2frame.restrBook,1,wx.ALL|wx.EXPAND,1)
1985    # clear menu and menu pointers
1986    Pages = []   
1987
1988    txt = 'Bond'
1989    BondRestr = wx.ScrolledWindow(G2frame.restrBook)
1990    G2frame.restrBook.AddPage(BondRestr,txt)
1991    Pages.append(txt)
1992
1993    txt = 'Angle'
1994    AngleRestr = wx.ScrolledWindow(G2frame.restrBook)
1995    G2frame.restrBook.AddPage(AngleRestr,txt) 
1996    Pages.append(txt)
1997   
1998    txt = 'Plane'
1999    PlaneRestr = wx.ScrolledWindow(G2frame.restrBook)
2000    G2frame.restrBook.AddPage(PlaneRestr,txt)
2001    Pages.append(txt)
2002
2003    txt = 'Chiral'
2004    ChiralRestr = wx.ScrolledWindow(G2frame.restrBook)
2005    G2frame.restrBook.AddPage(ChiralRestr,txt)
2006    Pages.append(txt)
2007
2008    if 'macro' in General['Type']:
2009        txt = 'Torsion'
2010        TorsionRestr = wx.ScrolledWindow(G2frame.restrBook)
2011        G2frame.restrBook.AddPage(TorsionRestr,txt)
2012        Pages.append(txt)
2013
2014        txt = 'Ramachandran'
2015        RamaRestr = wx.ScrolledWindow(G2frame.restrBook)
2016        G2frame.restrBook.AddPage(RamaRestr,txt)
2017        Pages.append(txt)
2018
2019    txt = 'Chem. comp.'
2020    ChemCompRestr = wx.ScrolledWindow(G2frame.restrBook)
2021    G2frame.restrBook.AddPage(ChemCompRestr,txt)
2022    Pages.append(txt)
2023   
2024    if General['SH Texture']['Order']:
2025        txt = 'Texture'
2026        TextureRestr = wx.ScrolledWindow(G2frame.restrBook)
2027        G2frame.restrBook.AddPage(TextureRestr,txt)
2028        Pages.append(txt)
2029
2030    UpdateBondRestr(restrData['Bond'])
2031
2032    G2frame.restrBook.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged)
2033
2034    # fill page selection menu
2035    menu = G2frame.dataWindow.RestraintTab
2036    for page in Pages:
2037        if menu.FindItem(page) >= 0: continue # is tab already in menu?
2038        Id = wx.NewId()
2039        TabSelectionIdDict[Id] = page
2040        menu.Append(Id,page,'')
2041        G2frame.Bind(wx.EVT_MENU, OnSelectPage, id=Id)
Note: See TracBrowser for help on using the repository browser.