source: trunk/GSASIIrestrGUI.py @ 2546

Last change on this file since 2546 was 2546, checked in by vondreele, 5 years ago

cleanup of all GSASII main routines - remove unused variables, dead code, etc.

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