source: trunk/GSASIIconstrGUI.py @ 2753

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

minor shuffling about for atom constraints - no discernable impact
setup default reflectometry data stub in G2pwdrGUI
setup reflectometry (REFD) data import stub (no importers yet) & generic data handling for REFD tree entries in GGSAII

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