source: branch/2frame/GSASIIconstrGUI.py @ 2888

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