source: trunk/GSASIIrestrGUI.py @ 1831

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

move remaining generic controls to G2ctrls

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