source: trunk/GSASIIrestrGUI.py @ 3826

Last change on this file since 3826 was 3826, checked in by vondreele, 3 years ago

fix future problem with wx.NewId? --> wx.NewIdRef?

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