source: trunk/GSASIIrestrGUI.py @ 3355

Last change on this file since 3355 was 3355, checked in by toby, 5 years ago

name main & plot window against project name, remove other SetTitle? calls

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