source: trunk/GSASIIrestrGUI.py @ 1214

Last change on this file since 1214 was 1214, checked in by toby, 8 years ago

fix use of RB vars in G2VarObj, etc.; Add select tab to restraints

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