source: trunk/GSASIIrestrGUI.py @ 2450

Last change on this file since 2450 was 2431, checked in by vondreele, 9 years ago

fix problem with wxPython 3.x with changing text entry focus. Needed a event.Skip() in every TextCtrl? event handler. Hope I got them all...

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