source: trunk/GSASIIconstrGUI.py @ 2520

Last change on this file since 2520 was 2520, checked in by vondreele, 7 years ago

remove unused & commented Srr fctr & deriv routines
Mag moment derivatives semireasonable but not close
transform nucl - mag ok if no off diag terms in transform matrix

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