source: trunk/GSASIIrestrGUI.py @ 2846

Last change on this file since 2846 was 2840, checked in by vondreele, 8 years ago

fix PDB importer & reimport atoms
rework RB GUI - now shows just one residue at a time & structure plot follows selection
also RB parameter changes show on structure drawing
add protein validator - not complete
remove commented out dead code from GSAIIgrid
fix bug if cancel of macro restraints selection

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