source: trunk/GSASIIrestrGUI.py @ 3356

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

remove commented out SetTitle? & SetLabel? for data window from various places. Title is now gpx file name.
reconfigure restraints to show phase name subentries in tree - selection done from tree.
import phase, add phase & delete phase all handle restraints in new scheme
use of restraints unaffected as restraint data layout unchanged

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