source: trunk/GSASIIrestrGUI.py @ 4908

Last change on this file since 4908 was 4908, checked in by toby, 5 months ago

fix deleting of restraints

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