source: trunk/GSASIIconstrGUI.py @ 2691

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

fix Transform - cell constraints were wrong
needed to clear newPhaseDrawing? for new transformed phase & do a UsedHistogramsAndPhases?

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 91.9 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIconstrGUI - constraint GUI routines
3########### SVN repository information ###################
4# $Date: 2017-02-04 16:04:46 +0000 (Sat, 04 Feb 2017) $
5# $Author: vondreele $
6# $Revision: 2691 $
7# $URL: trunk/GSASIIconstrGUI.py $
8# $Id: GSASIIconstrGUI.py 2691 2017-02-04 16:04:46Z 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: 2691 $")
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 GSASIIgrid as G2gd
34import GSASIIctrls 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.PatternTree.GetItemPyData(
227        G2gd.GetPatternTreeItemId(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.dataFrame,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.dataFrame,
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.dataFrame,
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.dataFrame,
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.dataFrame,
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.dataFrame,
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.dataFrame)
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(
665            G2frame.dataFrame,
666            legend,title1,varListlbl,toggle=False,size=(625,400),monoFont=True)
667        dlg.CenterOnParent()
668        if dlg.ShowModal() == wx.ID_OK:
669            for sel in dlg.GetSelections():
670                Varb = varList[sel]
671                VarObj = G2obj.G2VarObj(Varb)
672                newcons = [[[0.0,VarObj],None,None,'h']]
673                if CheckAddedConstraint(newcons):
674                    data[constrDictEnt] += newcons
675        dlg.Destroy()
676        wx.CallAfter(OnPageChanged,None)
677       
678    def OnAddEquivalence(event):
679        '''add an Equivalence constraint'''
680        page = G2frame.Page
681        vartype,varList,constrDictEnt = PageSelection(page)
682        title1 = "Setup equivalent "+vartype+" variables"
683        title2 = "Select additional "+vartype+" variable(s) to be equivalent with "
684        if not varList:
685            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
686                                parent=G2frame.dataFrame)
687            return
688#        legend = "Select variables to make equivalent (only one of the variables will be varied when all are set to be varied)"
689        GetAddVars(page,title1,title2,varList,constrDictEnt,'equivalence')
690       
691    def OnAddAtomEquiv(event):
692        ''' Add equivalences between all parameters on atoms '''
693        page = G2frame.Page
694        vartype,varList,constrDictEnt = PageSelection(page)
695        title1 = "Setup equivalent atom variables"
696        title2 = "Select additional atoms(s) to be equivalent with "
697        if not varList:
698            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
699                                parent=G2frame.dataFrame)
700            return
701#        legend = "Select atoms to make equivalent (only one of the atom variables will be varied when all are set to be varied)"
702        GetAddAtomVars(page,title1,title2,varList,constrDictEnt,'equivalence')
703       
704    def OnAddRiding(event):
705        ''' Add riding equivalences between all parameters on atoms '''
706        page = G2frame.Page
707        vartype,varList,constrDictEnt = PageSelection(page)
708        title1 = "Setup riding atoms "
709        title2 = "Select additional atoms(s) to ride on "
710        if not varList:
711            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
712                                parent=G2frame.dataFrame)
713            return
714#        legend = "Select atoms to ride (only one of the atom variables will be varied when all are set to be varied)"
715        GetAddAtomVars(page,title1,title2,varList,constrDictEnt,'riding')
716   
717    def OnAddFunction(event):
718        '''add a Function (new variable) constraint'''
719        page = G2frame.Page
720        vartype,varList,constrDictEnt = PageSelection(page)
721        title1 = "Setup new variable based on "+vartype+" variables"
722        title2 = "Include additional "+vartype+" variable(s) to be included with "
723        if not varList:
724            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
725                                parent=G2frame.dataFrame)
726            return
727#        legend = "Select variables to include in a new variable (the new variable will be varied when all included variables are varied)"
728        GetAddVars(page,title1,title2,varList,constrDictEnt,'function')
729                       
730    def OnAddConstraint(event):
731        '''add a constraint equation to the constraints list'''
732        page = G2frame.Page
733        vartype,varList,constrDictEnt = PageSelection(page)
734        title1 = "Setup constraint on "+vartype+" variables"
735        title2 = "Select additional "+vartype+" variable(s) to include in constraint with "
736        if not varList:
737            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
738                                parent=G2frame.dataFrame)
739            return
740#        legend = "Select variables to include in a constraint equation (the values will be constrainted to equal a specified constant)"
741        GetAddVars(page,title1,title2,varList,constrDictEnt,'constraint')
742
743    def GetAddVars(page,title1,title2,varList,constrDictEnt,constType):
744        '''Get the variables to be added for OnAddEquivalence, OnAddFunction,
745        and OnAddConstraint. Then create and check the constraint.
746        '''
747        #varListlbl = ["("+i+") "+G2obj.fmtVarDescr(i) for i in varList]
748        l2 = l1 = 1
749        for i in varList:
750            l1 = max(l1,len(i))
751            loc,desc = G2obj.VarDescr(i)
752            l2 = max(l2,len(loc))
753        fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}"
754        varListlbl = [fmt.format(i,*G2obj.VarDescr(i)) for i in varList]       
755        dlg = G2G.G2SingleChoiceDialog(G2frame.dataFrame,'Select 1st variable:',
756                                      title1,varListlbl,
757                                      monoFont=True,size=(625,400))
758        dlg.CenterOnParent()
759        if dlg.ShowModal() == wx.ID_OK:
760            sel = dlg.GetSelection()
761            FrstVarb = varList[sel]
762            VarObj = G2obj.G2VarObj(FrstVarb)
763            moreVarb = FindEquivVarb(FrstVarb,varList)
764            newcons = SelectVarbs(page,VarObj,moreVarb,title2+FrstVarb,constType)
765            if len(newcons) > 0:
766                if CheckAddedConstraint(newcons):
767                    data[constrDictEnt] += newcons
768        dlg.Destroy()
769        wx.CallAfter(OnPageChanged,None)
770                       
771    def FindNeighbors(phase,FrstName,AtNames):
772        General = phase['General']
773        cx,ct,cs,cia = General['AtomPtrs']
774        Atoms = phase['Atoms']
775        atNames = [atom[ct-1] for atom in Atoms]
776        Cell = General['Cell'][1:7]
777        Amat,Bmat = G2lat.cell2AB(Cell)
778        atTypes = General['AtomTypes']
779        Radii = np.array(General['BondRadii'])
780        AtInfo = dict(zip(atTypes,Radii)) #or General['BondRadii']
781        Orig = atNames.index(FrstName.split()[1])
782        OType = Atoms[Orig][ct]
783        XYZ = G2mth.getAtomXYZ(Atoms,cx)       
784        Neigh = []
785        Dx = np.inner(Amat,XYZ-XYZ[Orig]).T
786        dist = np.sqrt(np.sum(Dx**2,axis=1))
787        sumR = AtInfo[OType]+0.5    #H-atoms only!
788        IndB = ma.nonzero(ma.masked_greater(dist-0.85*sumR,0.))
789        for j in IndB[0]:
790            if j != Orig:
791                Neigh.append(AtNames[j])
792        return Neigh
793       
794    def GetAddAtomVars(page,title1,title2,varList,constrDictEnt,constType):
795        '''Get the atom variables to be added for OnAddAtomEquiv. Then create and
796        check the constraints. Riding for H atoms only.
797        '''
798        Atoms = {G2obj.VarDescr(i)[0]:[] for i in varList if 'Atom' in G2obj.VarDescr(i)[0]}
799        for item in varList:
800            atName = G2obj.VarDescr(item)[0]
801            if atName in Atoms:
802                Atoms[atName].append(item)
803        AtNames = Atoms.keys()
804        AtNames.sort()
805        dlg = G2G.G2SingleChoiceDialog(G2frame.dataFrame,'Select 1st atom:',
806                                      title1,AtNames,
807                                      monoFont=True,size=(625,400))
808        dlg.CenterOnParent()
809        FrstAtom = ''
810        if dlg.ShowModal() == wx.ID_OK:
811            sel = dlg.GetSelection()
812            FrstAtom = AtNames[sel]
813            if 'riding' in constType:
814                phaseName = (FrstAtom.split(' in ')[1]).strip()
815                phase = Phases[phaseName]
816                AtNames = FindNeighbors(phase,FrstAtom,AtNames)
817            else:
818                AtNames.remove(FrstAtom)
819        dlg.Destroy()
820        if FrstAtom == '':
821            print 'no atom selected'
822            return
823        dlg = G2G.G2MultiChoiceDialog(
824            G2frame.dataFrame,title2+FrstAtom,
825            'Constrain '+str(FrstAtom)+' with...',AtNames,
826            toggle=False,size=(625,400),monoFont=True)
827        if dlg.ShowModal() == wx.ID_OK:
828            Selections = dlg.GetSelections()[:]
829        else:
830            print 'no target atom selected'
831            dlg.Destroy()
832            return
833        dlg.Destroy()
834        for name in Atoms[FrstAtom]:
835            newcons = []
836            constr = []
837            if 'riding' in constType:
838                if 'AUiso' in name:
839                    constr = [[1.0,G2obj.G2VarObj(name)]]
840                elif 'AU11' in name:
841                    pass
842                elif 'AU' not in name:
843                    constr = [[1.0,G2obj.G2VarObj(name)]]
844            else:
845                constr = [[1.0,G2obj.G2VarObj(name)]]
846            pref = ':'+name.rsplit(':',1)[0].split(':',1)[1]    #get stuff between phase id & atom id
847            for sel in Selections:
848                name2 = Atoms[AtNames[sel]][0]
849                pid = name2.split(':',1)[0]                     #get phase id for 2nd atom
850                id = name2.rsplit(':',1)[-1]                    #get atom no. for 2nd atom
851                if 'riding' in constType:
852                    pref = pid+pref
853                    if 'AUiso' in pref:
854                        parts = pref.split('AUiso')
855                        constr += [[1.2,G2obj.G2VarObj('%s:%s'%(parts[0]+'AUiso',id))]]
856                    elif 'AU' not in pref:
857                        constr += [[1.0,G2obj.G2VarObj('%s:%s'%(pref,id))]]
858                else:
859                    constr += [[1.0,G2obj.G2VarObj('%s:%s'%(pid+pref,id))]]
860            if not constr:
861                continue
862            if 'frac' in pref and 'riding' not in constType:
863                newcons = [constr+[1.0,None,'c']]
864            else:
865                newcons = [constr+[None,None,'e']]
866            if len(newcons) > 0:
867                if CheckAddedConstraint(newcons):
868                    data[constrDictEnt] += newcons
869        wx.CallAfter(OnPageChanged,None)
870                       
871    def MakeConstraintsSizer(name,pageDisplay):
872        '''Creates a sizer displaying all of the constraints entered of
873        the specified type.
874
875        :param str name: the type of constraints to be displayed ('HAP',
876          'Hist', 'Phase', or 'Global')
877        :param wx.Panel pageDisplay: parent panel for sizer
878        :returns: wx.Sizer created by method
879        '''
880        constSizer = wx.FlexGridSizer(0,6,0,0)
881        maxlen = 70 # characters before wrapping a constraint
882        for Id,item in enumerate(data[name]):
883            refineflag = False
884            helptext = ""
885            eqString = ['',]
886            if item[-1] == 'h': # Hold on variable
887                constSizer.Add((-1,-1),0)              # blank space for edit button
888                typeString = 'FIXED'
889                var = str(item[0][1])
890                varMean = G2obj.fmtVarDescr(var)
891                eqString[-1] =  var +'   '
892                helptext = "Prevents variable:\n"+ var + " ("+ varMean + ")\nfrom being changed"
893            elif isinstance(item[-1],str): # not true on original-style (2011?) constraints
894                constEdit = wx.Button(pageDisplay,-1,'Edit',style=wx.BU_EXACTFIT)
895                constEdit.Bind(wx.EVT_BUTTON,OnConstEdit)
896                constSizer.Add(constEdit,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)            # edit button
897                Indx[constEdit.GetId()] = [Id,name]
898                if item[-1] == 'f':
899                    helptext = "A new variable"
900                    if item[-3]:
901                        helptext += " named "+str(item[-3])
902                    helptext += " is created from a linear combination of the following variables:\n"
903                    for term in item[:-3]:
904                        var = str(term[1])
905                        if len(eqString[-1]) > maxlen:
906                            eqString.append(' ')
907                        m = term[0]
908                        if eqString[-1] != '':
909                            if m >= 0:
910                                eqString[-1] += ' + '
911                            else:
912                                eqString[-1] += ' - '
913                                m = abs(m)
914                        eqString[-1] += '%.3f*%s '%(m,var)
915                        varMean = G2obj.fmtVarDescr(var)
916                        helptext += "\n" + var + " ("+ varMean + ")"
917                    if '_Explain' in data:
918                        if data['_Explain'].get(item[-3]):
919                            helptext += '\n\n'
920                            helptext += data['_Explain'][item[-3]]
921                    # typeString = 'NEWVAR'
922                    # if item[-3]:
923                    #     eqString[-1] += ' = '+item[-3]
924                    # else:
925                    #     eqString[-1] += ' = New Variable'
926                    if item[-3]:
927                        typeString = item[-3] + ' = '
928                    else:
929                        typeString = 'New Variable = '
930                    #print 'refine',item[-2]
931                    refineflag = True
932                elif item[-1] == 'c':
933                    helptext = "The following variables constrained to equal a constant:"
934                    for term in item[:-3]:
935                        var = str(term[1])
936                        if len(eqString[-1]) > maxlen:
937                            eqString.append(' ')
938                        if eqString[-1] != '':
939                            if term[0] > 0:
940                                eqString[-1] += ' + '
941                            else:
942                                eqString[-1] += ' - '
943                        eqString[-1] += '%.3f*%s '%(abs(term[0]),var)
944                        varMean = G2obj.fmtVarDescr(var)
945                        helptext += "\n" + var + " ("+ varMean + ")"
946                    typeString = 'CONST'
947                    eqString[-1] += ' = '+str(item[-3])
948                elif item[-1] == 'e':
949                    helptext = "The following variables are set to be equivalent, noting multipliers:"
950                    for term in item[:-3]:
951                        var = str(term[1])
952                        if term[0] == 0: term[0] = 1.0
953                        if len(eqString[-1]) > maxlen:
954                            eqString.append(' ')
955                        if eqString[-1] == '':
956                            eqString[-1] += var+' '
957                            first = term[0]
958                        else:
959                            eqString[-1] += ' = %.3f*%s '%(first/term[0],var)
960                        varMean = G2obj.fmtVarDescr(var)
961                        helptext += "\n" + var + " ("+ varMean + ")"
962                    typeString = 'EQUIV'
963                else:
964                    print 'Unexpected constraint',item
965               
966            else:
967                print 'Removing old-style constraints'
968                data[name] = []
969                return constSizer
970            constDel = wx.Button(pageDisplay,-1,'Delete',style=wx.BU_EXACTFIT)
971            constDel.Bind(wx.EVT_BUTTON,OnConstDel)
972            Indx[constDel.GetId()] = [Id,name]
973            constSizer.Add(constDel,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)             # delete button
974            if helptext:
975                ch = G2G.HelpButton(pageDisplay,helptext)
976                constSizer.Add(ch,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)
977            else:
978                constSizer.Add((-1,-1))
979            if refineflag:
980                ch = G2G.G2CheckBox(pageDisplay,'',item,-2)
981                constSizer.Add(ch,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)
982            else:
983                constSizer.Add((-1,-1))               
984            constSizer.Add(wx.StaticText(pageDisplay,-1,typeString),
985                           0,wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT,3)
986            if len(eqString) > 1:
987                Eq = wx.BoxSizer(wx.VERTICAL)
988                for s in eqString:
989                    Eq.Add(wx.StaticText(pageDisplay,-1,s),0,wx.ALIGN_CENTER_VERTICAL)
990            else:
991                Eq = wx.StaticText(pageDisplay,-1,eqString[0])
992            constSizer.Add(Eq,1,wx.ALIGN_CENTER_VERTICAL)
993        return constSizer
994               
995    def OnConstDel(event):
996        'Delete a constraint'
997        Obj = event.GetEventObject()
998        Id,name = Indx[Obj.GetId()]
999        del(data[name][Id])
1000        wx.CallAfter(OnPageChanged,None)
1001       
1002    def OnConstEdit(event):
1003        '''Called to edit an individual contraint in response to a
1004        click on its Edit button
1005        '''
1006        Obj = event.GetEventObject()
1007        Id,name = Indx[Obj.GetId()]
1008        if data[name][Id][-1] == 'f':
1009            items = data[name][Id][:-3]
1010            constType = 'New Variable'
1011            if data[name][Id][-3]:
1012                varname = data[name][Id][-3]
1013            else:
1014                varname = ""
1015            lbl = 'Enter value for each term in constraint; sum = new variable'
1016            dlg = ConstraintDialog(G2frame.dataFrame,constType,lbl,items,
1017                                   varname=varname,varyflag=data[name][Id][-2])
1018        elif data[name][Id][-1] == 'c':
1019            items = data[name][Id][:-3]+[
1020                [data[name][Id][-3],'fixed value =']]
1021            constType = 'Constraint'
1022            lbl = 'Edit value for each term in constant constraint sum'
1023            dlg = ConstraintDialog(G2frame.dataFrame,constType,lbl,items)
1024        elif data[name][Id][-1] == 'e':
1025            items = data[name][Id][:-3]
1026            constType = 'Equivalence'
1027            lbl = 'The following terms are set to be equal:'
1028            dlg = ConstraintDialog(G2frame.dataFrame,constType,lbl,items,'/')
1029        else:
1030            return
1031        try:
1032            prev = data[name][Id][:]
1033            if dlg.ShowModal() == wx.ID_OK:
1034                result = dlg.GetData()
1035                for i in range(len(data[name][Id][:-3])):
1036                    if type(data[name][Id][i]) is tuple: # fix non-mutable construct
1037                        data[name][Id][i] = list(data[name][Id][i])
1038                    data[name][Id][i][0] = result[i][0]
1039                if data[name][Id][-1] == 'c':
1040                    data[name][Id][-3] = str(result[-1][0])
1041                elif data[name][Id][-1] == 'f':
1042                    # process the variable name to put in global form (::var)
1043                    varname = str(dlg.newvar[0]).strip().replace(' ','_')
1044                    if varname.startswith('::'):
1045                        varname = varname[2:]
1046                    varname = varname.replace(':',';')
1047                    if varname:
1048                        data[name][Id][-3] = varname
1049                    else:
1050                        data[name][Id][-3] = ''
1051                    data[name][Id][-2] = dlg.newvar[1]
1052                if not CheckChangedConstraint():
1053                    data[name][Id] = prev
1054        except:
1055            import traceback
1056            print traceback.format_exc()
1057        finally:
1058            dlg.Destroy()
1059        wx.CallAfter(OnPageChanged,None)
1060   
1061    def UpdateConstraintPanel(panel,typ):
1062        '''Update the contents of the selected Constraint
1063        notebook tab. Called in :func:`OnPageChanged`
1064        '''
1065        if panel.GetSizer(): panel.GetSizer().Clear(True) # N.B. don't use panel.DestroyChildren()
1066        # because it deletes scrollbars on Mac
1067        Siz = wx.BoxSizer(wx.VERTICAL)
1068        Siz.Add((5,5),0)
1069        Siz.Add(MakeConstraintsSizer(typ,panel))
1070        panel.SetSizer(Siz,True)
1071        Size = Siz.GetMinSize()
1072        Size[0] += 40
1073        Size[1] = max(Size[1],450) + 20
1074        panel.SetSize(Size)
1075        panel.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1076        Size[1] = min(500,Size[1])
1077        G2frame.dataFrame.setSizePosLeft(Size)
1078
1079    def OnPageChanged(event):
1080        '''Called when a tab is pressed or when a "select tab" menu button is
1081        used (see RaisePage), or to refresh the current tab contents (event=None)
1082        '''
1083        if event:       #page change event!
1084            page = event.GetSelection()
1085        else: # called directly, get current page
1086            page = G2frame.dataDisplay.GetSelection()
1087        G2frame.dataDisplay.ChangeSelection(page)
1088        text = G2frame.dataDisplay.GetPageText(page)
1089        G2frame.dataFrame.ConstraintEdit.Enable(G2gd.wxID_EQUIVALANCEATOMS,False)
1090#        G2frame.dataFrame.ConstraintEdit.Enable(G2gd.wxID_ADDRIDING,False)
1091        if text == 'Histogram/Phase':
1092            G2frame.Page = [page,'hap']
1093            UpdateConstraintPanel(HAPConstr,'HAP')
1094        elif text == 'Histogram':
1095            G2frame.Page = [page,'hst']
1096            UpdateConstraintPanel(HistConstr,'Hist')
1097        elif text == 'Phase':
1098            G2frame.Page = [page,'phs']
1099            G2frame.dataFrame.ConstraintEdit.Enable(G2gd.wxID_EQUIVALANCEATOMS,True)
1100#            G2frame.dataFrame.ConstraintEdit.Enable(G2gd.wxID_ADDRIDING,True)
1101            if 'DELETED' in str(PhaseConstr):   #seems to be no other way to do this (wx bug)
1102                if GSASIIpath.GetConfigValue('debug'):
1103                    print 'wx error: PhaseConstr not cleanly deleted after Refine'
1104                return
1105            UpdateConstraintPanel(PhaseConstr,'Phase')
1106        elif text == 'Global':
1107            G2frame.Page = [page,'glb']
1108            UpdateConstraintPanel(GlobalConstr,'Global')
1109
1110    def RaisePage(event):
1111        'Respond to a "select tab" menu button'
1112        try:
1113            i = (G2gd.wxID_CONSPHASE,
1114                 G2gd.wxID_CONSHAP,
1115                 G2gd.wxID_CONSHIST,
1116                 G2gd.wxID_CONSGLOBAL).index(event.GetId())
1117            G2frame.dataDisplay.SetSelection(i)
1118            wx.CallAfter(OnPageChanged,None)
1119        except ValueError:
1120            print('Unexpected event in RaisePage')
1121
1122    def SetStatusLine(text):
1123        Status.SetStatusText(text)                                     
1124       
1125    if G2frame.dataDisplay:
1126        G2frame.dataDisplay.Destroy()
1127    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.ConstraintMenu)
1128    G2frame.dataFrame.SetLabel('Constraints')
1129    if not G2frame.dataFrame.GetStatusBar():
1130        Status = G2frame.dataFrame.CreateStatusBar()
1131    SetStatusLine('')
1132   
1133    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.ConstraintMenu)
1134    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAddConstraint, id=G2gd.wxID_CONSTRAINTADD)
1135    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAddFunction, id=G2gd.wxID_FUNCTADD)
1136    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAddEquivalence, id=G2gd.wxID_EQUIVADD)
1137    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAddHold, id=G2gd.wxID_HOLDADD)
1138    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAddAtomEquiv, id=G2gd.wxID_EQUIVALANCEATOMS)
1139#    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAddRiding, id=G2gd.wxID_ADDRIDING)
1140    # tab commands
1141    for id in (G2gd.wxID_CONSPHASE,
1142               G2gd.wxID_CONSHAP,
1143               G2gd.wxID_CONSHIST,
1144               G2gd.wxID_CONSGLOBAL):
1145        G2frame.dataFrame.Bind(wx.EVT_MENU, RaisePage,id=id)
1146
1147    G2frame.dataDisplay = G2G.GSNoteBook(parent=G2frame.dataFrame)
1148    # note that order of pages is hard-coded in RaisePage
1149    PhaseConstr = wx.ScrolledWindow(G2frame.dataDisplay)
1150    G2frame.dataDisplay.AddPage(PhaseConstr,'Phase')
1151    HAPConstr = wx.ScrolledWindow(G2frame.dataDisplay)
1152    G2frame.dataDisplay.AddPage(HAPConstr,'Histogram/Phase')
1153    HistConstr = wx.ScrolledWindow(G2frame.dataDisplay)
1154    G2frame.dataDisplay.AddPage(HistConstr,'Histogram')
1155    GlobalConstr = wx.ScrolledWindow(G2frame.dataDisplay)
1156    G2frame.dataDisplay.AddPage(GlobalConstr,'Global')
1157    wx.CallAfter(OnPageChanged,None)
1158    G2frame.dataDisplay.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged)
1159    # validate all the constrants -- should not see any errors here normally
1160    allcons = []
1161    for key in data:
1162        if key.startswith('_'): continue
1163        allcons += data[key]
1164    if not len(allcons): return
1165    G2mv.InitVars()   
1166    constDictList,fixedList,ignored = G2stIO.ProcessConstraints(allcons)
1167    errmsg, warnmsg = G2mv.CheckConstraints('',constDictList,fixedList)
1168    if errmsg:
1169        G2frame.ErrorDialog('Constraint Error','Error in constraints:\n'+errmsg,
1170            parent=G2frame.dataFrame)
1171    elif warnmsg:
1172        print 'Unexpected contraint warning:\n',warnmsg
1173
1174################################################################################
1175#### Make nuclear-magnetic phase constraints - called by OnTransform in G2phsGUI
1176################################################################################       
1177       
1178def MagConstraints(G2frame,oldPhase,newPhase,Trans,Vec,atCodes):
1179    '''Add constraints for new magnetic phase created via transformation of old
1180    nuclear one
1181    NB: A = [G11,G22,G33,2*G12,2*G13,2*G23]
1182    '''
1183   
1184    def SetUniqAj(pId,iA,Aname,SGLaue):
1185        if SGLaue in ['4/m','4/mmm'] and iA in [0,1]:
1186            parm = '%d::%s'%(pId,'A0')
1187        elif SGLaue in ['m3','m3m'] and iA in [0,1,2]:
1188            parm = '%d::%s'%(pId,'A0')
1189        elif SGLaue in ['6/m','6/mmm','3m1', '31m', '3'] and iA in [0,1,3]:
1190            parm = '%d::%s'%(pId,'A0')
1191        elif SGLaue in ['3R', '3mR']:
1192            if ia in [0,1,2]:
1193                parm = '%d::%s'%(pId,'A0')
1194            else:
1195                parm = '%d::%s'%(pId,'A3')
1196        else:
1197            parm = '%d::%s'%(pId,Aname)
1198        return parm
1199       
1200    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
1201    UseList = newPhase['Histograms']
1202    detTrans = np.abs(nl.det(Trans))
1203    invTrans = nl.inv(Trans)
1204#    print 'invTrans',invTrans
1205    nAcof = G2lat.cell2A(newPhase['General']['Cell'][1:7])
1206   
1207    opId = oldPhase['pId']
1208    npId = newPhase['pId']
1209    cx,ct,cs,cia = newPhase['General']['AtomPtrs']
1210    nAtoms = newPhase['Atoms']
1211    oSGData = oldPhase['General']['SGData']
1212    nSGData = newPhase['General']['SGData']
1213    oAcof = G2lat.cell2A(oldPhase['General']['Cell'][1:7])
1214    nAcof = G2lat.cell2A(newPhase['General']['Cell'][1:7])
1215    item = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,'Constraints') 
1216    constraints = G2frame.PatternTree.GetItemPyData(item)
1217#    GSASIIpath.IPyBreak()
1218    parmDict = {}
1219    varyList = []
1220    for ia,code in enumerate(atCodes):
1221        atom = nAtoms[ia]
1222        siteSym = G2spc.SytSym(atom[cx:cx+3],nSGData)[0]
1223        CSX = G2spc.GetCSxinel(siteSym)
1224        item = code.split('+')[0]
1225        iat,opr = item.split(':')
1226        Nop = abs(int(opr))%100-1
1227        if '-' in opr:
1228            Nop *= -1
1229        Opr = oldPhase['General']['SGData']['SGOps'][abs(Nop)][0]
1230        if Nop < 0:         #inversion
1231            Opr *= -1
1232        XOpr = np.inner(Opr,Trans.T)
1233        names = ['dAx','dAy','dAz']
1234        for ix,name in enumerate(names):
1235            IndpCon = [1.0,G2obj.G2VarObj('%d::%s:%s'%(opId,name,iat))]
1236            DepCons = []
1237            for iop,opval in enumerate(XOpr[ix]):
1238                if opval and CSX[0][ix]:    #-opval from defn of dAx, etc.
1239                    DepCons.append([-opval,G2obj.G2VarObj('%d::%s:%d'%(npId,names[iop],ia))])
1240            if len(DepCons) == 1:
1241                constraints['Phase'].append([IndpCon,DepCons[0],None,None,'e'])
1242            elif len(DepCons) > 1:
1243                for Dep in DepCons:
1244                    Dep[0] *= -1
1245                constraints['Phase'].append([IndpCon]+DepCons+[0.0,None,'c'])
1246        for name in ['Afrac','AUiso']:
1247            IndpCon = [1.0,G2obj.G2VarObj('%d::%s:%s'%(opId,name,iat))]
1248            DepCons = [1.0,G2obj.G2VarObj('%d::%s:%d'%(npId,name,ia))]
1249            constraints['Phase'].append([IndpCon,DepCons,None,None,'e'])
1250        #how do I do Uij's for most Trans?
1251    Anames = [['A0','A3','A4'],['A3','A1','A5'],['A4','A5','A2']]
1252    As = ['A0','A1','A2','A3','A4','A5']
1253    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]]
1254    Axes = ['a','b','c']
1255    Holds = []
1256    for iA,Aid in enumerate(Aids):
1257        parm = SetUniqAj(opId,iA,Aid[2],oSGData['SGLaue'])
1258        parmDict[parm] = oAcof[iA]
1259        varyList.append(parm)
1260        IndpCon = [1.0,G2obj.G2VarObj(parm)]
1261        DepCons = []
1262        for iat in range(3):
1263            if nSGData['SGLaue'] in ['-1','2/m']:       #set holds
1264                if (abs(nAcof[iA]) < 1.e-8) and (abs(Trans[Aid[0],Aid[1]]) < 1.e-8):
1265                    if Axes[iat] != oSGData['SGUniq'] and oSGData['SGLaue'] != nSGData['SGLaue']:
1266                        HoldObj = G2obj.G2VarObj('%d::%s'%(npId,Aid[2]))
1267                        if not HoldObj in Holds: 
1268                            constraints['Phase'].append([[0.0,HoldObj],None,None,'h'])
1269                            Holds.append(HoldObj)
1270                            continue
1271#            print iA,Aid,iat,invTrans[iat][Aid[0]],invTrans[Aid[1]][iat],Anames[Aid[0]][Aid[1]],parm
1272            if abs(invTrans[iat,Aid[1]]) > 1.e-8 and abs(nAcof[iA]) > 1.e-8:
1273                parm = SetUniqAj(npId,iA,Anames[Aid[0]][Aid[1]],nSGData['SGLaue'])
1274                parmDict[parm] = nAcof[As.index(Aid[2])]
1275                if not parm in varyList:
1276                    varyList.append(parm)
1277                DepCons.append([Trans[Aid[0],Aid[0]]*Trans[Aid[1],Aid[1]],G2obj.G2VarObj(parm)])
1278        if len(DepCons) == 1:
1279            constraints['Phase'].append([IndpCon,DepCons[0],None,None,'e'])
1280        elif len(DepCons) > 1:       
1281            for Dep in DepCons:
1282                Dep[0] *= -1
1283            constraints['Phase'].append([IndpCon]+DepCons+[0.0,None,'c'])
1284#    constDict,fixedList,ignored = G2stIO.ProcessConstraints(constraints['Phase'])
1285#    groups,parmlist = G2mv.GroupConstraints(constDict)
1286#    G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList,parmDict)
1287#    print 'old',parmDict
1288#    G2mv.Dict2Map(parmDict,varyList)
1289#    print 'new',parmDict
1290    for hId,hist in enumerate(UseList):    #HAP - seems OK
1291        ohapkey = '%d:%d:'%(opId,hId)
1292        nhapkey = '%d:%d:'%(npId,hId)
1293        IndpCon = [1.0,G2obj.G2VarObj(ohapkey+'Scale')]
1294        DepCons = [detTrans,G2obj.G2VarObj(nhapkey+'Scale')]
1295        constraints['HAP'].append([IndpCon,DepCons,None,None,'e'])
1296        for name in ['Size;i','Mustrain;i']:
1297            IndpCon = [1.0,G2obj.G2VarObj(ohapkey+name)]
1298            DepCons = [1.0,G2obj.G2VarObj(nhapkey+name)]
1299            constraints['HAP'].append([IndpCon,DepCons,None,None,'e'])
1300       
1301################################################################################
1302#### Rigid bodies
1303################################################################################
1304
1305def UpdateRigidBodies(G2frame,data):
1306    '''Called when Rigid bodies tree item is selected.
1307    Displays the rigid bodies in the data window
1308    '''
1309    if not data.get('RBIds') or not data:
1310        data.update({'Vector':{'AtInfo':{}},'Residue':{'AtInfo':{}},
1311            'RBIds':{'Vector':[],'Residue':[]}})       #empty/bad dict - fill it
1312           
1313    global resList
1314    Indx = {}
1315    resList = []
1316    plotDefaults = {'oldxy':[0.,0.],'Quaternion':[0.,0.,0.,1.],'cameraPos':30.,'viewDir':[0,0,1],}
1317   
1318    def OnPageChanged(event):
1319        global resList
1320        resList = []
1321        if event:       #page change event!
1322            page = event.GetSelection()
1323        else:
1324            page = G2frame.dataDisplay.GetSelection()
1325        G2frame.dataDisplay.ChangeSelection(page)
1326        text = G2frame.dataDisplay.GetPageText(page)
1327        if text == 'Vector rigid bodies':
1328            G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.VectorBodyMenu)
1329            G2frame.dataFrame.Bind(wx.EVT_MENU, AddVectorRB, id=G2gd.wxID_VECTORBODYADD)
1330            G2frame.Page = [page,'vrb']
1331            UpdateVectorRB()
1332        elif text == 'Residue rigid bodies':
1333            G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RigidBodyMenu)
1334            G2frame.dataFrame.Bind(wx.EVT_MENU, AddResidueRB, id=G2gd.wxID_RIGIDBODYADD)
1335            G2frame.dataFrame.Bind(wx.EVT_MENU, OnImportRigidBody, id=G2gd.wxID_RIGIDBODYIMPORT)
1336            G2frame.dataFrame.Bind(wx.EVT_MENU, OnDefineTorsSeq, id=G2gd.wxID_RESIDUETORSSEQ) #enable only if residue RBs exist?
1337            G2frame.Page = [page,'rrb']
1338            UpdateResidueRB()
1339           
1340    def getMacroFile(macName):
1341        defDir = os.path.join(os.path.split(__file__)[0],'GSASIImacros')
1342        dlg = wx.FileDialog(G2frame,message='Choose '+macName+' rigid body macro file',
1343            defaultDir=defDir,defaultFile="",wildcard="GSAS-II macro file (*.mac)|*.mac",
1344            style=wx.OPEN | wx.CHANGE_DIR)
1345        try:
1346            if dlg.ShowModal() == wx.ID_OK:
1347                macfile = dlg.GetPath()
1348                macro = open(macfile,'Ur')
1349                head = macro.readline()
1350                if macName not in head:
1351                    print head
1352                    print '**** ERROR - wrong restraint macro file selected, try again ****'
1353                    macro = []
1354            else: # cancel was pressed
1355                macro = []
1356        finally:
1357            dlg.Destroy()
1358        return macro        #advanced past 1st line
1359       
1360    def getTextFile():
1361        dlg = wx.FileDialog(G2frame,'Choose rigid body text file', '.', '',
1362            "GSAS-II text file (*.txt)|*.txt|XYZ file (*.xyz)|*.xyz|"
1363            "Sybyl mol2 file (*.mol2)|*.mol2|PDB file (*.pdb;*.ent)|*.pdb;*.ent",
1364            wx.OPEN | wx.CHANGE_DIR)
1365        try:
1366            if dlg.ShowModal() == wx.ID_OK:
1367                txtfile = dlg.GetPath()
1368                ext = os.path.splitext(txtfile)[1]
1369                text = open(txtfile,'Ur')
1370            else: # cancel was pressed
1371                ext = ''
1372                text = []
1373        finally:
1374            dlg.Destroy()
1375        if 'ent' in ext:
1376            ext = '.pdb'
1377        return text,ext.lower()
1378       
1379    def OnImportRigidBody(event):
1380        page = G2frame.dataDisplay.GetSelection()
1381        if 'Vector' in G2frame.dataDisplay.GetPageText(page):
1382            pass
1383        elif 'Residue' in G2frame.dataDisplay.GetPageText(page):
1384            ImportResidueRB()
1385           
1386    def AddVectorRB(event):
1387        AtInfo = data['Vector']['AtInfo']
1388        dlg = MultiIntegerDialog(G2frame.dataDisplay,'New Rigid Body',['No. atoms','No. translations'],[1,1])
1389        if dlg.ShowModal() == wx.ID_OK:
1390            nAtoms,nTrans = dlg.GetValues()
1391            rbId = ran.randint(0,sys.maxint)
1392            vecMag = [1.0 for i in range(nTrans)]
1393            vecRef = [False for i in range(nTrans)]
1394            vecVal = [np.zeros((nAtoms,3)) for j in range(nTrans)]
1395            rbTypes = ['C' for i in range(nAtoms)]
1396            Info = G2elem.GetAtomInfo('C')
1397            AtInfo['C'] = [Info['Drad'],Info['Color']]
1398            data['Vector'][rbId] = {'RBname':'UNKRB','VectMag':vecMag,'rbXYZ':np.zeros((nAtoms,3)),
1399                'rbRef':[0,1,2,False],'VectRef':vecRef,'rbTypes':rbTypes,'rbVect':vecVal,'useCount':0}
1400            data['RBIds']['Vector'].append(rbId)
1401        dlg.Destroy()
1402        UpdateVectorRB()
1403       
1404    def AddResidueRB(event):
1405        AtInfo = data['Residue']['AtInfo']
1406        macro = getMacroFile('rigid body')
1407        if not macro:
1408            return
1409        macStr = macro.readline()
1410        while macStr:
1411            items = macStr.split()
1412            if 'I' == items[0]:
1413                rbId = ran.randint(0,sys.maxint)
1414                rbName = items[1]
1415                rbTypes = []
1416                rbXYZ = []
1417                rbSeq = []
1418                atNames = []
1419                nAtms,nSeq,nOrig,mRef,nRef = [int(items[i]) for i in [2,3,4,5,6]]
1420                for iAtm in range(nAtms):
1421                    macStr = macro.readline().split()
1422                    atName = macStr[0]
1423                    atType = macStr[1]
1424                    atNames.append(atName)
1425                    rbXYZ.append([float(macStr[i]) for i in [2,3,4]])
1426                    rbTypes.append(atType)
1427                    if atType not in AtInfo:
1428                        Info = G2elem.GetAtomInfo(atType)
1429                        AtInfo[atType] = [Info['Drad'],Info['Color']]
1430                rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[nOrig-1])
1431                for iSeq in range(nSeq):
1432                    macStr = macro.readline().split()
1433                    mSeq = int(macStr[0])
1434                    for jSeq in range(mSeq):
1435                        macStr = macro.readline().split()
1436                        iBeg = int(macStr[0])-1
1437                        iFin = int(macStr[1])-1
1438                        angle = 0.0
1439                        nMove = int(macStr[2])
1440                        iMove = [int(macStr[i])-1 for i in range(3,nMove+3)]
1441                        rbSeq.append([iBeg,iFin,angle,iMove])
1442                data['Residue'][rbId] = {'RBname':rbName,'rbXYZ':rbXYZ,'rbTypes':rbTypes,
1443                    'atNames':atNames,'rbRef':[nOrig-1,mRef-1,nRef-1,True],'rbSeq':rbSeq,
1444                    'SelSeq':[0,0],'useCount':0}
1445                data['RBIds']['Residue'].append(rbId)
1446                print 'Rigid body '+rbName+' added'
1447            macStr = macro.readline()
1448        macro.close()
1449        UpdateResidueRB()
1450       
1451    def ImportResidueRB():
1452        AtInfo = data['Residue']['AtInfo']
1453        text,ext = getTextFile()
1454        if not text:
1455            return
1456        rbId = ran.randint(0,sys.maxint)
1457        rbTypes = []
1458        rbXYZ = []
1459        atNames = []
1460        txtStr = text.readline()
1461        if 'xyz' in ext:
1462            txtStr = text.readline()
1463            txtStr = text.readline()
1464        elif 'mol2' in ext:
1465            while 'ATOM' not in txtStr:
1466                txtStr = text.readline()
1467            txtStr = text.readline()
1468        elif 'pdb' in ext:
1469            while 'ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]:
1470                txtStr = text.readline()
1471                #print txtStr
1472        items = txtStr.split()
1473        while len(items):
1474            if 'txt' in ext:
1475                atName = items[0]
1476                atType = items[1]
1477                rbXYZ.append([float(items[i]) for i in [2,3,4]])
1478            elif 'xyz' in ext:
1479                atType = items[0]
1480                rbXYZ.append([float(items[i]) for i in [1,2,3]])
1481                atName = atType+str(len(rbXYZ))
1482            elif 'mol2' in ext:
1483                atType = items[1]
1484                atName = items[1]+items[0]
1485                rbXYZ.append([float(items[i]) for i in [2,3,4]])
1486            elif 'pdb' in ext:
1487                atType = items[-1]
1488                atName = items[2]
1489                xyz = txtStr[30:55].split()                   
1490                rbXYZ.append([float(x) for x in xyz])
1491            atNames.append(atName)
1492            rbTypes.append(atType)
1493            if atType not in AtInfo:
1494                Info = G2elem.GetAtomInfo(atType)
1495                AtInfo[atType] = [Info['Drad'],Info['Color']]
1496            txtStr = text.readline()
1497            if 'mol2' in ext and 'BOND' in txtStr:
1498                break
1499            if 'pdb' in ext and ('ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]):
1500                break
1501            items = txtStr.split()
1502        rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[0])
1503        data['Residue'][rbId] = {'RBname':'UNKRB','rbXYZ':rbXYZ,'rbTypes':rbTypes,
1504            'atNames':atNames,'rbRef':[0,1,2,False],'rbSeq':[],'SelSeq':[0,0],'useCount':0}
1505        data['RBIds']['Residue'].append(rbId)
1506        print 'Rigid body UNKRB added'
1507        text.close()
1508        UpdateResidueRB()
1509       
1510    def FindNeighbors(Orig,XYZ,atTypes,atNames,AtInfo):
1511        Radii = []
1512        for Atype in atTypes:
1513            Radii.append(AtInfo[Atype][0])
1514        Radii = np.array(Radii)
1515        Neigh = []
1516        Dx = XYZ-XYZ[Orig]
1517        dist = np.sqrt(np.sum(Dx**2,axis=1))
1518        sumR = Radii[Orig]+Radii
1519        IndB = ma.nonzero(ma.masked_greater(dist-0.85*sumR,0.))
1520        for j in IndB[0]:
1521            if j != Orig:
1522                Neigh.append(atNames[j])
1523        return Neigh
1524       
1525    def FindAllNeighbors(XYZ,atTypes,atNames,AtInfo):
1526        NeighDict = {}
1527        for iat,xyz in enumerate(atNames):
1528            NeighDict[atNames[iat]] = FindNeighbors(iat,XYZ,atTypes,atNames,AtInfo)
1529        return NeighDict
1530       
1531    def FindRiding(Orig,Pivot,NeighDict):
1532        riding = [Orig,Pivot]
1533        iAdd = 1
1534        new = True
1535        while new:
1536            newAtms = NeighDict[riding[iAdd]]
1537            for At in newAtms:
1538                new = False
1539                if At not in riding:
1540                    riding.append(At)
1541                    new = True
1542            iAdd += 1
1543            if iAdd < len(riding):
1544                new = True
1545        return riding[2:]
1546                       
1547    def OnDefineTorsSeq(event):
1548        rbKeys = data['Residue'].keys()
1549        rbKeys.remove('AtInfo')
1550        rbNames = [data['Residue'][k]['RBname'] for k in rbKeys]
1551        rbIds = dict(zip(rbNames,rbKeys))
1552        rbNames.sort()
1553        rbId = 0
1554        if len(rbNames) == 0:
1555            print 'There are no rigid bodies defined'
1556            G2frame.ErrorDialog('No rigid bodies','There are no rigid bodies defined',
1557                                parent=G2frame.dataFrame)
1558            return
1559        elif len(rbNames) > 1:
1560            dlg = wx.SingleChoiceDialog(G2frame,'Select rigid body for torsion sequence','Torsion sequence',rbNames)
1561            if dlg.ShowModal() == wx.ID_OK:
1562                sel = dlg.GetSelection()
1563                rbId = rbIds[rbNames[sel]]
1564                rbData = data['Residue'][rbId]
1565            dlg.Destroy()
1566        else:
1567            rbId = rbIds[rbNames[0]]
1568            rbData = data['Residue'][rbId]
1569        if not len(rbData):
1570            return
1571        atNames = rbData['atNames']
1572        AtInfo = data['Residue']['AtInfo']
1573        atTypes = rbData['rbTypes']
1574        XYZ = rbData['rbXYZ']
1575        neighDict = FindAllNeighbors(XYZ,atTypes,atNames,AtInfo)
1576        TargList = []           
1577        dlg = wx.SingleChoiceDialog(G2frame,'Select origin atom for torsion sequence','Origin atom',rbData['atNames'])
1578        if dlg.ShowModal() == wx.ID_OK:
1579            Orig = dlg.GetSelection()
1580            TargList = neighDict[atNames[Orig]]
1581        dlg.Destroy()
1582        if not len(TargList):
1583            return
1584        dlg = wx.SingleChoiceDialog(G2frame,'Select pivot atom for torsion sequence','Pivot atom',TargList)
1585        if dlg.ShowModal() == wx.ID_OK:
1586            Piv = atNames.index(TargList[dlg.GetSelection()])
1587            riding = FindRiding(atNames[Orig],atNames[Piv],neighDict)
1588            Riding = []
1589            for atm in riding:
1590                Riding.append(atNames.index(atm))
1591            rbData['rbSeq'].append([Orig,Piv,0.0,Riding])           
1592        dlg.Destroy()
1593        UpdateResidueRB()
1594
1595    def UpdateVectorRB(Scroll=0):
1596        AtInfo = data['Vector']['AtInfo']
1597        refChoice = {}
1598        if 'DELETED' in str(Status):   #seems to be no other way to do this (wx bug)
1599            if GSASIIpath.GetConfigValue('debug'):
1600                print 'wx error: Rigid Body/Status not cleanly deleted after Refine'
1601            return
1602        SetStatusLine(' You may use e.g. "c60" or "s60" for a vector entry')
1603        def rbNameSizer(rbId,rbData):
1604
1605            def OnRBName(event):
1606                event.Skip()
1607                Obj = event.GetEventObject()
1608                rbData['RBname'] = Obj.GetValue()
1609               
1610            def OnDelRB(event):
1611                Obj = event.GetEventObject()
1612                rbId = Indx[Obj.GetId()]
1613                if rbId in data['Vector']:
1614                    del data['Vector'][rbId]
1615                    data['RBIds']['Vector'].remove(rbId)
1616                    rbData['useCount'] -= 1
1617                wx.CallAfter(UpdateVectorRB)
1618               
1619            def OnPlotRB(event):
1620                Obj = event.GetEventObject()
1621                Obj.SetValue(False)
1622                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,rbData,plotDefaults)
1623           
1624            nameSizer = wx.BoxSizer(wx.HORIZONTAL)
1625            nameSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Rigid body name: '),
1626                0,wx.ALIGN_CENTER_VERTICAL)
1627            RBname = wx.TextCtrl(VectorRBDisplay,-1,rbData['RBname'])
1628            Indx[RBname.GetId()] = rbId
1629            RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName)
1630            RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName)
1631            nameSizer.Add(RBname,0,wx.ALIGN_CENTER_VERTICAL)
1632            nameSizer.Add((5,0),)
1633            plotRB = wx.CheckBox(VectorRBDisplay,-1,'Plot?')
1634            Indx[plotRB.GetId()] = rbId
1635            plotRB.Bind(wx.EVT_CHECKBOX,OnPlotRB)
1636            nameSizer.Add(plotRB,0,wx.ALIGN_CENTER_VERTICAL)
1637            nameSizer.Add((5,0),)
1638            if not rbData['useCount']:
1639                delRB = wx.CheckBox(VectorRBDisplay,-1,'Delete?')
1640                Indx[delRB.GetId()] = rbId
1641                delRB.Bind(wx.EVT_CHECKBOX,OnDelRB)
1642                nameSizer.Add(delRB,0,wx.ALIGN_CENTER_VERTICAL)
1643            return nameSizer
1644           
1645        def rbRefAtmSizer(rbId,rbData):
1646           
1647            def OnRefSel(event):
1648                Obj = event.GetEventObject()
1649                iref = Indx[Obj.GetId()]
1650                sel = Obj.GetValue()
1651                rbData['rbRef'][iref] = atNames.index(sel)
1652                FillRefChoice(rbId,rbData)
1653           
1654            refAtmSizer = wx.BoxSizer(wx.HORIZONTAL)
1655            atNames = [name+str(i) for i,name in enumerate(rbData['rbTypes'])]
1656            rbRef = rbData.get('rbRef',[0,1,2,False])
1657            rbData['rbRef'] = rbRef
1658            if rbData['useCount']:
1659                refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1,
1660                    'Orientation reference atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \
1661                     atNames[rbRef[1]],atNames[rbRef[2]])),0)
1662            else:
1663                refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1,
1664                    'Orientation reference atoms A-B-C: '),0,wx.ALIGN_CENTER_VERTICAL)
1665                for i in range(3):
1666                    choices = [atNames[j] for j in refChoice[rbId][i]]
1667                    refSel = wx.ComboBox(VectorRBDisplay,-1,value='',
1668                        choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN)
1669                    refSel.SetValue(atNames[rbRef[i]])
1670                    refSel.Bind(wx.EVT_COMBOBOX, OnRefSel)
1671                    Indx[refSel.GetId()] = i
1672                    refAtmSizer.Add(refSel,0,wx.ALIGN_CENTER_VERTICAL)
1673            return refAtmSizer
1674                       
1675        def rbVectMag(rbId,imag,rbData):
1676           
1677            def OnRBVectorMag(event):
1678                event.Skip()
1679                Obj = event.GetEventObject()
1680                rbId,imag = Indx[Obj.GetId()]
1681                try:
1682                    val = float(Obj.GetValue())
1683                    if val <= 0.:
1684                        raise ValueError
1685                    rbData['VectMag'][imag] = val
1686                except ValueError:
1687                    pass
1688                Obj.SetValue('%8.4f'%(val))
1689                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1690                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbId],plotDefaults)
1691               
1692            def OnRBVectorRef(event):
1693                Obj = event.GetEventObject()
1694                rbId,imag = Indx[Obj.GetId()]
1695                rbData['VectRef'][imag] = Obj.GetValue()
1696                       
1697            magSizer = wx.wx.BoxSizer(wx.HORIZONTAL)
1698            magSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Translation magnitude: '),
1699                0,wx.ALIGN_CENTER_VERTICAL)
1700            magValue = wx.TextCtrl(VectorRBDisplay,-1,'%8.4f'%(rbData['VectMag'][imag]))
1701            Indx[magValue.GetId()] = [rbId,imag]
1702            magValue.Bind(wx.EVT_TEXT_ENTER,OnRBVectorMag)
1703            magValue.Bind(wx.EVT_KILL_FOCUS,OnRBVectorMag)
1704            magSizer.Add(magValue,0,wx.ALIGN_CENTER_VERTICAL)
1705            magSizer.Add((5,0),)
1706            magref = wx.CheckBox(VectorRBDisplay,-1,label=' Refine?') 
1707            magref.SetValue(rbData['VectRef'][imag])
1708            magref.Bind(wx.EVT_CHECKBOX,OnRBVectorRef)
1709            Indx[magref.GetId()] = [rbId,imag]
1710            magSizer.Add(magref,0,wx.ALIGN_CENTER_VERTICAL)
1711            return magSizer
1712           
1713        def rbVectors(rbId,imag,mag,XYZ,rbData):
1714
1715            def TypeSelect(event):
1716                AtInfo = data['Vector']['AtInfo']
1717                r,c = event.GetRow(),event.GetCol()
1718                if vecGrid.GetColLabelValue(c) == 'Type':
1719                    PE = G2elemGUI.PickElement(G2frame,oneOnly=True)
1720                    if PE.ShowModal() == wx.ID_OK:
1721                        if PE.Elem != 'None':
1722                            El = PE.Elem.strip().lower().capitalize()
1723                            if El not in AtInfo:
1724                                Info = G2elem.GetAtomInfo(El)
1725                                AtInfo[El] = [Info['Drad'],Info['Color']]
1726                            rbData['rbTypes'][r] = El
1727                            vecGrid.SetCellValue(r,c,El)
1728                    PE.Destroy()
1729                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1730
1731            def ChangeCell(event):
1732                r,c =  event.GetRow(),event.GetCol()
1733                if r >= 0 and (0 <= c < 3):
1734                    try:
1735                        val = float(vecGrid.GetCellValue(r,c))
1736                        rbData['rbVect'][imag][r][c] = val
1737                    except ValueError:
1738                        pass
1739                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbId],plotDefaults)
1740                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1741
1742            vecSizer = wx.BoxSizer()
1743            Types = 3*[wg.GRID_VALUE_FLOAT+':10,5',]+[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',]
1744            colLabels = ['Vector x','Vector y','Vector z','Type','Cart x','Cart y','Cart z']
1745            table = []
1746            rowLabels = []
1747            for ivec,xyz in enumerate(rbData['rbVect'][imag]):
1748                table.append(list(xyz)+[rbData['rbTypes'][ivec],]+list(XYZ[ivec]))
1749                rowLabels.append(str(ivec))
1750            vecTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1751            vecGrid = G2G.GSGrid(VectorRBDisplay)
1752            vecGrid.SetTable(vecTable, True)
1753            vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell)
1754            if not imag:
1755                vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect)
1756            attr = wx.grid.GridCellAttr()
1757            attr.SetEditor(G2G.GridFractionEditor(vecGrid))
1758            for c in range(3):
1759                vecGrid.SetColAttr(c, attr)
1760            for row in range(vecTable.GetNumberRows()):
1761                if imag:
1762                    vecGrid.SetCellStyle(row,3,VERY_LIGHT_GREY,True)                   
1763                for col in [4,5,6]:
1764                    vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True)
1765            vecGrid.SetMargins(0,0)
1766            vecGrid.AutoSizeColumns(False)
1767            vecSizer.Add(vecGrid)
1768            return vecSizer
1769       
1770        def FillRefChoice(rbId,rbData):
1771            choiceIds = [i for i in range(len(rbData['rbTypes']))]
1772           
1773            rbRef = rbData.get('rbRef',[-1,-1,-1,False])
1774            for i in range(3):
1775                choiceIds.remove(rbRef[i])
1776            refChoice[rbId] = [choiceIds[:],choiceIds[:],choiceIds[:]]
1777            for i in range(3):
1778                refChoice[rbId][i].append(rbRef[i])
1779                refChoice[rbId][i].sort()     
1780           
1781        #VectorRB.DestroyChildren() # bad, deletes scrollbars on Mac!
1782        if VectorRB.GetSizer():
1783            VectorRB.GetSizer().Clear(True)
1784        VectorRBDisplay = wx.Panel(VectorRB)
1785        VectorRBSizer = wx.BoxSizer(wx.VERTICAL)
1786        for rbId in data['RBIds']['Vector']:
1787            if rbId != 'AtInfo':
1788                rbData = data['Vector'][rbId]
1789                FillRefChoice(rbId,rbData)
1790                VectorRBSizer.Add(rbNameSizer(rbId,rbData),0)
1791                VectorRBSizer.Add(rbRefAtmSizer(rbId,rbData),0)
1792                XYZ = np.array([[0.,0.,0.] for Ty in rbData['rbTypes']])
1793                for imag,mag in enumerate(rbData['VectMag']):
1794                    XYZ += mag*rbData['rbVect'][imag]
1795                    VectorRBSizer.Add(rbVectMag(rbId,imag,rbData),0)
1796                    VectorRBSizer.Add(rbVectors(rbId,imag,mag,XYZ,rbData),0)
1797                VectorRBSizer.Add((5,5),0)
1798                data['Vector'][rbId]['rbXYZ'] = XYZ       
1799        VectorRBSizer.Layout()   
1800        VectorRBDisplay.SetSizer(VectorRBSizer,True)
1801        Size = VectorRBSizer.GetMinSize()
1802        Size[0] += 40
1803        Size[1] = max(Size[1],450) + 20
1804        VectorRBDisplay.SetSize(Size)
1805        VectorRB.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1806        VectorRB.Scroll(0,Scroll)
1807        Size[1] = min(Size[1],500)
1808        G2frame.dataFrame.setSizePosLeft(Size)
1809       
1810    def UpdateResidueRB():
1811        AtInfo = data['Residue']['AtInfo']
1812        refChoice = {}
1813        RefObjs = []
1814
1815        def rbNameSizer(rbId,rbData):
1816
1817            def OnRBName(event):
1818                Obj = event.GetEventObject()
1819                rbData['RBname'] = Obj.GetValue()
1820               
1821            def OnDelRB(event):
1822                Obj = event.GetEventObject()
1823                rbId = Indx[Obj.GetId()]
1824                if rbId in data['Residue']: 
1825                    del data['Residue'][rbId]
1826                    data['RBIds']['Residue'].remove(rbId)
1827                wx.CallAfter(UpdateResidueRB)
1828               
1829            def OnPlotRB(event):
1830                Obj = event.GetEventObject()
1831                Obj.SetValue(False)
1832                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1833           
1834            nameSizer = wx.BoxSizer(wx.HORIZONTAL)
1835            nameSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Residue name: '),
1836                0,wx.ALIGN_CENTER_VERTICAL)
1837            RBname = wx.TextCtrl(ResidueRBDisplay,-1,rbData['RBname'])
1838            Indx[RBname.GetId()] = rbId
1839            RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName)
1840            RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName)
1841            nameSizer.Add(RBname,0,wx.ALIGN_CENTER_VERTICAL)
1842            nameSizer.Add((5,0),)
1843            plotRB = wx.CheckBox(ResidueRBDisplay,-1,'Plot?')
1844            Indx[plotRB.GetId()] = rbId
1845            plotRB.Bind(wx.EVT_CHECKBOX,OnPlotRB)
1846            nameSizer.Add(plotRB,0,wx.ALIGN_CENTER_VERTICAL)
1847            nameSizer.Add((5,0),)
1848            if not rbData['useCount']:
1849                delRB = wx.CheckBox(ResidueRBDisplay,-1,'Delete?')
1850                Indx[delRB.GetId()] = rbId
1851                delRB.Bind(wx.EVT_CHECKBOX,OnDelRB)
1852                nameSizer.Add(delRB,0,wx.ALIGN_CENTER_VERTICAL)
1853            return nameSizer
1854           
1855        def rbResidues(rbId,rbData):
1856           
1857            def TypeSelect(event):
1858                AtInfo = data['Residue']['AtInfo']
1859                r,c = event.GetRow(),event.GetCol()
1860                if vecGrid.GetColLabelValue(c) == 'Type':
1861                    PE = G2elemGUI.PickElement(G2frame,oneOnly=True)
1862                    if PE.ShowModal() == wx.ID_OK:
1863                        if PE.Elem != 'None':
1864                            El = PE.Elem.strip().lower().capitalize()
1865                            if El not in AtInfo:
1866                                Info = G2elem.GetAtomInfo(El)
1867                                AtInfo[El] = [Info['Drad']['Color']]
1868                            rbData['rbTypes'][r] = El
1869                            vecGrid.SetCellValue(r,c,El)
1870                    PE.Destroy()
1871
1872            def ChangeCell(event):
1873                r,c =  event.GetRow(),event.GetCol()
1874                if r >= 0 and (0 <= c < 3):
1875                    try:
1876                        val = float(vecGrid.GetCellValue(r,c))
1877                        rbData['rbXYZ'][r][c] = val
1878                    except ValueError:
1879                        pass
1880                       
1881            def RowSelect(event):
1882                r,c =  event.GetRow(),event.GetCol()
1883                if c < 0:                   #only row clicks
1884                    for vecgrid in resList:
1885                        vecgrid.ClearSelection()
1886                    vecGrid.SelectRow(r,True)
1887
1888            def OnRefSel(event):
1889                Obj = event.GetEventObject()
1890                iref,res,jref = Indx[Obj.GetId()]
1891                sel = Obj.GetValue()
1892                ind = atNames.index(sel)
1893                rbData['rbRef'][iref] = ind
1894                FillRefChoice(rbId,rbData)
1895                for i,ref in enumerate(RefObjs[jref]):
1896                    ref.SetItems([atNames[j] for j in refChoice[rbId][i]])
1897                    ref.SetValue(atNames[rbData['rbRef'][i]])
1898                if not iref:     #origin change
1899                    rbXYZ = rbData['rbXYZ']
1900                    rbXYZ -= rbXYZ[ind]
1901                    res.ClearSelection()
1902                    resTable = res.GetTable()
1903                    for r in range(res.GetNumberRows()):
1904                        row = resTable.GetRowValues(r)
1905                        row[2:4] = rbXYZ[r]
1906                        resTable.SetRowValues(r,row)
1907                    res.ForceRefresh()
1908                    G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1909               
1910            Types = 2*[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',]
1911            colLabels = ['Name','Type','Cart x','Cart y','Cart z']
1912            table = []
1913            rowLabels = []
1914            for ivec,xyz in enumerate(rbData['rbXYZ']):
1915                table.append([rbData['atNames'][ivec],]+[rbData['rbTypes'][ivec],]+list(xyz))
1916                rowLabels.append(str(ivec))
1917            vecTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1918            vecGrid = G2G.GSGrid(ResidueRBDisplay)
1919            Indx[vecGrid.GetId()] = rbId
1920            resList.append(vecGrid)
1921            vecGrid.SetTable(vecTable, True)
1922            vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell)
1923            vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect)
1924            vecGrid.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK, RowSelect)
1925            attr = wx.grid.GridCellAttr()
1926            attr.SetEditor(G2G.GridFractionEditor(vecGrid))
1927            for c in range(3):
1928                vecGrid.SetColAttr(c, attr)
1929            for row in range(vecTable.GetNumberRows()):
1930                for col in range(5):
1931                    vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True)
1932            vecGrid.SetMargins(0,0)
1933            vecGrid.AutoSizeColumns(False)
1934            vecSizer = wx.BoxSizer()
1935            vecSizer.Add(vecGrid)
1936           
1937            refAtmSizer = wx.BoxSizer(wx.HORIZONTAL)
1938            atNames = rbData['atNames']
1939            rbRef = rbData['rbRef']
1940            if rbData['rbRef'][3] or rbData['useCount']:
1941                refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
1942                    'Orientation reference atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \
1943                     atNames[rbRef[1]],atNames[rbRef[2]])),0)
1944            else:
1945                refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
1946                    'Orientation reference atoms A-B-C: '),0,wx.ALIGN_CENTER_VERTICAL)
1947                refObj = [0,0,0]
1948                for i in range(3):
1949                    choices = [atNames[j] for j in refChoice[rbId][i]]
1950                    refSel = wx.ComboBox(ResidueRBDisplay,-1,value='',
1951                        choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN)
1952                    refSel.SetValue(atNames[rbRef[i]])
1953                    refSel.Bind(wx.EVT_COMBOBOX, OnRefSel)
1954                    Indx[refSel.GetId()] = [i,vecGrid,len(RefObjs)]
1955                    refObj[i] = refSel
1956                    refAtmSizer.Add(refSel,0,wx.ALIGN_CENTER_VERTICAL)
1957                RefObjs.append(refObj)
1958           
1959            mainSizer = wx.BoxSizer(wx.VERTICAL)
1960            mainSizer.Add(refAtmSizer)
1961            mainSizer.Add(vecSizer)
1962            return mainSizer
1963           
1964        def SeqSizer(angSlide,rbId,iSeq,Seq,atNames):
1965           
1966            def ChangeAngle(event):
1967                event.Skip()
1968                Obj = event.GetEventObject()
1969                rbId,Seq = Indx[Obj.GetId()][:2]
1970                val = Seq[2]
1971                try:
1972                    val = float(Obj.GetValue())
1973                    Seq[2] = val
1974                except ValueError:
1975                    pass
1976                Obj.SetValue('%8.2f'%(val))
1977                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,data['Residue'][rbId],plotDefaults)
1978               
1979            def OnRadBtn(event):
1980                Obj = event.GetEventObject()
1981                Seq,iSeq,angId = Indx[Obj.GetId()]
1982                data['Residue'][rbId]['SelSeq'] = [iSeq,angId]
1983                angSlide.SetValue(int(100*Seq[2]))
1984               
1985            def OnDelBtn(event):
1986                Obj = event.GetEventObject()
1987                rbId,Seq = Indx[Obj.GetId()]
1988                data['Residue'][rbId]['rbSeq'].remove(Seq)       
1989                wx.CallAfter(UpdateResidueRB)
1990           
1991            seqSizer = wx.FlexGridSizer(0,5,2,2)
1992            seqSizer.AddGrowableCol(3,0)
1993            iBeg,iFin,angle,iMove = Seq
1994            ang = wx.TextCtrl(ResidueRBDisplay,-1,'%8.2f'%(angle),size=(50,20))
1995            if not iSeq:
1996                radBt = wx.RadioButton(ResidueRBDisplay,-1,'',style=wx.RB_GROUP)
1997                data['Residue'][rbId]['SelSeq'] = [iSeq,ang.GetId()]
1998            else:
1999                radBt = wx.RadioButton(ResidueRBDisplay,-1,'')
2000            radBt.Bind(wx.EVT_RADIOBUTTON,OnRadBtn)                   
2001            seqSizer.Add(radBt)
2002            delBt = wx.RadioButton(ResidueRBDisplay,-1,'')
2003            delBt.Bind(wx.EVT_RADIOBUTTON,OnDelBtn)
2004            seqSizer.Add(delBt)
2005            bond = wx.TextCtrl(ResidueRBDisplay,-1,'%s %s'%(atNames[iBeg],atNames[iFin]),size=(50,20))
2006            seqSizer.Add(bond,0,wx.ALIGN_CENTER_VERTICAL)
2007            Indx[radBt.GetId()] = [Seq,iSeq,ang.GetId()]
2008            Indx[delBt.GetId()] = [rbId,Seq]
2009            Indx[ang.GetId()] = [rbId,Seq,ang]
2010            ang.Bind(wx.EVT_TEXT_ENTER,ChangeAngle)
2011            ang.Bind(wx.EVT_KILL_FOCUS,ChangeAngle)
2012            seqSizer.Add(ang,0,wx.ALIGN_CENTER_VERTICAL)
2013            atms = ''
2014            for i in iMove:   
2015                atms += ' %s,'%(atNames[i])
2016            moves = wx.TextCtrl(ResidueRBDisplay,-1,atms[:-1],size=(200,20))
2017            seqSizer.Add(moves,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.RIGHT)
2018            return seqSizer
2019           
2020        def SlideSizer():
2021           
2022            def OnSlider(event):
2023                Obj = event.GetEventObject()
2024                rbData = Indx[Obj.GetId()]
2025                iSeq,angId = rbData['SelSeq']
2026                val = float(Obj.GetValue())/100.
2027                rbData['rbSeq'][iSeq][2] = val
2028                Indx[angId][2].SetValue('%8.2f'%(val))
2029                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
2030           
2031            slideSizer = wx.BoxSizer(wx.HORIZONTAL)
2032            slideSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Selected torsion angle:'),0)
2033            iSeq,angId = rbData['SelSeq']
2034            angSlide = wx.Slider(ResidueRBDisplay,-1,
2035                int(100*rbData['rbSeq'][iSeq][2]),0,36000,size=(200,20),
2036                style=wx.SL_HORIZONTAL)
2037            angSlide.Bind(wx.EVT_SLIDER, OnSlider)
2038            Indx[angSlide.GetId()] = rbData
2039            slideSizer.Add(angSlide,0)           
2040            return slideSizer,angSlide
2041           
2042        def FillRefChoice(rbId,rbData):
2043            choiceIds = [i for i in range(len(rbData['atNames']))]
2044            for seq in rbData['rbSeq']:
2045                for i in seq[3]:
2046                    try:
2047                        choiceIds.remove(i)
2048                    except ValueError:
2049                        pass
2050            rbRef = rbData['rbRef']
2051            for i in range(3):
2052                try:
2053                    choiceIds.remove(rbRef[i])
2054                except ValueError:
2055                    pass
2056            refChoice[rbId] = [choiceIds[:],choiceIds[:],choiceIds[:]]
2057            for i in range(3):
2058                refChoice[rbId][i].append(rbRef[i])
2059                refChoice[rbId][i].sort()     
2060           
2061        #ResidueRB.DestroyChildren() # bad, deletes scrollbars on Mac!
2062        if ResidueRB.GetSizer():
2063            ResidueRB.DestroyChildren()
2064#            ResidueRB.GetSizer().Clear(True)
2065           
2066        ResidueRBDisplay = wx.Panel(ResidueRB)
2067        ResidueRBSizer = wx.BoxSizer(wx.VERTICAL)
2068        for rbId in data['RBIds']['Residue']:
2069            rbData = data['Residue'][rbId]
2070            FillRefChoice(rbId,rbData)
2071            ResidueRBSizer.Add(rbNameSizer(rbId,rbData),0)
2072            ResidueRBSizer.Add(rbResidues(rbId,rbData),0)
2073            ResidueRBSizer.Add((5,5),0)
2074            if rbData['rbSeq']:
2075                slideSizer,angSlide = SlideSizer()
2076            if len(rbData['rbSeq']):
2077                ResidueRBSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
2078                    'Sel  Del  Bond             Angle      Riding atoms'),
2079                    0,wx.ALIGN_CENTER_VERTICAL)                       
2080            for iSeq,Seq in enumerate(rbData['rbSeq']):
2081                ResidueRBSizer.Add(SeqSizer(angSlide,rbId,iSeq,Seq,rbData['atNames']))
2082            if rbData['rbSeq']:
2083                ResidueRBSizer.Add(slideSizer,)
2084            ResidueRBSizer.Add(wx.StaticText(ResidueRBDisplay,-1,100*'-'))
2085
2086        ResidueRBSizer.Add((5,25),)
2087        ResidueRBSizer.Layout()   
2088        ResidueRBDisplay.SetSizer(ResidueRBSizer,True)
2089        Size = ResidueRBSizer.GetMinSize()
2090        Size[0] += 40
2091        Size[1] = max(Size[1],450) + 20
2092        ResidueRBDisplay.SetSize(Size)
2093        ResidueRB.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
2094        Size[1] = min(500,Size[1])
2095        G2frame.dataFrame.setSizePosLeft(Size)
2096       
2097    def SetStatusLine(text):
2098        Status.SetStatusText(text)                                     
2099
2100    if G2frame.dataDisplay:
2101        G2frame.dataDisplay.Destroy()
2102    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RigidBodyMenu)
2103    G2frame.dataFrame.SetLabel('Rigid bodies')
2104    if not G2frame.dataFrame.GetStatusBar():
2105        Status = G2frame.dataFrame.CreateStatusBar()
2106    SetStatusLine('')
2107
2108    G2frame.dataDisplay = G2G.GSNoteBook(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize())
2109
2110    VectorRB = wx.ScrolledWindow(G2frame.dataDisplay)
2111    G2frame.dataDisplay.AddPage(VectorRB,'Vector rigid bodies')
2112    ResidueRB = wx.ScrolledWindow(G2frame.dataDisplay)
2113    G2frame.dataDisplay.AddPage(ResidueRB,'Residue rigid bodies')
2114    UpdateVectorRB()
2115    G2frame.dataDisplay.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged)
2116    wx.CallAfter(OnPageChanged,None)
Note: See TracBrowser for help on using the repository browser.