source: trunk/GSASIIconstrGUI.py @ 3054

Last change on this file since 3054 was 3054, checked in by vondreele, 4 years ago

fixes to MCSA gui routines; trap <3 atom RBs
fix missing AtLookup? error in LeBail? refinements

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 92.4 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIconstrGUI - constraint GUI routines
3########### SVN repository information ###################
4# $Date: 2017-09-07 21:13:57 +0000 (Thu, 07 Sep 2017) $
5# $Author: vondreele $
6# $Revision: 3054 $
7# $URL: trunk/GSASIIconstrGUI.py $
8# $Id: GSASIIconstrGUI.py 3054 2017-09-07 21:13:57Z vondreele $
9########### SVN repository information ###################
10'''
11*GSASIIconstrGUI: Constraint GUI routines*
12------------------------------------------
13
14Used to define constraints and rigid bodies.
15
16'''
17import sys
18import wx
19import wx.grid as wg
20import random as ran
21import numpy as np
22import numpy.ma as ma
23import numpy.linalg as nl
24import os.path
25import GSASIIpath
26GSASIIpath.SetVersionNumber("$Revision: 3054 $")
27import GSASIIElem as G2elem
28import GSASIIElemGUI as G2elemGUI
29import GSASIIstrIO as G2stIO
30import GSASIImapvars as G2mv
31import GSASIImath as G2mth
32import GSASIIlattice as G2lat
33import GSASIIdataGUI as G2gd
34import GSASIIctrlGUI as G2G
35import GSASIIplot as G2plt
36import GSASIIobj as G2obj
37import GSASIIspc as G2spc
38VERY_LIGHT_GREY = wx.Colour(235,235,235)
39
40class MultiIntegerDialog(wx.Dialog):
41    '''Input a series of integers based on prompts
42    '''
43    def __init__(self,parent,title,prompts,values):
44        wx.Dialog.__init__(self,parent,-1,title, 
45            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
46        self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
47        self.values = values
48        self.prompts = prompts
49        self.Draw()
50       
51    def Draw(self):
52       
53        def OnValItem(event):
54            event.Skip()
55            Obj = event.GetEventObject()
56            ind = Indx[Obj.GetId()]
57            try:
58                val = int(Obj.GetValue())
59                if val <= 0:
60                    raise ValueError
61            except ValueError:
62                val = self.values[ind]
63            self.values[ind] = val
64            Obj.SetValue('%d'%(val))
65           
66        self.panel.Destroy()
67        self.panel = wx.Panel(self)
68        mainSizer = wx.BoxSizer(wx.VERTICAL)
69        Indx = {}
70        for ival,[prompt,value] in enumerate(zip(self.prompts,self.values)):
71            mainSizer.Add(wx.StaticText(self.panel,-1,prompt),0,wx.ALIGN_CENTER)
72            valItem = wx.TextCtrl(self.panel,-1,value='%d'%(value),style=wx.TE_PROCESS_ENTER)
73            mainSizer.Add(valItem,0,wx.ALIGN_CENTER)
74            Indx[valItem.GetId()] = ival
75            valItem.Bind(wx.EVT_TEXT_ENTER,OnValItem)
76            valItem.Bind(wx.EVT_KILL_FOCUS,OnValItem)
77        OkBtn = wx.Button(self.panel,-1,"Ok")
78        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
79        CancelBtn = wx.Button(self.panel,-1,'Cancel')
80        CancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
81        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
82        btnSizer.Add((20,20),1)
83        btnSizer.Add(OkBtn)
84        btnSizer.Add(CancelBtn)
85        btnSizer.Add((20,20),1)
86        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
87        self.panel.SetSizer(mainSizer)
88        self.panel.Fit()
89        self.Fit()
90
91    def GetValues(self):
92        return self.values
93       
94    def OnOk(self,event):
95        parent = self.GetParent()
96        parent.Raise()
97        self.EndModal(wx.ID_OK)             
98       
99    def OnCancel(self,event):
100        parent = self.GetParent()
101        parent.Raise()
102        self.EndModal(wx.ID_CANCEL)
103
104class ConstraintDialog(wx.Dialog):
105    '''Window to edit Constraint values
106    '''
107    def __init__(self,parent,title,text,data,separator='*',varname="",varyflag=False):
108        wx.Dialog.__init__(self,parent,-1,'Edit '+title, 
109            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
110        self.data = data[:]
111        self.newvar = [varname,varyflag]
112        panel = wx.Panel(self)
113        mainSizer = wx.BoxSizer(wx.VERTICAL)
114        topLabl = wx.StaticText(panel,-1,text)
115        mainSizer.Add((10,10),1)
116        mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
117        mainSizer.Add((10,10),1)
118        dataGridSizer = wx.FlexGridSizer(cols=3,hgap=2,vgap=2)
119        self.OkBtn = wx.Button(panel,wx.ID_OK)
120        for id in range(len(self.data)):
121            lbl1 = lbl = str(self.data[id][1])
122            if lbl[-1] != '=': lbl1 = lbl + ' ' + separator + ' '
123            name = wx.StaticText(panel,wx.ID_ANY,lbl1,
124                                 style=wx.ALIGN_RIGHT)
125            scale = G2G.ValidatedTxtCtrl(panel,self.data[id],0,
126                                          typeHint=float,
127                                          OKcontrol=self.DisableOK)
128            dataGridSizer.Add(name,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,5)
129            dataGridSizer.Add(scale,0,wx.RIGHT,3)
130            if ':' in lbl:
131                dataGridSizer.Add(
132                    wx.StaticText(panel,-1,G2obj.fmtVarDescr(lbl)),
133                    0,wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,3)
134            else:
135                dataGridSizer.Add((-1,-1))
136        if title == 'New Variable':
137            name = wx.StaticText(panel,wx.ID_ANY,"New variable's\nname (optional)",
138                                 style=wx.ALIGN_CENTER)
139            scale = G2G.ValidatedTxtCtrl(panel,self.newvar,0,
140                                          typeHint=str,notBlank=False)
141            dataGridSizer.Add(name,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,5)
142            dataGridSizer.Add(scale,0,wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,3)
143            self.refine = wx.CheckBox(panel,label='Refine?')
144            self.refine.SetValue(self.newvar[1]==True)
145            self.refine.Bind(wx.EVT_CHECKBOX, self.OnCheckBox)
146            dataGridSizer.Add(self.refine,0,wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,3)
147           
148        mainSizer.Add(dataGridSizer,0,wx.EXPAND)
149        self.OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
150        self.OkBtn.SetDefault()
151        cancelBtn = wx.Button(panel,wx.ID_CANCEL)
152        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
153        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
154        btnSizer.Add((20,20),1)
155        btnSizer.Add(self.OkBtn)
156        btnSizer.Add((20,20),1)
157        btnSizer.Add(cancelBtn)
158        btnSizer.Add((20,20),1)
159
160        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
161        panel.SetSizer(mainSizer)
162        panel.Fit()
163        self.Fit()
164        self.CenterOnParent()
165       
166    def DisableOK(self,setting):
167        if setting:
168            self.OkBtn.Enable()
169        else:
170            self.OkBtn.Disable()
171
172    def OnCheckBox(self,event):
173        self.newvar[1] = self.refine.GetValue()
174
175    def OnOk(self,event):
176        parent = self.GetParent()
177        parent.Raise()
178        self.EndModal(wx.ID_OK)             
179
180    def OnCancel(self,event):
181        parent = self.GetParent()
182        parent.Raise()
183        self.EndModal(wx.ID_CANCEL)             
184
185    def GetData(self):
186        return self.data
187       
188################################################################################
189#####  Constraints
190################################################################################           
191       
192def UpdateConstraints(G2frame,data):
193    '''Called when Constraints tree item is selected.
194    Displays the constraints in the data window
195    '''
196    if not data:
197        data.update({'Hist':[],'HAP':[],'Phase':[],'Global':[]})       #empty dict - fill it
198    if 'Global' not in data:                                            #patch
199        data['Global'] = []
200    # DEBUG code ########################################
201    #import GSASIIconstrGUI
202    #reload(GSASIIconstrGUI)
203    #reload(G2obj)
204    #reload(G2stIO)
205    #import GSASIIstrMain
206    #reload(GSASIIstrMain)   
207    #reload(G2mv)
208    #reload(G2gd)
209    ###################################################
210    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
211    G2obj.IndexAllIds(Histograms,Phases)
212    ##################################################################################
213    # patch: convert old-style (str) variables in constraints to G2VarObj objects
214    for key,value in data.items():
215        if key.startswith('_'): continue
216        j = 0
217        for cons in value:
218            #print cons             # DEBUG
219            for i in range(len(cons[:-3])):
220                if type(cons[i][1]) is str:
221                    cons[i][1] = G2obj.G2VarObj(cons[i][1])
222                    j += 1
223        if j:
224            print str(key) + ': '+str(j)+' variable(s) as strings converted to objects'
225    ##################################################################################
226    rigidbodyDict = G2frame.GPXtree.GetItemPyData(
227        G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Rigid bodies'))
228    rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
229    rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
230    badPhaseParms = ['Ax','Ay','Az','Amul','AI/A','Atype','SHorder','mV0','mV1','mV2','waveType','Vol','isMag',]
231    globalList = rbDict.keys()
232    globalList.sort()
233    try:
234        AtomDict = dict([Phases[phase]['pId'],Phases[phase]['Atoms']] for phase in Phases)
235    except KeyError:
236        G2frame.ErrorDialog('Constraint Error','You must run least squares at least once before setting constraints\n'+ \
237            'We suggest you refine scale factor first')
238        return
239
240    # create a list of the phase variables
241    Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable,MFtable,maxSSwave = G2stIO.GetPhaseData(Phases,rbIds=rbIds,Print=False)
242    phaseList = []
243    for item in phaseDict:
244        if item.split(':')[2] not in badPhaseParms:
245            phaseList.append(item)
246    phaseList.sort()
247    phaseAtNames = {}
248    phaseAtTypes = {}
249    TypeList = []
250    for item in phaseList:
251        Split = item.split(':')
252        if Split[2][:2] in ['AU','Af','dA','AM']:
253            Id = int(Split[0])
254            phaseAtNames[item] = AtomDict[Id][int(Split[3])][0]
255            phaseAtTypes[item] = AtomDict[Id][int(Split[3])][1]
256            if phaseAtTypes[item] not in TypeList:
257                TypeList.append(phaseAtTypes[item])
258        else:
259            phaseAtNames[item] = ''
260            phaseAtTypes[item] = ''
261             
262    # create a list of the hist*phase variables; include wildcards here
263    hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
264    hapList = [i for i in hapDict.keys() if i.split(':')[2] not in ('Type',)]
265    hapList.sort()
266    wildList = [] # list of variables with "*" for histogram number
267    for i in hapList:
268        s = i.split(':')
269        if s[1] == "": continue
270        s[1] = '*'
271        sj = ':'.join(s)
272        if sj not in wildList: wildList.append(sj)
273    #wildList.sort() # unneeded
274    hapList += wildList
275    histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
276    histList = []
277    for item in histDict:
278        if item.split(':')[2] not in ['Omega','Type','Chi','Phi',
279                                      'Azimuth','Gonio. radius',
280                                      'Lam1','Lam2','Back','Temperature','Pressure',
281                                      'FreePrm1','FreePrm2','FreePrm3',
282                                      ]:
283            histList.append(item)
284    histList.sort()
285    wildList = []
286    # for i in histList: # any reason to have this for hist constraints?
287    #     s = i.split(':')
288    #     if s[1] == "": continue
289    #     s[1] = '*'
290    #     sj = ':'.join(s)
291    #     if sj not in wildList: wildList.append(sj)
292    # histList += wildList
293    Indx = {}
294    G2frame.Page = [0,'phs']
295       
296    def FindEquivVarb(name,nameList):
297        'Creates a list of variables appropriate to constrain with name'
298        outList = []
299        #phlist = []
300        items = name.split(':')
301        namelist = [items[2],]
302        if 'dA' in name:
303            namelist = ['dAx','dAy','dAz']
304        elif 'AU' in name:
305            namelist = ['AUiso','AU11','AU22','AU33','AU12','AU13','AU23']
306        elif 'AM' in name:
307            namelist = ['AMx','AMy','AMz']
308        elif items[-1] in ['A0','A1','A2','A3','A4','A5']:
309            namelist = ['A0','A1','A2','A3','A4','A5']
310        elif items[-1] in ['D11','D22','D33','D12','D13','D23']:
311            namelist = ['D11','D22','D33','D12','D13','D23']
312        elif 'Tm' in name:
313            namelist = ['Tmin','Tmax']
314        elif 'RB' in name:
315            rbfx = 'RB'+items[2][2]
316            if 'T' in name and 'Tr' not in name:
317                namelist = [rbfx+'T11',rbfx+'T22',rbfx+'T33',rbfx+'T12',rbfx+'T13',rbfx+'T23']
318            if 'L' in name:
319                namelist = [rbfx+'L11',rbfx+'L22',rbfx+'L33',rbfx+'L12',rbfx+'L13',rbfx+'L23']
320            if 'S' in name:
321                namelist = [rbfx+'S12',rbfx+'S13',rbfx+'S21',rbfx+'S23',rbfx+'S31',rbfx+'S32',rbfx+'SAA',rbfx+'SBB']
322            if 'U' in name:
323                namelist = [rbfx+'U',]
324
325        for item in nameList:
326            keys = item.split(':')
327            #if keys[0] not in phlist:
328            #    phlist.append(keys[0])
329            if items[1] == '*' and keys[2] in namelist: # wildcard -- select only sequential options
330                keys[1] = '*'
331                mitem = ':'.join(keys)
332                if mitem == name: continue
333                if mitem not in outList: outList.append(mitem)
334            elif keys[2] in namelist and item != name:
335                outList.append(item)
336        return outList
337       
338    def SelectVarbs(page,FrstVarb,varList,legend,constType):
339        '''Select variables used in constraints after one variable has
340        been selected. This routine determines the appropriate variables to be
341        used based on the one that has been selected and asks for more to be added.
342
343        It then creates the constraint and adds it to the constraints list.
344       
345        Called from OnAddEquivalence, OnAddFunction & OnAddConstraint (all but
346        OnAddHold)
347
348        :param list page: defines calling page -- type of variables to be used
349        :parm GSASIIobj.G2VarObj FrstVarb: reference to first selected variable
350        :param list varList: list of other appropriate variables to select from
351        :param str legend: header for selection dialog
352        :param str constType: type of constraint to be generated
353        :returns: a constraint, as defined in
354          :ref:`GSASIIobj <Constraint_definitions_table>`
355        '''
356        choices = [[i]+list(G2obj.VarDescr(i)) for i in varList]
357        meaning = G2obj.getDescr(FrstVarb.name)
358        if not meaning:
359            meaning = "(no definition found!)"
360        l = str(FrstVarb).split(':')
361        # make lists of phases & histograms to iterate over
362        phaselist = [l[0]]
363        if l[0]:
364            phaselbl = ['phase #'+l[0]]
365            if len(Phases) > 1:
366                phaselist += ['all'] 
367                phaselbl += ['all phases']
368        else:
369            phaselbl = ['']
370        histlist = [l[1]]
371        if l[1] == '*':
372            pass
373        elif l[1]:
374            histlbl = ['histogram #'+l[1]]
375            if len(Histograms) > 1:
376                histlist += ['all']
377                histlbl += ['all histograms']
378                typ = Histograms[G2obj.LookupHistName(l[1])[0]]['Instrument Parameters'][0]['Type'][1]
379                i = 0
380                for hist in Histograms:
381                    if Histograms[hist]['Instrument Parameters'][0]['Type'][1] == typ: i += 1
382                if i > 1:
383                    histlist += ['all='+typ]
384                    histlbl += ['all '+typ+' histograms']
385        else:
386            histlbl = ['']
387        # make a list of equivalent parameter names
388        nameList = [FrstVarb.name]
389        for var in varList:
390            nam = var.split(":")[2]
391            if nam not in nameList: nameList += [nam]
392        # add "wild-card" names to the list of variables
393        if l[1] == '*':
394            pass
395        elif page[1] == 'phs':
396            if 'RB' in FrstVarb.name:
397                pass
398            elif FrstVarb.atom is None:
399                for nam in nameList:
400                    for ph,plbl in zip(phaselist,phaselbl):
401                        if plbl: plbl = 'For ' + plbl
402                        var = ph+"::"+nam
403                        if var == str(FrstVarb) or var in varList: continue
404                        varList += [var]
405                        choices.append([var,plbl,meaning])
406            else:
407                for nam in nameList:
408                    for ph,plbl in zip(phaselist,phaselbl):
409                        if plbl: plbl = ' in ' + plbl
410                        for atype in ['']+TypeList:
411                            if atype:
412                                albl = "For "+atype+" atoms"
413                                akey = "all="+atype                       
414                            else:
415                                albl = "For all atoms"
416                                akey = "all"
417                            var = ph+"::"+nam+":"+akey
418                            if var == str(FrstVarb) or var in varList: continue
419                            varList += [var]
420                            choices.append([var,albl+plbl,meaning])
421        elif page[1] == 'hap':
422            if FrstVarb.name == "Scale":
423                meaning = "Phase fraction"
424            for nam in nameList:
425                for ph,plbl in zip(phaselist,phaselbl):
426                    if plbl: plbl = 'For ' + plbl
427                    for hst,hlbl in zip(histlist,histlbl):
428                        if hlbl:
429                            if plbl:
430                                hlbl = ' in ' + hlbl
431                            else:
432                                hlbl = 'For ' + hlbl                               
433                            var = ph+":"+hst+":"+nam
434                            if var == str(FrstVarb) or var in varList: continue
435                            varList += [var]
436                            choices.append([var,plbl+hlbl,meaning])
437        elif page[1] == 'hst':
438            if FrstVarb.name == "Scale":
439                meaning = "Scale factor"
440            for nam in nameList:
441                for hst,hlbl in zip(histlist,histlbl):
442                    if hlbl:
443                        hlbl = 'For ' + hlbl                               
444                        var = ":"+hst+":"+nam
445                        if var == str(FrstVarb) or var in varList: continue
446                        varList += [var]
447                        choices.append([var,hlbl,meaning])
448        elif page[1] == 'glb':
449            pass
450        else:
451            raise Exception, 'Unknown constraint page '+ page[1]                   
452        if len(choices):
453            l1 = l2 = 1
454            for i1,i2,i3 in choices:
455                l1 = max(l1,len(i1))
456                l2 = max(l2,len(i2))
457            fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}"
458            atchoice = [fmt.format(*i1) for i1 in choices]
459            dlg = G2G.G2MultiChoiceDialog(
460                G2frame,legend,
461                'Constrain '+str(FrstVarb)+' with...',atchoice,
462                toggle=False,size=(625,400),monoFont=True)
463            dlg.CenterOnParent()
464            res = dlg.ShowModal()
465            Selections = dlg.GetSelections()[:]
466            dlg.Destroy()
467            if res != wx.ID_OK: return []
468            if len(Selections) == 0:
469                dlg = wx.MessageDialog(
470                    G2frame,
471                    'No variables were selected to include with '+str(FrstVarb),
472                    'No variables')
473                dlg.CenterOnParent()
474                dlg.ShowModal()
475                dlg.Destroy()
476                return []
477        else:
478            dlg = wx.MessageDialog(
479                G2frame,
480                'There are no appropriate variables to include with '+str(FrstVarb),
481                'No variables')
482            dlg.CenterOnParent()
483            dlg.ShowModal()
484            dlg.Destroy()
485            return []
486        # now process the variables provided by the user
487        varbs = [str(FrstVarb),] # list of selected variables
488        for sel in Selections:
489            var = varList[sel]
490            # phase(s) included
491            l = var.split(':')
492            if l[0] == "all":
493                phlist = [str(Phases[phase]['pId']) for phase in Phases]
494            else:
495                phlist = [l[0]]
496            # histogram(s) included
497            if l[1] == "all":
498                hstlist = [str(Histograms[hist]['hId']) for hist in Histograms]
499            elif '=' in l[1]:
500                htyp = l[1].split('=')[1]
501                hstlist = [str(Histograms[hist]['hId']) for hist in Histograms if 
502                           Histograms[hist]['Instrument Parameters'][0]['Type'][1] == htyp]
503            else:
504                hstlist = [l[1]]
505            if len(l) == 3:
506                for ph in phlist:
507                    for hst in hstlist:
508                        var = ph + ":" + hst + ":" + l[2]
509                        if var in varbs: continue
510                        varbs.append(var)
511            else: # constraints with atoms or rigid bodies
512                if len(l) == 5: # rigid body parameter
513                    var = ':'.join(l)
514                    if var in varbs: continue
515                    varbs.append(var)
516                elif l[3] == "all":
517                    for ph in phlist:
518                        key = G2obj.LookupPhaseName(l[0])[0]
519                        for hst in hstlist: # should be blank
520                            for iatm,at in enumerate(Phases[key]['Atoms']):
521                                var = ph + ":" + hst + ":" + l[2] + ":" + str(iatm)
522                                if var in varbs: continue
523                                varbs.append(var)
524                elif '=' in l[3]:
525                    for ph in phlist:
526                        key = G2obj.LookupPhaseName(l[0])[0]
527                        cx,ct,cs,cia = Phases[key]['General']['AtomPtrs']
528                        for hst in hstlist: # should be blank
529                            atyp = l[3].split('=')[1]
530                            for iatm,at in enumerate(Phases[key]['Atoms']):
531                                if at[ct] != atyp: continue
532                                var = ph + ":" + hst + ":" + l[2] + ":" + str(iatm)
533                                if var in varbs: continue
534                                varbs.append(var)
535                else:
536                    for ph in phlist:
537                        key = G2obj.LookupPhaseName(l[0])[0]
538                        for hst in hstlist: # should be blank
539                            var = ph + ":" + hst + ":" + l[2] + ":" + l[3]
540                            if var in varbs: continue
541                            varbs.append(var)
542        if len(varbs) >= 1 or 'constraint' in constType:
543            constr = [[1.0,FrstVarb]]
544            for item in varbs[1:]:
545                constr += [[1.0,G2obj.G2VarObj(item)]]
546            if 'equivalence' in constType:
547                return [constr+[None,None,'e']]
548            elif 'function' in constType:
549                return [constr+[None,False,'f']]
550            elif 'constraint' in constType:
551                return [constr+[1.0,None,'c']]
552            else:
553                raise Exception,'Unknown constraint type: '+str(constType)
554        else:
555            dlg = wx.MessageDialog(
556                G2frame,
557                'There are no selected variables to include with '+str(FrstVarb),
558                'No variables')
559            dlg.CenterOnParent()
560            dlg.ShowModal()
561            dlg.Destroy()
562        return []
563
564    def CheckAddedConstraint(newcons):
565        '''Check a new constraint that has just been input.
566        If there is an error display a message and give the user a
567        choice to keep or discard the last entry (why keep? -- they
568        may want to delete something else or edit multipliers).
569        Since the varylist is not available, no warning messages
570        should be generated.
571
572        :returns: True if constraint should be added
573        '''
574        allcons = []
575        for key in data:
576            if key.startswith('_'): continue
577            allcons += data[key]
578        allcons += newcons
579        if not len(allcons): return True
580        G2mv.InitVars()   
581        constDictList,fixedList,ignored = G2stIO.ProcessConstraints(allcons)
582        errmsg, warnmsg = G2mv.CheckConstraints('',constDictList,fixedList)
583        if errmsg:
584            res = G2frame.ErrorDialog('Constraint Error',
585                'Error with newly added constraint:\n'+errmsg+
586                '\n\nDiscard newly added constraint?',parent=G2frame,
587                wtype=wx.YES_NO)
588            return res != wx.ID_YES
589        elif warnmsg:
590            print 'Unexpected contraint warning:\n',warnmsg
591        return True
592
593    def CheckChangedConstraint():
594        '''Check all constraints after an edit has been made.
595        If there is an error display a message and give the user a
596        choice to keep or discard the last edit.
597        Since the varylist is not available, no warning messages
598        should be generated.
599       
600        :returns: True if the edit should be retained
601        '''
602        allcons = []
603        for key in data:
604            if key.startswith('_'): continue
605            allcons += data[key]
606        if not len(allcons): return True
607        G2mv.InitVars()   
608        constDictList,fixedList,ignored = G2stIO.ProcessConstraints(allcons)
609        errmsg, warnmsg = G2mv.CheckConstraints('',constDictList,fixedList)
610        if errmsg:
611            res = G2frame.ErrorDialog('Constraint Error',
612                'Error after editing constraint:\n'+errmsg+
613                '\n\nDiscard last constraint edit?',parent=G2frame,
614                wtype=wx.YES_NO)
615            return res != wx.ID_YES
616        elif warnmsg:
617            print 'Unexpected contraint warning:\n',warnmsg
618        return True
619             
620    def PageSelection(page):
621        'Decode page reference'
622        if page[1] == "phs":
623            vartype = "phase"
624            varList = phaseList
625            constrDictEnt = 'Phase'
626        elif page[1] == "hap":
627            vartype = "Histogram*Phase"
628            varList = hapList
629            constrDictEnt = 'HAP'
630        elif page[1] == "hst":
631            vartype = "Histogram"
632            varList = histList
633            constrDictEnt = 'Hist'
634        elif page[1] == "glb":
635            vartype = "Global"
636            varList = globalList
637            constrDictEnt = 'Global'
638        else:
639            raise Exception,'Should not happen!'
640        return vartype,varList,constrDictEnt
641
642    def OnAddHold(event):
643        '''Create a new Hold constraint
644
645        Hold constraints allows the user to select one variable (the list of available
646        variables depends on which tab is currently active).
647        '''
648        page = G2frame.Page
649        vartype,varList,constrDictEnt = PageSelection(page)
650        title1 = "Hold "+vartype+" variable"
651        if not varList:
652            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
653                parent=G2frame)
654            return
655        l2 = l1 = 1
656        for i in varList:
657            l1 = max(l1,len(i))
658            loc,desc = G2obj.VarDescr(i)
659            l2 = max(l2,len(loc))
660        fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}"
661        varListlbl = [fmt.format(i,*G2obj.VarDescr(i)) for i in varList]
662        #varListlbl = ["("+i+") "+G2obj.fmtVarDescr(i) for i in varList]
663        legend = "Select variables to hold (Will not be varied, even if vary flag is set)"
664        dlg = G2G.G2MultiChoiceDialog(G2frame,
665            legend,title1,varListlbl,toggle=False,size=(625,400),monoFont=True)
666        dlg.CenterOnParent()
667        if dlg.ShowModal() == wx.ID_OK:
668            for sel in dlg.GetSelections():
669                Varb = varList[sel]
670                VarObj = G2obj.G2VarObj(Varb)
671                newcons = [[[0.0,VarObj],None,None,'h']]
672                if CheckAddedConstraint(newcons):
673                    data[constrDictEnt] += newcons
674        dlg.Destroy()
675        wx.CallAfter(OnPageChanged,None)
676       
677    def OnAddEquivalence(event):
678        '''add an Equivalence constraint'''
679        page = G2frame.Page
680        vartype,varList,constrDictEnt = PageSelection(page)
681        title1 = "Setup equivalent "+vartype+" variables"
682        title2 = "Select additional "+vartype+" variable(s) to be equivalent with "
683        if not varList:
684            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
685                parent=G2frame)
686            return
687#        legend = "Select variables to make equivalent (only one of the variables will be varied when all are set to be varied)"
688        GetAddVars(page,title1,title2,varList,constrDictEnt,'equivalence')
689       
690    def OnAddAtomEquiv(event):
691        ''' Add equivalences between all parameters on atoms '''
692        page = G2frame.Page
693        vartype,varList,constrDictEnt = PageSelection(page)
694        title1 = "Setup equivalent atom variables"
695        title2 = "Select additional atoms(s) to be equivalent with "
696        if not varList:
697            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
698                parent=G2frame)
699            return
700#        legend = "Select atoms to make equivalent (only one of the atom variables will be varied when all are set to be varied)"
701        GetAddAtomVars(page,title1,title2,varList,constrDictEnt,'equivalence')
702       
703    def OnAddRiding(event):
704        ''' Add riding equivalences between all parameters on atoms '''
705        page = G2frame.Page
706        vartype,varList,constrDictEnt = PageSelection(page)
707        title1 = "Setup riding atoms "
708        title2 = "Select additional atoms(s) to ride on "
709        if not varList:
710            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
711                parent=G2frame)
712            return
713#        legend = "Select atoms to ride (only one of the atom variables will be varied when all are set to be varied)"
714        GetAddAtomVars(page,title1,title2,varList,constrDictEnt,'riding')
715   
716    def OnAddFunction(event):
717        '''add a Function (new variable) constraint'''
718        page = G2frame.Page
719        vartype,varList,constrDictEnt = PageSelection(page)
720        title1 = "Setup new variable based on "+vartype+" variables"
721        title2 = "Include additional "+vartype+" variable(s) to be included with "
722        if not varList:
723            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
724                parent=G2frame)
725            return
726#        legend = "Select variables to include in a new variable (the new variable will be varied when all included variables are varied)"
727        GetAddVars(page,title1,title2,varList,constrDictEnt,'function')
728                       
729    def OnAddConstraint(event):
730        '''add a constraint equation to the constraints list'''
731        page = G2frame.Page
732        vartype,varList,constrDictEnt = PageSelection(page)
733        title1 = "Setup constraint on "+vartype+" variables"
734        title2 = "Select additional "+vartype+" variable(s) to include in constraint with "
735        if not varList:
736            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
737                parent=G2frame)
738            return
739#        legend = "Select variables to include in a constraint equation (the values will be constrainted to equal a specified constant)"
740        GetAddVars(page,title1,title2,varList,constrDictEnt,'constraint')
741
742    def GetAddVars(page,title1,title2,varList,constrDictEnt,constType):
743        '''Get the variables to be added for OnAddEquivalence, OnAddFunction,
744        and OnAddConstraint. Then create and check the constraint.
745        '''
746        #varListlbl = ["("+i+") "+G2obj.fmtVarDescr(i) for i in varList]
747        l2 = l1 = 1
748        for i in varList:
749            l1 = max(l1,len(i))
750            loc,desc = G2obj.VarDescr(i)
751            l2 = max(l2,len(loc))
752        fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}"
753        varListlbl = [fmt.format(i,*G2obj.VarDescr(i)) for i in varList]       
754        dlg = G2G.G2SingleChoiceDialog(G2frame,'Select 1st variable:',
755            title1,varListlbl,monoFont=True,size=(625,400))
756        dlg.CenterOnParent()
757        if dlg.ShowModal() == wx.ID_OK:
758            sel = dlg.GetSelection()
759            FrstVarb = varList[sel]
760            VarObj = G2obj.G2VarObj(FrstVarb)
761            moreVarb = FindEquivVarb(FrstVarb,varList)
762            newcons = SelectVarbs(page,VarObj,moreVarb,title2+FrstVarb,constType)
763            if len(newcons) > 0:
764                if CheckAddedConstraint(newcons):
765                    data[constrDictEnt] += newcons
766        dlg.Destroy()
767        wx.CallAfter(OnPageChanged,None)
768                       
769    def FindNeighbors(phase,FrstName,AtNames):
770        General = phase['General']
771        cx,ct,cs,cia = General['AtomPtrs']
772        Atoms = phase['Atoms']
773        atNames = [atom[ct-1] for atom in Atoms]
774        Cell = General['Cell'][1:7]
775        Amat,Bmat = G2lat.cell2AB(Cell)
776        atTypes = General['AtomTypes']
777        Radii = np.array(General['BondRadii'])
778        AtInfo = dict(zip(atTypes,Radii)) #or General['BondRadii']
779        Orig = atNames.index(FrstName.split()[1])
780        OType = Atoms[Orig][ct]
781        XYZ = G2mth.getAtomXYZ(Atoms,cx)       
782        Neigh = []
783        Dx = np.inner(Amat,XYZ-XYZ[Orig]).T
784        dist = np.sqrt(np.sum(Dx**2,axis=1))
785        sumR = AtInfo[OType]+0.5    #H-atoms only!
786        IndB = ma.nonzero(ma.masked_greater(dist-0.85*sumR,0.))
787        for j in IndB[0]:
788            if j != Orig:
789                Neigh.append(AtNames[j])
790        return Neigh
791       
792    def GetAddAtomVars(page,title1,title2,varList,constrDictEnt,constType):
793        '''Get the atom variables to be added for OnAddAtomEquiv. Then create and
794        check the constraints. Riding for H atoms only.
795        '''
796        Atoms = {G2obj.VarDescr(i)[0]:[] for i in varList if 'Atom' in G2obj.VarDescr(i)[0]}
797        for item in varList:
798            atName = G2obj.VarDescr(item)[0]
799            if atName in Atoms:
800                Atoms[atName].append(item)
801        AtNames = Atoms.keys()
802        AtNames.sort()
803        dlg = G2G.G2SingleChoiceDialog(G2frame,'Select 1st atom:',
804            title1,AtNames,monoFont=True,size=(625,400))
805        dlg.CenterOnParent()
806        FrstAtom = ''
807        if dlg.ShowModal() == wx.ID_OK:
808            sel = dlg.GetSelection()
809            FrstAtom = AtNames[sel]
810            if 'riding' in constType:
811                phaseName = (FrstAtom.split(' in ')[1]).strip()
812                phase = Phases[phaseName]
813                AtNames = FindNeighbors(phase,FrstAtom,AtNames)
814            else:
815                AtNames.remove(FrstAtom)
816        dlg.Destroy()
817        if FrstAtom == '':
818            print 'no atom selected'
819            return
820        dlg = G2G.G2MultiChoiceDialog(
821            G2frame,title2+FrstAtom,
822            'Constrain '+str(FrstAtom)+' with...',AtNames,
823            toggle=False,size=(625,400),monoFont=True)
824        if dlg.ShowModal() == wx.ID_OK:
825            Selections = dlg.GetSelections()[:]
826        else:
827            print 'no target atom selected'
828            dlg.Destroy()
829            return
830        dlg.Destroy()
831        for name in Atoms[FrstAtom]:
832            newcons = []
833            constr = []
834            if 'riding' in constType:
835                if 'AUiso' in name:
836                    constr = [[1.0,G2obj.G2VarObj(name)]]
837                elif 'AU11' in name:
838                    pass
839                elif 'AU' not in name:
840                    constr = [[1.0,G2obj.G2VarObj(name)]]
841            else:
842                constr = [[1.0,G2obj.G2VarObj(name)]]
843            pref = ':'+name.rsplit(':',1)[0].split(':',1)[1]    #get stuff between phase id & atom id
844            for sel in Selections:
845                name2 = Atoms[AtNames[sel]][0]
846                pid = name2.split(':',1)[0]                     #get phase id for 2nd atom
847                id = name2.rsplit(':',1)[-1]                    #get atom no. for 2nd atom
848                if 'riding' in constType:
849                    pref = pid+pref
850                    if 'AUiso' in pref:
851                        parts = pref.split('AUiso')
852                        constr += [[1.2,G2obj.G2VarObj('%s:%s'%(parts[0]+'AUiso',id))]]
853                    elif 'AU' not in pref:
854                        constr += [[1.0,G2obj.G2VarObj('%s:%s'%(pref,id))]]
855                else:
856                    constr += [[1.0,G2obj.G2VarObj('%s:%s'%(pid+pref,id))]]
857            if not constr:
858                continue
859            if 'frac' in pref and 'riding' not in constType:
860                newcons = [constr+[1.0,None,'c']]
861            else:
862                newcons = [constr+[None,None,'e']]
863            if len(newcons) > 0:
864                if CheckAddedConstraint(newcons):
865                    data[constrDictEnt] += newcons
866        wx.CallAfter(OnPageChanged,None)
867                       
868    def MakeConstraintsSizer(name,pageDisplay):
869        '''Creates a sizer displaying all of the constraints entered of
870        the specified type.
871
872        :param str name: the type of constraints to be displayed ('HAP',
873          'Hist', 'Phase', or 'Global')
874        :param wx.Panel pageDisplay: parent panel for sizer
875        :returns: wx.Sizer created by method
876        '''
877        #TODO: show symmetry generated constraints - no clue how to do this.
878        constSizer = wx.FlexGridSizer(0,6,0,0)
879        maxlen = 70 # characters before wrapping a constraint
880        for Id,item in enumerate(data[name]):
881            refineflag = False
882            helptext = ""
883            eqString = ['',]
884            if item[-1] == 'h': # Hold on variable
885                constSizer.Add((-1,-1),0)              # blank space for edit button
886                typeString = 'FIXED'
887                var = str(item[0][1])
888                varMean = G2obj.fmtVarDescr(var)
889                eqString[-1] =  var +'   '
890                helptext = "Prevents variable:\n"+ var + " ("+ varMean + ")\nfrom being changed"
891            elif isinstance(item[-1],str): # not true on original-style (2011?) constraints
892                constEdit = wx.Button(pageDisplay,-1,'Edit',style=wx.BU_EXACTFIT)
893                constEdit.Bind(wx.EVT_BUTTON,OnConstEdit)
894                constSizer.Add(constEdit,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)            # edit button
895                Indx[constEdit.GetId()] = [Id,name]
896                if item[-1] == 'f':
897                    helptext = "A new variable"
898                    if item[-3]:
899                        helptext += " named "+str(item[-3])
900                    helptext += " is created from a linear combination of the following variables:\n"
901                    for term in item[:-3]:
902                        var = str(term[1])
903                        if len(eqString[-1]) > maxlen:
904                            eqString.append(' ')
905                        m = term[0]
906                        if eqString[-1] != '':
907                            if m >= 0:
908                                eqString[-1] += ' + '
909                            else:
910                                eqString[-1] += ' - '
911                                m = abs(m)
912                        eqString[-1] += '%.3f*%s '%(m,var)
913                        varMean = G2obj.fmtVarDescr(var)
914                        helptext += "\n" + var + " ("+ varMean + ")"
915                    if '_Explain' in data:
916                        if data['_Explain'].get(item[-3]):
917                            helptext += '\n\n'
918                            helptext += data['_Explain'][item[-3]]
919                    # typeString = 'NEWVAR'
920                    # if item[-3]:
921                    #     eqString[-1] += ' = '+item[-3]
922                    # else:
923                    #     eqString[-1] += ' = New Variable'
924                    if item[-3]:
925                        typeString = item[-3] + ' = '
926                    else:
927                        typeString = 'New Variable = '
928                    #print 'refine',item[-2]
929                    refineflag = True
930                elif item[-1] == 'c':
931                    helptext = "The following variables constrained to equal a constant:"
932                    for term in item[:-3]:
933                        var = str(term[1])
934                        if len(eqString[-1]) > maxlen:
935                            eqString.append(' ')
936                        if eqString[-1] != '':
937                            if term[0] > 0:
938                                eqString[-1] += ' + '
939                            else:
940                                eqString[-1] += ' - '
941                        eqString[-1] += '%.3f*%s '%(abs(term[0]),var)
942                        varMean = G2obj.fmtVarDescr(var)
943                        helptext += "\n" + var + " ("+ varMean + ")"
944                    typeString = 'CONST'
945                    eqString[-1] += ' = '+str(item[-3])
946                elif item[-1] == 'e':
947                    helptext = "The following variables are set to be equivalent, noting multipliers:"
948                    for term in item[:-3]:
949                        var = str(term[1])
950                        if term[0] == 0: term[0] = 1.0
951                        if len(eqString[-1]) > maxlen:
952                            eqString.append(' ')
953                        if eqString[-1] == '':
954                            eqString[-1] += var+' '
955                            first = term[0]
956                        else:
957                            eqString[-1] += ' = %.3f*%s '%(first/term[0],var)
958                        varMean = G2obj.fmtVarDescr(var)
959                        helptext += "\n" + var + " ("+ varMean + ")"
960                    typeString = 'EQUIV'
961                else:
962                    print 'Unexpected constraint',item
963               
964            else:
965                print 'Removing old-style constraints'
966                data[name] = []
967                return constSizer
968            constDel = wx.Button(pageDisplay,-1,'Delete',style=wx.BU_EXACTFIT)
969            constDel.Bind(wx.EVT_BUTTON,OnConstDel)
970            Indx[constDel.GetId()] = [Id,name]
971            constSizer.Add(constDel,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)             # delete button
972            if helptext:
973                ch = G2G.HelpButton(pageDisplay,helptext)
974                constSizer.Add(ch,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)
975            else:
976                constSizer.Add((-1,-1))
977            if refineflag:
978                ch = G2G.G2CheckBox(pageDisplay,'',item,-2)
979                constSizer.Add(ch,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)
980            else:
981                constSizer.Add((-1,-1))               
982            constSizer.Add(wx.StaticText(pageDisplay,-1,typeString),
983                           0,wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT,3)
984            if len(eqString) > 1:
985                Eq = wx.BoxSizer(wx.VERTICAL)
986                for s in eqString:
987                    Eq.Add(wx.StaticText(pageDisplay,-1,s),0,wx.ALIGN_CENTER_VERTICAL)
988            else:
989                Eq = wx.StaticText(pageDisplay,-1,eqString[0])
990            constSizer.Add(Eq,1,wx.ALIGN_CENTER_VERTICAL)
991        return constSizer
992               
993    def OnConstDel(event):
994        'Delete a constraint'
995        Obj = event.GetEventObject()
996        Id,name = Indx[Obj.GetId()]
997        del(data[name][Id])
998        wx.CallAfter(OnPageChanged,None)
999       
1000    def OnConstEdit(event):
1001        '''Called to edit an individual contraint in response to a
1002        click on its Edit button
1003        '''
1004        Obj = event.GetEventObject()
1005        Id,name = Indx[Obj.GetId()]
1006        if data[name][Id][-1] == 'f':
1007            items = data[name][Id][:-3]
1008            constType = 'New Variable'
1009            if data[name][Id][-3]:
1010                varname = data[name][Id][-3]
1011            else:
1012                varname = ""
1013            lbl = 'Enter value for each term in constraint; sum = new variable'
1014            dlg = ConstraintDialog(G2frame,constType,lbl,items,
1015                                   varname=varname,varyflag=data[name][Id][-2])
1016        elif data[name][Id][-1] == 'c':
1017            items = data[name][Id][:-3]+[
1018                [data[name][Id][-3],'fixed value =']]
1019            constType = 'Constraint'
1020            lbl = 'Edit value for each term in constant constraint sum'
1021            dlg = ConstraintDialog(G2frame,constType,lbl,items)
1022        elif data[name][Id][-1] == 'e':
1023            items = data[name][Id][:-3]
1024            constType = 'Equivalence'
1025            lbl = 'The following terms are set to be equal:'
1026            dlg = ConstraintDialog(G2frame,constType,lbl,items,'/')
1027        else:
1028            return
1029        try:
1030            prev = data[name][Id][:]
1031            if dlg.ShowModal() == wx.ID_OK:
1032                result = dlg.GetData()
1033                for i in range(len(data[name][Id][:-3])):
1034                    if type(data[name][Id][i]) is tuple: # fix non-mutable construct
1035                        data[name][Id][i] = list(data[name][Id][i])
1036                    data[name][Id][i][0] = result[i][0]
1037                if data[name][Id][-1] == 'c':
1038                    data[name][Id][-3] = str(result[-1][0])
1039                elif data[name][Id][-1] == 'f':
1040                    # process the variable name to put in global form (::var)
1041                    varname = str(dlg.newvar[0]).strip().replace(' ','_')
1042                    if varname.startswith('::'):
1043                        varname = varname[2:]
1044                    varname = varname.replace(':',';')
1045                    if varname:
1046                        data[name][Id][-3] = varname
1047                    else:
1048                        data[name][Id][-3] = ''
1049                    data[name][Id][-2] = dlg.newvar[1]
1050                if not CheckChangedConstraint():
1051                    data[name][Id] = prev
1052        except:
1053            import traceback
1054            print traceback.format_exc()
1055        finally:
1056            dlg.Destroy()
1057        wx.CallAfter(OnPageChanged,None)
1058   
1059    def UpdateConstraintPanel(panel,typ):
1060        '''Update the contents of the selected Constraint
1061        notebook tab. Called in :func:`OnPageChanged`
1062        '''
1063        if panel.GetSizer(): panel.GetSizer().Clear(True)
1064        Siz = wx.BoxSizer(wx.VERTICAL)
1065        Siz.Add((5,5),0)
1066        Siz.Add(MakeConstraintsSizer(typ,panel),1,wx.EXPAND)
1067        panel.SetSizer(Siz,True)
1068        Size = Siz.GetMinSize()
1069        Size[0] += 40
1070        Size[1] = max(Size[1],450) + 20
1071        panel.SetSize(Size)
1072        panel.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1073        panel.Show()
1074
1075    def OnPageChanged(event):
1076        '''Called when a tab is pressed or when a "select tab" menu button is
1077        used (see RaisePage), or to refresh the current tab contents (event=None)
1078        '''
1079        if event:       #page change event!
1080            page = event.GetSelection()
1081        else: # called directly, get current page
1082            page = G2frame.constr.GetSelection()
1083        #G2frame.constr.SetSize(G2frame.dataWindow.GetClientSize())    #TODO -almost right
1084        G2frame.constr.ChangeSelection(page)
1085        text = G2frame.constr.GetPageText(page)
1086        G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_EQUIVALANCEATOMS,False)
1087#        G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_ADDRIDING,False)
1088        if text == 'Histogram/Phase':
1089            G2frame.Page = [page,'hap']
1090            UpdateConstraintPanel(HAPConstr,'HAP')
1091        elif text == 'Histogram':
1092            G2frame.Page = [page,'hst']
1093            UpdateConstraintPanel(HistConstr,'Hist')
1094        elif text == 'Phase':
1095            G2frame.Page = [page,'phs']
1096            G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_EQUIVALANCEATOMS,True)
1097#            G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_ADDRIDING,True)
1098            if 'DELETED' in str(PhaseConstr):   #seems to be no other way to do this (wx bug)
1099                if GSASIIpath.GetConfigValue('debug'):
1100                    print 'wx error: PhaseConstr not cleanly deleted after Refine'
1101                return
1102            UpdateConstraintPanel(PhaseConstr,'Phase')
1103        elif text == 'Global':
1104            G2frame.Page = [page,'glb']
1105            UpdateConstraintPanel(GlobalConstr,'Global')
1106
1107    def RaisePage(event):
1108        'Respond to a "select tab" menu button'
1109        try:
1110            i = (G2G.wxID_CONSPHASE,
1111                 G2G.wxID_CONSHAP,
1112                 G2G.wxID_CONSHIST,
1113                 G2G.wxID_CONSGLOBAL).index(event.GetId())
1114            G2frame.constr.SetSelection(i)
1115            wx.CallAfter(OnPageChanged,None)
1116        except ValueError:
1117            print('Unexpected event in RaisePage')
1118
1119    def SetStatusLine(text):
1120        G2frame.GetStatusBar().SetStatusText(text,1)                                     
1121       
1122    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.ConstraintMenu)
1123    #G2frame.SetLabel(G2frame.GetLabel().split('||')[0]+' || '+'Constraints')
1124    G2frame.SetTitle('Constraints')
1125    SetStatusLine('')
1126   
1127    G2frame.Bind(wx.EVT_MENU, OnAddConstraint, id=G2G.wxID_CONSTRAINTADD)
1128    G2frame.Bind(wx.EVT_MENU, OnAddFunction, id=G2G.wxID_FUNCTADD)
1129    G2frame.Bind(wx.EVT_MENU, OnAddEquivalence, id=G2G.wxID_EQUIVADD)
1130    G2frame.Bind(wx.EVT_MENU, OnAddHold, id=G2G.wxID_HOLDADD)
1131    G2frame.Bind(wx.EVT_MENU, OnAddAtomEquiv, id=G2G.wxID_EQUIVALANCEATOMS)
1132#    G2frame.Bind(wx.EVT_MENU, OnAddRiding, id=G2G.wxID_ADDRIDING)
1133    # tab commands
1134    for id in (G2G.wxID_CONSPHASE,
1135               G2G.wxID_CONSHAP,
1136               G2G.wxID_CONSHIST,
1137               G2G.wxID_CONSGLOBAL):
1138        G2frame.Bind(wx.EVT_MENU, RaisePage,id=id)
1139
1140    #G2frame.constr = G2G.GSNoteBook(parent=G2frame.dataWindow,size=G2frame.dataWindow.GetClientSize())
1141    G2frame.constr = G2G.GSNoteBook(parent=G2frame.dataWindow)
1142    G2frame.dataWindow.GetSizer().Add(G2frame.constr,1,wx.ALL|wx.EXPAND)
1143    # note that order of pages is hard-coded in RaisePage
1144    PhaseConstr = wx.ScrolledWindow(G2frame.constr)
1145    G2frame.constr.AddPage(PhaseConstr,'Phase')
1146    HAPConstr = wx.ScrolledWindow(G2frame.constr)
1147    G2frame.constr.AddPage(HAPConstr,'Histogram/Phase')
1148    HistConstr = wx.ScrolledWindow(G2frame.constr)
1149    G2frame.constr.AddPage(HistConstr,'Histogram')
1150    GlobalConstr = wx.ScrolledWindow(G2frame.constr)
1151    G2frame.constr.AddPage(GlobalConstr,'Global')
1152    wx.CallAfter(OnPageChanged,None)
1153    G2frame.constr.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged)
1154    # validate all the constrants -- should not see any errors here normally
1155    allcons = []
1156    for key in data:
1157        if key.startswith('_'): continue
1158        allcons += data[key]
1159    if not len(allcons): return
1160    G2mv.InitVars()   
1161    constDictList,fixedList,ignored = G2stIO.ProcessConstraints(allcons)
1162    errmsg, warnmsg = G2mv.CheckConstraints('',constDictList,fixedList)
1163    if errmsg:
1164        G2frame.ErrorDialog('Constraint Error','Error in constraints:\n'+errmsg,
1165            parent=G2frame)
1166    elif warnmsg:
1167        print 'Unexpected contraint warning:\n',warnmsg
1168
1169################################################################################
1170#### Make nuclear-magnetic phase constraints - called by OnTransform in G2phsGUI
1171################################################################################       
1172       
1173def MagConstraints(G2frame,oldPhase,newPhase,Trans,Vec,atCodes):
1174    '''Add constraints for new magnetic phase created via transformation of old
1175    nuclear one
1176    NB: A = [G11,G22,G33,2*G12,2*G13,2*G23]
1177    '''
1178   
1179    def SetUniqAj(pId,iA,Aname,SGLaue):
1180        if SGLaue in ['4/m','4/mmm'] and iA in [0,1]:
1181            parm = '%d::%s'%(pId,'A0')
1182        elif SGLaue in ['m3','m3m'] and iA in [0,1,2]:
1183            parm = '%d::%s'%(pId,'A0')
1184        elif SGLaue in ['6/m','6/mmm','3m1', '31m', '3'] and iA in [0,1,3]:
1185            parm = '%d::%s'%(pId,'A0')
1186        elif SGLaue in ['3R', '3mR']:
1187            if ia in [0,1,2]:
1188                parm = '%d::%s'%(pId,'A0')
1189            else:
1190                parm = '%d::%s'%(pId,'A3')
1191        else:
1192            parm = '%d::%s'%(pId,Aname)
1193        return parm
1194       
1195    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
1196    UseList = newPhase['Histograms']
1197    detTrans = np.abs(nl.det(Trans))
1198    invTrans = nl.inv(Trans)
1199#    print 'invTrans',invTrans
1200    nAcof = G2lat.cell2A(newPhase['General']['Cell'][1:7])
1201   
1202    opId = oldPhase['pId']
1203    npId = newPhase['pId']
1204    cx,ct,cs,cia = newPhase['General']['AtomPtrs']
1205    nAtoms = newPhase['Atoms']
1206    oSGData = oldPhase['General']['SGData']
1207    nSGData = newPhase['General']['SGData']
1208    oAcof = G2lat.cell2A(oldPhase['General']['Cell'][1:7])
1209    nAcof = G2lat.cell2A(newPhase['General']['Cell'][1:7])
1210    item = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Constraints') 
1211    constraints = G2frame.GPXtree.GetItemPyData(item)
1212#    GSASIIpath.IPyBreak()
1213    parmDict = {}
1214    varyList = []
1215    for ia,code in enumerate(atCodes):
1216        atom = nAtoms[ia]
1217        siteSym = G2spc.SytSym(atom[cx:cx+3],nSGData)[0]
1218        CSX = G2spc.GetCSxinel(siteSym)
1219        item = code.split('+')[0]
1220        iat,opr = item.split(':')
1221        Nop = abs(int(opr))%100-1
1222        if '-' in opr:
1223            Nop *= -1
1224        Opr = oldPhase['General']['SGData']['SGOps'][abs(Nop)][0]
1225        if Nop < 0:         #inversion
1226            Opr *= -1
1227        XOpr = np.inner(Opr,Trans.T)
1228        names = ['dAx','dAy','dAz']
1229        for ix,name in enumerate(names):
1230            IndpCon = [1.0,G2obj.G2VarObj('%d::%s:%s'%(opId,name,iat))]
1231            DepCons = []
1232            for iop,opval in enumerate(XOpr[ix]):
1233                if opval and CSX[0][ix]:    #-opval from defn of dAx, etc.
1234                    DepCons.append([-opval,G2obj.G2VarObj('%d::%s:%d'%(npId,names[iop],ia))])
1235            if len(DepCons) == 1:
1236                constraints['Phase'].append([IndpCon,DepCons[0],None,None,'e'])
1237            elif len(DepCons) > 1:
1238                for Dep in DepCons:
1239                    Dep[0] *= -1
1240                constraints['Phase'].append([IndpCon]+DepCons+[0.0,None,'c'])
1241        for name in ['Afrac','AUiso']:
1242            IndpCon = [1.0,G2obj.G2VarObj('%d::%s:%s'%(opId,name,iat))]
1243            DepCons = [1.0,G2obj.G2VarObj('%d::%s:%d'%(npId,name,ia))]
1244            constraints['Phase'].append([IndpCon,DepCons,None,None,'e'])
1245        #how do I do Uij's for most Trans?
1246    Anames = [['A0','A3','A4'],['A3','A1','A5'],['A4','A5','A2']]
1247    As = ['A0','A1','A2','A3','A4','A5']
1248    Aids = [[0,0,'A0',-1],[1,1,'A1',-1],[2,2,'A2',-1],[0,1,'A3',2],[0,2,'A4',1],[1,2,'A5',0]]
1249    Axes = ['a','b','c']
1250    Holds = []
1251    for iA,Aid in enumerate(Aids):
1252        parm = SetUniqAj(opId,iA,Aid[2],oSGData['SGLaue'])
1253        parmDict[parm] = oAcof[iA]
1254        varyList.append(parm)
1255        IndpCon = [1.0,G2obj.G2VarObj(parm)]
1256        DepCons = []
1257        for iat in range(3):
1258            if nSGData['SGLaue'] in ['-1','2/m']:       #set holds
1259                if (abs(nAcof[iA]) < 1.e-8) and (abs(Trans[Aid[0],Aid[1]]) < 1.e-8):
1260                    if Axes[iat] != oSGData['SGUniq'] and oSGData['SGLaue'] != nSGData['SGLaue']:
1261                        HoldObj = G2obj.G2VarObj('%d::%s'%(npId,Aid[2]))
1262                        if not HoldObj in Holds: 
1263                            constraints['Phase'].append([[0.0,HoldObj],None,None,'h'])
1264                            Holds.append(HoldObj)
1265                            continue
1266#            print iA,Aid,iat,invTrans[iat][Aid[0]],invTrans[Aid[1]][iat],Anames[Aid[0]][Aid[1]],parm
1267            if abs(invTrans[iat,Aid[1]]) > 1.e-8 and abs(nAcof[iA]) > 1.e-8:
1268                parm = SetUniqAj(npId,iA,Anames[Aid[0]][Aid[1]],nSGData['SGLaue'])
1269                parmDict[parm] = nAcof[As.index(Aid[2])]
1270                if not parm in varyList:
1271                    varyList.append(parm)
1272                DepCons.append([Trans[Aid[0],Aid[0]]*Trans[Aid[1],Aid[1]],G2obj.G2VarObj(parm)])
1273        if len(DepCons) == 1:
1274            constraints['Phase'].append([IndpCon,DepCons[0],None,None,'e'])
1275        elif len(DepCons) > 1:       
1276            for Dep in DepCons:
1277                Dep[0] *= -1
1278            constraints['Phase'].append([IndpCon]+DepCons+[0.0,None,'c'])
1279#    constDict,fixedList,ignored = G2stIO.ProcessConstraints(constraints['Phase'])
1280#    groups,parmlist = G2mv.GroupConstraints(constDict)
1281#    G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList,parmDict)
1282#    print 'old',parmDict
1283#    G2mv.Dict2Map(parmDict,varyList)
1284#    print 'new',parmDict
1285    for hId,hist in enumerate(UseList):    #HAP - seems OK
1286        ohapkey = '%d:%d:'%(opId,hId)
1287        nhapkey = '%d:%d:'%(npId,hId)
1288        IndpCon = [1.0,G2obj.G2VarObj(ohapkey+'Scale')]
1289        DepCons = [detTrans,G2obj.G2VarObj(nhapkey+'Scale')]
1290        constraints['HAP'].append([IndpCon,DepCons,None,None,'e'])
1291        for name in ['Size;i','Mustrain;i']:
1292            IndpCon = [1.0,G2obj.G2VarObj(ohapkey+name)]
1293            DepCons = [1.0,G2obj.G2VarObj(nhapkey+name)]
1294            constraints['HAP'].append([IndpCon,DepCons,None,None,'e'])
1295       
1296################################################################################
1297#### Rigid bodies
1298################################################################################
1299
1300def UpdateRigidBodies(G2frame,data):
1301    '''Called when Rigid bodies tree item is selected.
1302    Displays the rigid bodies in the data window
1303    '''
1304    if not data.get('RBIds') or not data:
1305        data.update({'Vector':{'AtInfo':{}},'Residue':{'AtInfo':{}},
1306            'RBIds':{'Vector':[],'Residue':[]}})       #empty/bad dict - fill it
1307           
1308    global resList
1309    Indx = {}
1310    resList = []
1311    plotDefaults = {'oldxy':[0.,0.],'Quaternion':[0.,0.,0.,1.],'cameraPos':30.,'viewDir':[0,0,1],}
1312
1313    G2frame.rbBook = G2G.GSNoteBook(parent=G2frame.dataWindow)
1314    G2frame.dataWindow.GetSizer().Add(G2frame.rbBook,1,wx.ALL|wx.EXPAND)
1315    VectorRB = wx.ScrolledWindow(G2frame.rbBook)
1316    VectorRBDisplay = wx.Panel(VectorRB)
1317    G2frame.rbBook.AddPage(VectorRB,'Vector rigid bodies')
1318    ResidueRB = wx.ScrolledWindow(G2frame.rbBook)
1319    ResidueRBDisplay = wx.Panel(ResidueRB)
1320    G2frame.rbBook.AddPage(ResidueRB,'Residue rigid bodies')
1321   
1322    def OnPageChanged(event):
1323        global resList
1324        resList = []
1325        if event:       #page change event!
1326            page = event.GetSelection()
1327        else:
1328            page = G2frame.rbBook.GetSelection()
1329        #G2frame.rbBook.SetSize(G2frame.dataWindow.GetClientSize())    #TODO -almost right
1330        G2frame.rbBook.ChangeSelection(page)
1331        text = G2frame.rbBook.GetPageText(page)
1332        if text == 'Vector rigid bodies':
1333            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.VectorBodyMenu)
1334            G2frame.Bind(wx.EVT_MENU, AddVectorRB, id=G2G.wxID_VECTORBODYADD)
1335            G2frame.Page = [page,'vrb']
1336            UpdateVectorRB()
1337        elif text == 'Residue rigid bodies':
1338            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RigidBodyMenu)
1339            G2frame.Bind(wx.EVT_MENU, AddResidueRB, id=G2G.wxID_RIGIDBODYADD)
1340            G2frame.Bind(wx.EVT_MENU, OnImportRigidBody, id=G2G.wxID_RIGIDBODYIMPORT)
1341            G2frame.Bind(wx.EVT_MENU, OnDefineTorsSeq, id=G2G.wxID_RESIDUETORSSEQ) #enable only if residue RBs exist?
1342            G2frame.Page = [page,'rrb']
1343            UpdateResidueRB()
1344           
1345    def getMacroFile(macName):
1346        defDir = os.path.join(os.path.split(__file__)[0],'GSASIImacros')
1347        dlg = wx.FileDialog(G2frame,message='Choose '+macName+' rigid body macro file',
1348            defaultDir=defDir,defaultFile="",wildcard="GSAS-II macro file (*.mac)|*.mac",
1349            style=wx.OPEN | wx.CHANGE_DIR)
1350        try:
1351            if dlg.ShowModal() == wx.ID_OK:
1352                macfile = dlg.GetPath()
1353                macro = open(macfile,'Ur')
1354                head = macro.readline()
1355                if macName not in head:
1356                    print head
1357                    print '**** ERROR - wrong restraint macro file selected, try again ****'
1358                    macro = []
1359            else: # cancel was pressed
1360                macro = []
1361        finally:
1362            dlg.Destroy()
1363        return macro        #advanced past 1st line
1364       
1365    def getTextFile():
1366        dlg = wx.FileDialog(G2frame,'Choose rigid body text file', '.', '',
1367            "GSAS-II text file (*.txt)|*.txt|XYZ file (*.xyz)|*.xyz|"
1368            "Sybyl mol2 file (*.mol2)|*.mol2|PDB file (*.pdb;*.ent)|*.pdb;*.ent",
1369            wx.OPEN | wx.CHANGE_DIR)
1370        try:
1371            if dlg.ShowModal() == wx.ID_OK:
1372                txtfile = dlg.GetPath()
1373                ext = os.path.splitext(txtfile)[1]
1374                text = open(txtfile,'Ur')
1375            else: # cancel was pressed
1376                ext = ''
1377                text = []
1378        finally:
1379            dlg.Destroy()
1380        if 'ent' in ext:
1381            ext = '.pdb'
1382        return text,ext.lower()
1383       
1384    def OnImportRigidBody(event):
1385        page = G2frame.rbBook.GetSelection()
1386        if 'Vector' in G2frame.rbBook.GetPageText(page):
1387            pass
1388        elif 'Residue' in G2frame.rbBook.GetPageText(page):
1389            ImportResidueRB()
1390           
1391    def AddVectorRB(event):
1392        AtInfo = data['Vector']['AtInfo']
1393        dlg = MultiIntegerDialog(G2frame,'New Rigid Body',['No. atoms','No. translations'],[1,1])
1394        if dlg.ShowModal() == wx.ID_OK:
1395            nAtoms,nTrans = dlg.GetValues()
1396            rbId = ran.randint(0,sys.maxint)
1397            vecMag = [1.0 for i in range(nTrans)]
1398            vecRef = [False for i in range(nTrans)]
1399            vecVal = [np.zeros((nAtoms,3)) for j in range(nTrans)]
1400            rbTypes = ['C' for i in range(nAtoms)]
1401            Info = G2elem.GetAtomInfo('C')
1402            AtInfo['C'] = [Info['Drad'],Info['Color']]
1403            data['Vector'][rbId] = {'RBname':'UNKRB','VectMag':vecMag,'rbXYZ':np.zeros((nAtoms,3)),
1404                'rbRef':[0,1,2,False],'VectRef':vecRef,'rbTypes':rbTypes,'rbVect':vecVal,'useCount':0}
1405            data['RBIds']['Vector'].append(rbId)
1406        dlg.Destroy()
1407        UpdateVectorRB()
1408       
1409    def AddResidueRB(event):
1410        AtInfo = data['Residue']['AtInfo']
1411        macro = getMacroFile('rigid body')
1412        if not macro:
1413            return
1414        macStr = macro.readline()
1415        while macStr:
1416            items = macStr.split()
1417            if 'I' == items[0]:
1418                rbId = ran.randint(0,sys.maxint)
1419                rbName = items[1]
1420                rbTypes = []
1421                rbXYZ = []
1422                rbSeq = []
1423                atNames = []
1424                nAtms,nSeq,nOrig,mRef,nRef = [int(items[i]) for i in [2,3,4,5,6]]
1425                for iAtm in range(nAtms):
1426                    macStr = macro.readline().split()
1427                    atName = macStr[0]
1428                    atType = macStr[1]
1429                    atNames.append(atName)
1430                    rbXYZ.append([float(macStr[i]) for i in [2,3,4]])
1431                    rbTypes.append(atType)
1432                    if atType not in AtInfo:
1433                        Info = G2elem.GetAtomInfo(atType)
1434                        AtInfo[atType] = [Info['Drad'],Info['Color']]
1435                rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[nOrig-1])
1436                for iSeq in range(nSeq):
1437                    macStr = macro.readline().split()
1438                    mSeq = int(macStr[0])
1439                    for jSeq in range(mSeq):
1440                        macStr = macro.readline().split()
1441                        iBeg = int(macStr[0])-1
1442                        iFin = int(macStr[1])-1
1443                        angle = 0.0
1444                        nMove = int(macStr[2])
1445                        iMove = [int(macStr[i])-1 for i in range(3,nMove+3)]
1446                        rbSeq.append([iBeg,iFin,angle,iMove])
1447                data['Residue'][rbId] = {'RBname':rbName,'rbXYZ':rbXYZ,'rbTypes':rbTypes,
1448                    'atNames':atNames,'rbRef':[nOrig-1,mRef-1,nRef-1,True],'rbSeq':rbSeq,
1449                    'SelSeq':[0,0],'useCount':0}
1450                data['RBIds']['Residue'].append(rbId)
1451                print 'Rigid body '+rbName+' added'
1452            macStr = macro.readline()
1453        macro.close()
1454        UpdateResidueRB()
1455       
1456    def ImportResidueRB():
1457        AtInfo = data['Residue']['AtInfo']
1458        text,ext = getTextFile()
1459        if not text:
1460            return
1461        rbId = ran.randint(0,sys.maxint)
1462        rbTypes = []
1463        rbXYZ = []
1464        atNames = []
1465        txtStr = text.readline()
1466        if 'xyz' in ext:
1467            txtStr = text.readline()
1468            txtStr = text.readline()
1469        elif 'mol2' in ext:
1470            while 'ATOM' not in txtStr:
1471                txtStr = text.readline()
1472            txtStr = text.readline()
1473        elif 'pdb' in ext:
1474            while 'ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]:
1475                txtStr = text.readline()
1476                #print txtStr
1477        items = txtStr.split()
1478        while len(items):
1479            if 'txt' in ext:
1480                atName = items[0]
1481                atType = items[1]
1482                rbXYZ.append([float(items[i]) for i in [2,3,4]])
1483            elif 'xyz' in ext:
1484                atType = items[0]
1485                rbXYZ.append([float(items[i]) for i in [1,2,3]])
1486                atName = atType+str(len(rbXYZ))
1487            elif 'mol2' in ext:
1488                atType = items[1]
1489                atName = items[1]+items[0]
1490                rbXYZ.append([float(items[i]) for i in [2,3,4]])
1491            elif 'pdb' in ext:
1492                atType = items[-1]
1493                atName = items[2]
1494                xyz = txtStr[30:55].split()                   
1495                rbXYZ.append([float(x) for x in xyz])
1496            atNames.append(atName)
1497            rbTypes.append(atType)
1498            if atType not in AtInfo:
1499                Info = G2elem.GetAtomInfo(atType)
1500                AtInfo[atType] = [Info['Drad'],Info['Color']]
1501            txtStr = text.readline()
1502            if 'mol2' in ext and 'BOND' in txtStr:
1503                break
1504            if 'pdb' in ext and ('ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]):
1505                break
1506            items = txtStr.split()
1507        if len(atNames) < 3:
1508            G2G.G2MessageBox(G2frame,'Not enough atoms in rigid body; must be 3 or more')
1509        else:
1510            rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[0])
1511            data['Residue'][rbId] = {'RBname':'UNKRB','rbXYZ':rbXYZ,'rbTypes':rbTypes,
1512                'atNames':atNames,'rbRef':[0,1,2,False],'rbSeq':[],'SelSeq':[0,0],'useCount':0}
1513            data['RBIds']['Residue'].append(rbId)
1514            print 'Rigid body UNKRB added'
1515        text.close()
1516        UpdateResidueRB()
1517       
1518    def FindNeighbors(Orig,XYZ,atTypes,atNames,AtInfo):
1519        Radii = []
1520        for Atype in atTypes:
1521            Radii.append(AtInfo[Atype][0])
1522        Radii = np.array(Radii)
1523        Neigh = []
1524        Dx = XYZ-XYZ[Orig]
1525        dist = np.sqrt(np.sum(Dx**2,axis=1))
1526        sumR = Radii[Orig]+Radii
1527        IndB = ma.nonzero(ma.masked_greater(dist-0.85*sumR,0.))
1528        for j in IndB[0]:
1529            if j != Orig and atTypes[j] != 'H':
1530                Neigh.append(atNames[j])
1531        return Neigh
1532       
1533    def FindAllNeighbors(XYZ,atTypes,atNames,AtInfo):
1534        NeighDict = {}
1535        for iat,xyz in enumerate(atNames):
1536            NeighDict[atNames[iat]] = FindNeighbors(iat,XYZ,atTypes,atNames,AtInfo)
1537        return NeighDict
1538       
1539    def FindRiding(Orig,Pivot,NeighDict):
1540        riding = [Orig,Pivot]
1541        iAdd = 1
1542        new = True
1543        while new:
1544            newAtms = NeighDict[riding[iAdd]]
1545            for At in newAtms:
1546                new = False
1547                if At not in riding:
1548                    riding.append(At)
1549                    new = True
1550            iAdd += 1
1551            if iAdd < len(riding):
1552                new = True
1553        return riding[2:]
1554                       
1555    def OnDefineTorsSeq(event):
1556        rbKeys = data['Residue'].keys()
1557        rbKeys.remove('AtInfo')
1558        rbNames = [data['Residue'][k]['RBname'] for k in rbKeys]
1559        rbIds = dict(zip(rbNames,rbKeys))
1560        rbNames.sort()
1561        rbId = 0
1562        if len(rbNames) == 0:
1563            print 'There are no rigid bodies defined'
1564            G2frame.ErrorDialog('No rigid bodies','There are no rigid bodies defined',
1565                                parent=G2frame)
1566            return
1567        elif len(rbNames) > 1:
1568            dlg = wx.SingleChoiceDialog(G2frame,'Select rigid body for torsion sequence','Torsion sequence',rbNames)
1569            if dlg.ShowModal() == wx.ID_OK:
1570                sel = dlg.GetSelection()
1571                rbId = rbIds[rbNames[sel]]
1572                rbData = data['Residue'][rbId]
1573            dlg.Destroy()
1574        else:
1575            rbId = rbIds[rbNames[0]]
1576            rbData = data['Residue'][rbId]
1577        if not len(rbData):
1578            return
1579        atNames = rbData['atNames']
1580        AtInfo = data['Residue']['AtInfo']
1581        atTypes = rbData['rbTypes']
1582        XYZ = rbData['rbXYZ']
1583        neighDict = FindAllNeighbors(XYZ,atTypes,atNames,AtInfo)
1584        TargList = []           
1585        dlg = wx.SingleChoiceDialog(G2frame,'Select origin atom for torsion sequence','Origin atom',rbData['atNames'])
1586        if dlg.ShowModal() == wx.ID_OK:
1587            Orig = dlg.GetSelection()
1588            TargList = neighDict[atNames[Orig]]
1589        dlg.Destroy()
1590        if not len(TargList):
1591            return
1592        dlg = wx.SingleChoiceDialog(G2frame,'Select pivot atom for torsion sequence','Pivot atom',TargList)
1593        if dlg.ShowModal() == wx.ID_OK:
1594            Piv = atNames.index(TargList[dlg.GetSelection()])
1595            riding = FindRiding(atNames[Orig],atNames[Piv],neighDict)
1596            Riding = []
1597            for atm in riding:
1598                Riding.append(atNames.index(atm))
1599            rbData['rbSeq'].append([Orig,Piv,0.0,Riding])           
1600        dlg.Destroy()
1601        UpdateResidueRB()
1602
1603    def UpdateVectorRB(Scroll=0):
1604        AtInfo = data['Vector']['AtInfo']
1605        refChoice = {}
1606        if 'DELETED' in str(G2frame.GetStatusBar()):   #seems to be no other way to do this (wx bug)
1607            if GSASIIpath.GetConfigValue('debug'):
1608                print 'wx error: Rigid Body/Status not cleanly deleted after Refine'
1609            return
1610        SetStatusLine(' You may use e.g. "c60" or "s60" for a vector entry')
1611        def rbNameSizer(rbId,rbData):
1612
1613            def OnRBName(event):
1614                event.Skip()
1615                Obj = event.GetEventObject()
1616                rbData['RBname'] = Obj.GetValue()
1617               
1618            def OnDelRB(event):
1619                Obj = event.GetEventObject()
1620                rbId = Indx[Obj.GetId()]
1621                if rbId in data['Vector']:
1622                    del data['Vector'][rbId]
1623                    data['RBIds']['Vector'].remove(rbId)
1624                    rbData['useCount'] -= 1
1625                wx.CallAfter(UpdateVectorRB)
1626               
1627            def OnPlotRB(event):
1628                Obj = event.GetEventObject()
1629                Obj.SetValue(False)
1630                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,rbData,plotDefaults)
1631           
1632            nameSizer = wx.BoxSizer(wx.HORIZONTAL)
1633            nameSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Rigid body name: '),
1634                0,wx.ALIGN_CENTER_VERTICAL)
1635            RBname = wx.TextCtrl(VectorRBDisplay,-1,rbData['RBname'])
1636            Indx[RBname.GetId()] = rbId
1637            RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName)
1638            RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName)
1639            nameSizer.Add(RBname,0,wx.ALIGN_CENTER_VERTICAL)
1640            nameSizer.Add((5,0),)
1641            plotRB = wx.CheckBox(VectorRBDisplay,-1,'Plot?')
1642            Indx[plotRB.GetId()] = rbId
1643            plotRB.Bind(wx.EVT_CHECKBOX,OnPlotRB)
1644            nameSizer.Add(plotRB,0,wx.ALIGN_CENTER_VERTICAL)
1645            nameSizer.Add((5,0),)
1646            if not rbData['useCount']:
1647                delRB = wx.CheckBox(VectorRBDisplay,-1,'Delete?')
1648                Indx[delRB.GetId()] = rbId
1649                delRB.Bind(wx.EVT_CHECKBOX,OnDelRB)
1650                nameSizer.Add(delRB,0,wx.ALIGN_CENTER_VERTICAL)
1651            return nameSizer
1652           
1653        def rbRefAtmSizer(rbId,rbData):
1654           
1655            def OnRefSel(event):
1656                Obj = event.GetEventObject()
1657                iref = Indx[Obj.GetId()]
1658                sel = Obj.GetValue()
1659                rbData['rbRef'][iref] = atNames.index(sel)
1660                FillRefChoice(rbId,rbData)
1661           
1662            refAtmSizer = wx.BoxSizer(wx.HORIZONTAL)
1663            atNames = [name+str(i) for i,name in enumerate(rbData['rbTypes'])]
1664            rbRef = rbData.get('rbRef',[0,1,2,False])
1665            rbData['rbRef'] = rbRef
1666            if rbData['useCount']:
1667                refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1,
1668                    'Orientation reference atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \
1669                     atNames[rbRef[1]],atNames[rbRef[2]])),0)
1670            else:
1671                refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1,
1672                    'Orientation reference atoms A-B-C: '),0,wx.ALIGN_CENTER_VERTICAL)
1673                for i in range(3):
1674                    choices = [atNames[j] for j in refChoice[rbId][i]]
1675                    refSel = wx.ComboBox(VectorRBDisplay,-1,value='',
1676                        choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN)
1677                    refSel.SetValue(atNames[rbRef[i]])
1678                    refSel.Bind(wx.EVT_COMBOBOX, OnRefSel)
1679                    Indx[refSel.GetId()] = i
1680                    refAtmSizer.Add(refSel,0,wx.ALIGN_CENTER_VERTICAL)
1681            return refAtmSizer
1682                       
1683        def rbVectMag(rbId,imag,rbData):
1684           
1685            def OnRBVectorMag(event):
1686                event.Skip()
1687                Obj = event.GetEventObject()
1688                rbId,imag = Indx[Obj.GetId()]
1689                try:
1690                    val = float(Obj.GetValue())
1691                    if val <= 0.:
1692                        raise ValueError
1693                    rbData['VectMag'][imag] = val
1694                except ValueError:
1695                    pass
1696                Obj.SetValue('%8.4f'%(val))
1697                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1698                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbId],plotDefaults)
1699               
1700            def OnRBVectorRef(event):
1701                Obj = event.GetEventObject()
1702                rbId,imag = Indx[Obj.GetId()]
1703                rbData['VectRef'][imag] = Obj.GetValue()
1704                       
1705            magSizer = wx.wx.BoxSizer(wx.HORIZONTAL)
1706            magSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Translation magnitude: '),
1707                0,wx.ALIGN_CENTER_VERTICAL)
1708            magValue = wx.TextCtrl(VectorRBDisplay,-1,'%8.4f'%(rbData['VectMag'][imag]))
1709            Indx[magValue.GetId()] = [rbId,imag]
1710            magValue.Bind(wx.EVT_TEXT_ENTER,OnRBVectorMag)
1711            magValue.Bind(wx.EVT_KILL_FOCUS,OnRBVectorMag)
1712            magSizer.Add(magValue,0,wx.ALIGN_CENTER_VERTICAL)
1713            magSizer.Add((5,0),)
1714            magref = wx.CheckBox(VectorRBDisplay,-1,label=' Refine?') 
1715            magref.SetValue(rbData['VectRef'][imag])
1716            magref.Bind(wx.EVT_CHECKBOX,OnRBVectorRef)
1717            Indx[magref.GetId()] = [rbId,imag]
1718            magSizer.Add(magref,0,wx.ALIGN_CENTER_VERTICAL)
1719            return magSizer
1720           
1721        def rbVectors(rbId,imag,mag,XYZ,rbData):
1722
1723            def TypeSelect(event):
1724                AtInfo = data['Vector']['AtInfo']
1725                r,c = event.GetRow(),event.GetCol()
1726                if vecGrid.GetColLabelValue(c) == 'Type':
1727                    PE = G2elemGUI.PickElement(G2frame,oneOnly=True)
1728                    if PE.ShowModal() == wx.ID_OK:
1729                        if PE.Elem != 'None':
1730                            El = PE.Elem.strip().lower().capitalize()
1731                            if El not in AtInfo:
1732                                Info = G2elem.GetAtomInfo(El)
1733                                AtInfo[El] = [Info['Drad'],Info['Color']]
1734                            rbData['rbTypes'][r] = El
1735                            vecGrid.SetCellValue(r,c,El)
1736                    PE.Destroy()
1737                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1738
1739            def ChangeCell(event):
1740                r,c =  event.GetRow(),event.GetCol()
1741                if r >= 0 and (0 <= c < 3):
1742                    try:
1743                        val = float(vecGrid.GetCellValue(r,c))
1744                        rbData['rbVect'][imag][r][c] = val
1745                    except ValueError:
1746                        pass
1747                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbId],plotDefaults)
1748                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1749
1750            vecSizer = wx.BoxSizer()
1751            Types = 3*[wg.GRID_VALUE_FLOAT+':10,5',]+[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',]
1752            colLabels = ['Vector x','Vector y','Vector z','Type','Cart x','Cart y','Cart z']
1753            table = []
1754            rowLabels = []
1755            for ivec,xyz in enumerate(rbData['rbVect'][imag]):
1756                table.append(list(xyz)+[rbData['rbTypes'][ivec],]+list(XYZ[ivec]))
1757                rowLabels.append(str(ivec))
1758            vecTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1759            vecGrid = G2G.GSGrid(VectorRBDisplay)
1760            vecGrid.SetTable(vecTable, True)
1761            vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell)
1762            if not imag:
1763                vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect)
1764            attr = wx.grid.GridCellAttr()
1765            attr.SetEditor(G2G.GridFractionEditor(vecGrid))
1766            for c in range(3):
1767                vecGrid.SetColAttr(c, attr)
1768            for row in range(vecTable.GetNumberRows()):
1769                if imag:
1770                    vecGrid.SetCellStyle(row,3,VERY_LIGHT_GREY,True)                   
1771                for col in [4,5,6]:
1772                    vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True)
1773#            vecGrid.SetScrollRate(0,0)
1774            vecGrid.AutoSizeColumns(False)
1775            vecSizer.Add(vecGrid)
1776            return vecSizer
1777       
1778        def FillRefChoice(rbId,rbData):
1779            choiceIds = [i for i in range(len(rbData['rbTypes']))]
1780           
1781            rbRef = rbData.get('rbRef',[-1,-1,-1,False])
1782            for i in range(3):
1783                choiceIds.remove(rbRef[i])
1784            refChoice[rbId] = [choiceIds[:],choiceIds[:],choiceIds[:]]
1785            for i in range(3):
1786                refChoice[rbId][i].append(rbRef[i])
1787                refChoice[rbId][i].sort()     
1788           
1789        if VectorRB.GetSizer(): VectorRB.GetSizer().Clear(True)
1790        VectorRBSizer = wx.BoxSizer(wx.VERTICAL)
1791        for rbId in data['RBIds']['Vector']:
1792            if rbId != 'AtInfo':
1793                rbData = data['Vector'][rbId]
1794                FillRefChoice(rbId,rbData)
1795                VectorRBSizer.Add(rbNameSizer(rbId,rbData),0)
1796                VectorRBSizer.Add(rbRefAtmSizer(rbId,rbData),0)
1797                XYZ = np.array([[0.,0.,0.] for Ty in rbData['rbTypes']])
1798                for imag,mag in enumerate(rbData['VectMag']):
1799                    XYZ += mag*rbData['rbVect'][imag]
1800                    VectorRBSizer.Add(rbVectMag(rbId,imag,rbData),0)
1801                    VectorRBSizer.Add(rbVectors(rbId,imag,mag,XYZ,rbData),0)
1802                VectorRBSizer.Add((5,5),0)
1803                data['Vector'][rbId]['rbXYZ'] = XYZ       
1804        VectorRBSizer.Layout()   
1805        VectorRBDisplay.SetSizer(VectorRBSizer,True)
1806        Size = VectorRBSizer.GetMinSize()
1807        Size[0] += 40
1808        Size[1] = max(Size[1],450) + 20
1809        VectorRBDisplay.SetSize(Size)
1810        VectorRB.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1811        VectorRB.Scroll(0,Scroll)
1812       
1813    def UpdateResidueRB():
1814        AtInfo = data['Residue']['AtInfo']
1815        refChoice = {}
1816        RefObjs = []
1817
1818        def rbNameSizer(rbId,rbData):
1819
1820            def OnRBName(event):
1821                Obj = event.GetEventObject()
1822                rbData['RBname'] = Obj.GetValue()
1823               
1824            def OnDelRB(event):
1825                Obj = event.GetEventObject()
1826                rbId = Indx[Obj.GetId()]
1827                if rbId in data['Residue']: 
1828                    del data['Residue'][rbId]
1829                    data['RBIds']['Residue'].remove(rbId)
1830                wx.CallAfter(UpdateResidueRB)
1831               
1832            def OnStripH(event):
1833                Obj = event.GetEventObject()
1834                rbId = Indx[Obj.GetId()]
1835                if rbId in data['Residue']:
1836                    newNames = []
1837                    newTypes = []
1838                    newXYZ = []
1839                    for i,atype in enumerate(rbData['rbTypes']):
1840                        if atype != 'H':
1841                            newNames.append(rbData['atNames'][i])
1842                            newTypes.append(rbData['rbTypes'][i])
1843                            newXYZ.append(rbData['rbXYZ'][i])
1844                    rbData['atNames'] = newNames
1845                    rbData['rbTypes'] = newTypes
1846                    rbData['rbXYZ'] = newXYZ
1847                wx.CallAfter(UpdateResidueRB)
1848                   
1849            def OnPlotRB(event):
1850                Obj = event.GetEventObject()
1851                Obj.SetValue(False)
1852                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1853           
1854            nameSizer = wx.BoxSizer(wx.HORIZONTAL)
1855            nameSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Residue name: '),
1856                0,wx.ALIGN_CENTER_VERTICAL)
1857            RBname = wx.TextCtrl(ResidueRBDisplay,-1,rbData['RBname'])
1858            Indx[RBname.GetId()] = rbId
1859            RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName)
1860            RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName)
1861            nameSizer.Add(RBname,0,wx.ALIGN_CENTER_VERTICAL)
1862            nameSizer.Add((5,0),)
1863            plotRB = wx.CheckBox(ResidueRBDisplay,-1,'Plot?')
1864            Indx[plotRB.GetId()] = rbId
1865            plotRB.Bind(wx.EVT_CHECKBOX,OnPlotRB)
1866            nameSizer.Add(plotRB,0,wx.ALIGN_CENTER_VERTICAL)
1867            nameSizer.Add((5,0),)
1868            if not rbData['useCount']:
1869                delRB = wx.CheckBox(ResidueRBDisplay,-1,'Delete?')
1870                Indx[delRB.GetId()] = rbId
1871                delRB.Bind(wx.EVT_CHECKBOX,OnDelRB)
1872                nameSizer.Add(delRB,0,wx.ALIGN_CENTER_VERTICAL)
1873                if 'H'  in rbData['rbTypes']:
1874                    stripH = wx.CheckBox(ResidueRBDisplay,-1,'Strip H-atoms?')
1875                    Indx[stripH.GetId()] = rbId
1876                    stripH.Bind(wx.EVT_CHECKBOX,OnStripH)
1877                    nameSizer.Add(stripH,0,wx.ALIGN_CENTER_VERTICAL)
1878            return nameSizer
1879           
1880        def rbResidues(rbId,rbData):
1881           
1882            def TypeSelect(event):
1883                AtInfo = data['Residue']['AtInfo']
1884                r,c = event.GetRow(),event.GetCol()
1885                if vecGrid.GetColLabelValue(c) == 'Type':
1886                    PE = G2elemGUI.PickElement(G2frame,oneOnly=True)
1887                    if PE.ShowModal() == wx.ID_OK:
1888                        if PE.Elem != 'None':
1889                            El = PE.Elem.strip().lower().capitalize()
1890                            if El not in AtInfo:
1891                                Info = G2elem.GetAtomInfo(El)
1892                                AtInfo[El] = [Info['Drad']['Color']]
1893                            rbData['rbTypes'][r] = El
1894                            vecGrid.SetCellValue(r,c,El)
1895                    PE.Destroy()
1896
1897            def ChangeCell(event):
1898                r,c =  event.GetRow(),event.GetCol()
1899                if r >= 0 and (0 <= c < 3):
1900                    try:
1901                        val = float(vecGrid.GetCellValue(r,c))
1902                        rbData['rbXYZ'][r][c] = val
1903                    except ValueError:
1904                        pass
1905                       
1906            def RowSelect(event):
1907                r,c =  event.GetRow(),event.GetCol()
1908                if c < 0:                   #only row clicks
1909                    for vecgrid in resList:
1910                        vecgrid.ClearSelection()
1911                    vecGrid.SelectRow(r,True)
1912
1913            def OnRefSel(event):
1914                Obj = event.GetEventObject()
1915                iref,res,jref = Indx[Obj.GetId()]
1916                sel = Obj.GetValue()
1917                ind = atNames.index(sel)
1918                if rbData['rbTypes'][ind] == 'H':
1919                    G2G.G2MessageBox(G2frame,'You should not select an H-atom for rigid body orientation')
1920                rbData['rbRef'][iref] = ind
1921                FillRefChoice(rbId,rbData)
1922                for i,ref in enumerate(RefObjs[jref]):
1923                    ref.SetItems([atNames[j] for j in refChoice[rbId][i]])
1924                    ref.SetValue(atNames[rbData['rbRef'][i]])
1925                if not iref:     #origin change
1926                    rbXYZ = rbData['rbXYZ']
1927                    rbXYZ -= rbXYZ[ind]
1928                    res.ClearSelection()
1929                    resTable = res.GetTable()
1930                    for r in range(res.GetNumberRows()):
1931                        row = resTable.GetRowValues(r)
1932                        row[2:4] = rbXYZ[r]
1933                        resTable.SetRowValues(r,row)
1934                    res.ForceRefresh()
1935                    G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1936               
1937            Types = 2*[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',]
1938            colLabels = ['Name','Type','Cart x','Cart y','Cart z']
1939            table = []
1940            rowLabels = []
1941            for ivec,xyz in enumerate(rbData['rbXYZ']):
1942                table.append([rbData['atNames'][ivec],]+[rbData['rbTypes'][ivec],]+list(xyz))
1943                rowLabels.append(str(ivec))
1944            vecTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1945            vecGrid = G2G.GSGrid(ResidueRBDisplay)
1946            Indx[vecGrid.GetId()] = rbId
1947            resList.append(vecGrid)
1948            vecGrid.SetTable(vecTable, True)
1949            vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell)
1950            vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect)
1951            vecGrid.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK, RowSelect)
1952            attr = wx.grid.GridCellAttr()
1953            attr.SetEditor(G2G.GridFractionEditor(vecGrid))
1954            for c in range(3):
1955                vecGrid.SetColAttr(c, attr)
1956            for row in range(vecTable.GetNumberRows()):
1957                for col in range(5):
1958                    vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True)
1959#            vecGrid.SetScrollRate(0,0)
1960            vecGrid.AutoSizeColumns(False)
1961            vecSizer = wx.BoxSizer()
1962            vecSizer.Add(vecGrid)
1963           
1964            refAtmSizer = wx.BoxSizer(wx.HORIZONTAL)
1965            atNames = rbData['atNames']
1966            rbRef = rbData['rbRef']
1967            if rbData['rbRef'][3] or rbData['useCount']:
1968                refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
1969                    'Orientation reference non-H atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \
1970                     atNames[rbRef[1]],atNames[rbRef[2]])),0)
1971            else:
1972                refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
1973                    'Orientation reference non-H atoms A-B-C: '),0,wx.ALIGN_CENTER_VERTICAL)
1974                refObj = [0,0,0]
1975                for i in range(3):
1976                    choices = [atNames[j] for j in refChoice[rbId][i]]
1977                    refSel = wx.ComboBox(ResidueRBDisplay,-1,value='',
1978                        choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN)
1979                    refSel.SetValue(atNames[rbRef[i]])
1980                    refSel.Bind(wx.EVT_COMBOBOX, OnRefSel)
1981                    Indx[refSel.GetId()] = [i,vecGrid,len(RefObjs)]
1982                    refObj[i] = refSel
1983                    refAtmSizer.Add(refSel,0,wx.ALIGN_CENTER_VERTICAL)
1984                RefObjs.append(refObj)
1985           
1986            mainSizer = wx.BoxSizer(wx.VERTICAL)
1987            mainSizer.Add(refAtmSizer)
1988            mainSizer.Add(vecSizer)
1989            return mainSizer
1990           
1991        def SeqSizer(angSlide,rbId,iSeq,Seq,atNames):
1992           
1993            def ChangeAngle(event):
1994                event.Skip()
1995                Obj = event.GetEventObject()
1996                rbId,Seq = Indx[Obj.GetId()][:2]
1997                val = Seq[2]
1998                try:
1999                    val = float(Obj.GetValue())
2000                    Seq[2] = val
2001                except ValueError:
2002                    pass
2003                Obj.SetValue('%8.2f'%(val))
2004                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,data['Residue'][rbId],plotDefaults)
2005               
2006            def OnRadBtn(event):
2007                Obj = event.GetEventObject()
2008                Seq,iSeq,angId = Indx[Obj.GetId()]
2009                data['Residue'][rbId]['SelSeq'] = [iSeq,angId]
2010                angSlide.SetValue(int(100*Seq[2]))
2011               
2012            def OnDelBtn(event):
2013                Obj = event.GetEventObject()
2014                rbId,Seq = Indx[Obj.GetId()]
2015                data['Residue'][rbId]['rbSeq'].remove(Seq)       
2016                wx.CallAfter(UpdateResidueRB)
2017           
2018            seqSizer = wx.FlexGridSizer(0,5,2,2)
2019            seqSizer.AddGrowableCol(3,0)
2020            iBeg,iFin,angle,iMove = Seq
2021            ang = wx.TextCtrl(ResidueRBDisplay,-1,'%8.2f'%(angle),size=(50,20))
2022            if not iSeq:
2023                radBt = wx.RadioButton(ResidueRBDisplay,-1,'',style=wx.RB_GROUP)
2024                data['Residue'][rbId]['SelSeq'] = [iSeq,ang.GetId()]
2025            else:
2026                radBt = wx.RadioButton(ResidueRBDisplay,-1,'')
2027            radBt.Bind(wx.EVT_RADIOBUTTON,OnRadBtn)                   
2028            seqSizer.Add(radBt)
2029            delBt = wx.RadioButton(ResidueRBDisplay,-1,'')
2030            delBt.Bind(wx.EVT_RADIOBUTTON,OnDelBtn)
2031            seqSizer.Add(delBt)
2032            bond = wx.TextCtrl(ResidueRBDisplay,-1,'%s %s'%(atNames[iBeg],atNames[iFin]),size=(50,20))
2033            seqSizer.Add(bond,0,wx.ALIGN_CENTER_VERTICAL)
2034            Indx[radBt.GetId()] = [Seq,iSeq,ang.GetId()]
2035            Indx[delBt.GetId()] = [rbId,Seq]
2036            Indx[ang.GetId()] = [rbId,Seq,ang]
2037            ang.Bind(wx.EVT_TEXT_ENTER,ChangeAngle)
2038            ang.Bind(wx.EVT_KILL_FOCUS,ChangeAngle)
2039            seqSizer.Add(ang,0,wx.ALIGN_CENTER_VERTICAL)
2040            atms = ''
2041            for i in iMove:   
2042                atms += ' %s,'%(atNames[i])
2043            moves = wx.TextCtrl(ResidueRBDisplay,-1,atms[:-1],size=(200,20))
2044            seqSizer.Add(moves,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.RIGHT)
2045            return seqSizer
2046           
2047        def SlideSizer():
2048           
2049            def OnSlider(event):
2050                Obj = event.GetEventObject()
2051                rbData = Indx[Obj.GetId()]
2052                iSeq,angId = rbData['SelSeq']
2053                val = float(Obj.GetValue())/100.
2054                rbData['rbSeq'][iSeq][2] = val
2055                Indx[angId][2].SetValue('%8.2f'%(val))
2056                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
2057           
2058            slideSizer = wx.BoxSizer(wx.HORIZONTAL)
2059            slideSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Selected torsion angle:'),0)
2060            iSeq,angId = rbData['SelSeq']
2061            angSlide = wx.Slider(ResidueRBDisplay,-1,
2062                int(100*rbData['rbSeq'][iSeq][2]),0,36000,size=(200,20),
2063                style=wx.SL_HORIZONTAL)
2064            angSlide.Bind(wx.EVT_SLIDER, OnSlider)
2065            Indx[angSlide.GetId()] = rbData
2066            slideSizer.Add(angSlide,0)           
2067            return slideSizer,angSlide
2068           
2069        def FillRefChoice(rbId,rbData):
2070            choiceIds = [i for i in range(len(rbData['atNames']))]
2071            for seq in rbData['rbSeq']:
2072                for i in seq[3]:
2073                    try:
2074                        choiceIds.remove(i)
2075                    except ValueError:
2076                        pass
2077            rbRef = rbData['rbRef']
2078            for i in range(3):
2079                try:
2080                    choiceIds.remove(rbRef[i])
2081                except ValueError:
2082                    pass
2083            refChoice[rbId] = [choiceIds[:],choiceIds[:],choiceIds[:]]
2084            for i in range(3):
2085                refChoice[rbId][i].append(rbRef[i])
2086                refChoice[rbId][i].sort()     
2087           
2088        ResidueRBDisplay.DestroyChildren()
2089        ResidueRBSizer = wx.BoxSizer(wx.VERTICAL)
2090        for rbId in data['RBIds']['Residue']:
2091            rbData = data['Residue'][rbId]
2092            if len(rbData['rbXYZ']) < 3:    #patch - skip around bad RBs with too few atoms
2093                continue
2094            FillRefChoice(rbId,rbData)
2095            ResidueRBSizer.Add(rbNameSizer(rbId,rbData),0)
2096            ResidueRBSizer.Add(rbResidues(rbId,rbData),0)
2097            ResidueRBSizer.Add((5,5),0)
2098            if rbData['rbSeq']:
2099                slideSizer,angSlide = SlideSizer()
2100            if len(rbData['rbSeq']):
2101                ResidueRBSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
2102                    'Sel  Del  Bond             Angle      Riding atoms'),
2103                    0,wx.ALIGN_CENTER_VERTICAL)                       
2104            for iSeq,Seq in enumerate(rbData['rbSeq']):
2105                ResidueRBSizer.Add(SeqSizer(angSlide,rbId,iSeq,Seq,rbData['atNames']))
2106            if rbData['rbSeq']:
2107                ResidueRBSizer.Add(slideSizer,)
2108            ResidueRBSizer.Add(wx.StaticText(ResidueRBDisplay,-1,70*'-'))
2109
2110        ResidueRBSizer.Add((5,25),)
2111        ResidueRBSizer.Layout()   
2112        ResidueRBDisplay.SetSizer(ResidueRBSizer,True)
2113        Size = ResidueRBSizer.GetMinSize()
2114        Size[0] += 40
2115        Size[1] = max(Size[1],450) + 20
2116        ResidueRBDisplay.SetSize(Size)
2117        ResidueRB.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
2118       
2119    def SetStatusLine(text):
2120        G2frame.GetStatusBar().SetStatusText(text,1)                                     
2121
2122    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RigidBodyMenu)
2123    G2frame.SetTitle('Rigid bodies')
2124    SetStatusLine('')
2125    UpdateVectorRB()
2126    G2frame.rbBook.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged)
2127    wx.CallAfter(OnPageChanged,None)
Note: See TracBrowser for help on using the repository browser.