source: trunk/GSASIIconstrGUI.py @ 2512

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

remove 'constraint' and 'restraint' from Consraints & Restraints tabs
work on automatic nucl-mag constraints
allow selection of possible mag atoms to use in new mag phase
fix issues with chemical composition restraints

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