source: trunk/GSASIIconstrGUI.py @ 2544

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

major cleanup of unused variables, etc.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 92.2 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIconstrGUI - constraint GUI routines
3########### SVN repository information ###################
4# $Date: 2016-11-21 22:40:02 +0000 (Mon, 21 Nov 2016) $
5# $Author: vondreele $
6# $Revision: 2544 $
7# $URL: trunk/GSASIIconstrGUI.py $
8# $Id: GSASIIconstrGUI.py 2544 2016-11-21 22:40:02Z 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: 2544 $")
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(*i1) for i1 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            rbId = ran.randint(0,sys.maxint)
1401            vecMag = [1.0 for i in range(nTrans)]
1402            vecRef = [False for i in range(nTrans)]
1403            vecVal = [np.zeros((nAtoms,3)) for j in range(nTrans)]
1404            rbTypes = ['C' for i in range(nAtoms)]
1405            Info = G2elem.GetAtomInfo('C')
1406            AtInfo['C'] = [Info['Drad'],Info['Color']]
1407            data['Vector'][rbId] = {'RBname':'UNKRB','VectMag':vecMag,'rbXYZ':np.zeros((nAtoms,3)),
1408                'rbRef':[0,1,2,False],'VectRef':vecRef,'rbTypes':rbTypes,'rbVect':vecVal,'useCount':0}
1409            data['RBIds']['Vector'].append(rbId)
1410        dlg.Destroy()
1411        UpdateVectorRB()
1412       
1413    def AddResidueRB(event):
1414        AtInfo = data['Residue']['AtInfo']
1415        macro = getMacroFile('rigid body')
1416        if not macro:
1417            return
1418        macStr = macro.readline()
1419        while macStr:
1420            items = macStr.split()
1421            if 'I' == items[0]:
1422                rbId = ran.randint(0,sys.maxint)
1423                rbName = items[1]
1424                rbTypes = []
1425                rbXYZ = []
1426                rbSeq = []
1427                atNames = []
1428                nAtms,nSeq,nOrig,mRef,nRef = [int(items[i]) for i in [2,3,4,5,6]]
1429                for iAtm in range(nAtms):
1430                    macStr = macro.readline().split()
1431                    atName = macStr[0]
1432                    atType = macStr[1]
1433                    atNames.append(atName)
1434                    rbXYZ.append([float(macStr[i]) for i in [2,3,4]])
1435                    rbTypes.append(atType)
1436                    if atType not in AtInfo:
1437                        Info = G2elem.GetAtomInfo(atType)
1438                        AtInfo[atType] = [Info['Drad'],Info['Color']]
1439                rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[nOrig-1])
1440                for iSeq in range(nSeq):
1441                    macStr = macro.readline().split()
1442                    mSeq = int(macStr[0])
1443                    for jSeq in range(mSeq):
1444                        macStr = macro.readline().split()
1445                        iBeg = int(macStr[0])-1
1446                        iFin = int(macStr[1])-1
1447                        angle = 0.0
1448                        nMove = int(macStr[2])
1449                        iMove = [int(macStr[i])-1 for i in range(3,nMove+3)]
1450                        rbSeq.append([iBeg,iFin,angle,iMove])
1451                data['Residue'][rbId] = {'RBname':rbName,'rbXYZ':rbXYZ,'rbTypes':rbTypes,
1452                    'atNames':atNames,'rbRef':[nOrig-1,mRef-1,nRef-1,True],'rbSeq':rbSeq,
1453                    'SelSeq':[0,0],'useCount':0}
1454                data['RBIds']['Residue'].append(rbId)
1455                print 'Rigid body '+rbName+' added'
1456            macStr = macro.readline()
1457        macro.close()
1458        UpdateResidueRB()
1459       
1460    def ImportResidueRB():
1461        AtInfo = data['Residue']['AtInfo']
1462        text,ext = getTextFile()
1463        if not text:
1464            return
1465        rbId = ran.randint(0,sys.maxint)
1466        rbTypes = []
1467        rbXYZ = []
1468        atNames = []
1469        txtStr = text.readline()
1470        if 'xyz' in ext:
1471            txtStr = text.readline()
1472            txtStr = text.readline()
1473        elif 'mol2' in ext:
1474            while 'ATOM' not in txtStr:
1475                txtStr = text.readline()
1476            txtStr = text.readline()
1477        elif 'pdb' in ext:
1478            while 'ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]:
1479                txtStr = text.readline()
1480                #print txtStr
1481        items = txtStr.split()
1482        while len(items):
1483            if 'txt' in ext:
1484                atName = items[0]
1485                atType = items[1]
1486                rbXYZ.append([float(items[i]) for i in [2,3,4]])
1487            elif 'xyz' in ext:
1488                atType = items[0]
1489                rbXYZ.append([float(items[i]) for i in [1,2,3]])
1490                atName = atType+str(len(rbXYZ))
1491            elif 'mol2' in ext:
1492                atType = items[1]
1493                atName = items[1]+items[0]
1494                rbXYZ.append([float(items[i]) for i in [2,3,4]])
1495            elif 'pdb' in ext:
1496                atType = items[-1]
1497                atName = items[2]
1498                xyz = txtStr[30:55].split()                   
1499                rbXYZ.append([float(x) for x in xyz])
1500            atNames.append(atName)
1501            rbTypes.append(atType)
1502            if atType not in AtInfo:
1503                Info = G2elem.GetAtomInfo(atType)
1504                AtInfo[atType] = [Info['Drad'],Info['Color']]
1505            txtStr = text.readline()
1506            if 'mol2' in ext and 'BOND' in txtStr:
1507                break
1508            if 'pdb' in ext and ('ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]):
1509                break
1510            items = txtStr.split()
1511        rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[0])
1512        data['Residue'][rbId] = {'RBname':'UNKRB','rbXYZ':rbXYZ,'rbTypes':rbTypes,
1513            'atNames':atNames,'rbRef':[0,1,2,False],'rbSeq':[],'SelSeq':[0,0],'useCount':0}
1514        data['RBIds']['Residue'].append(rbId)
1515        print 'Rigid body UNKRB added'
1516        text.close()
1517        UpdateResidueRB()
1518       
1519    def FindNeighbors(Orig,XYZ,atTypes,atNames,AtInfo):
1520        Radii = []
1521        for Atype in atTypes:
1522            Radii.append(AtInfo[Atype][0])
1523        Radii = np.array(Radii)
1524        Neigh = []
1525        Dx = XYZ-XYZ[Orig]
1526        dist = np.sqrt(np.sum(Dx**2,axis=1))
1527        sumR = Radii[Orig]+Radii
1528        IndB = ma.nonzero(ma.masked_greater(dist-0.85*sumR,0.))
1529        for j in IndB[0]:
1530            if j != Orig:
1531                Neigh.append(atNames[j])
1532        return Neigh
1533       
1534    def FindAllNeighbors(XYZ,atTypes,atNames,AtInfo):
1535        NeighDict = {}
1536        for iat,xyz in enumerate(atNames):
1537            NeighDict[atNames[iat]] = FindNeighbors(iat,XYZ,atTypes,atNames,AtInfo)
1538        return NeighDict
1539       
1540    def FindRiding(Orig,Pivot,NeighDict):
1541        riding = [Orig,Pivot]
1542        iAdd = 1
1543        new = True
1544        while new:
1545            newAtms = NeighDict[riding[iAdd]]
1546            for At in newAtms:
1547                new = False
1548                if At not in riding:
1549                    riding.append(At)
1550                    new = True
1551            iAdd += 1
1552            if iAdd < len(riding):
1553                new = True
1554        return riding[2:]
1555                       
1556    def OnDefineTorsSeq(event):
1557        rbKeys = data['Residue'].keys()
1558        rbKeys.remove('AtInfo')
1559        rbNames = [data['Residue'][k]['RBname'] for k in rbKeys]
1560        rbIds = dict(zip(rbNames,rbKeys))
1561        rbNames.sort()
1562        rbId = 0
1563        if len(rbNames) == 0:
1564            print 'There are no rigid bodies defined'
1565            G2frame.ErrorDialog('No rigid bodies','There are no rigid bodies defined',
1566                                parent=G2frame.dataFrame)
1567            return
1568        elif len(rbNames) > 1:
1569            dlg = wx.SingleChoiceDialog(G2frame,'Select rigid body for torsion sequence','Torsion sequence',rbNames)
1570            if dlg.ShowModal() == wx.ID_OK:
1571                sel = dlg.GetSelection()
1572                rbId = rbIds[rbNames[sel]]
1573                rbData = data['Residue'][rbId]
1574            dlg.Destroy()
1575        else:
1576            rbId = rbIds[rbNames[0]]
1577            rbData = data['Residue'][rbId]
1578        if not len(rbData):
1579            return
1580        atNames = rbData['atNames']
1581        AtInfo = data['Residue']['AtInfo']
1582        atTypes = rbData['rbTypes']
1583        XYZ = rbData['rbXYZ']
1584        neighDict = FindAllNeighbors(XYZ,atTypes,atNames,AtInfo)
1585        TargList = []           
1586        dlg = wx.SingleChoiceDialog(G2frame,'Select origin atom for torsion sequence','Origin atom',rbData['atNames'])
1587        if dlg.ShowModal() == wx.ID_OK:
1588            Orig = dlg.GetSelection()
1589            TargList = neighDict[atNames[Orig]]
1590        dlg.Destroy()
1591        if not len(TargList):
1592            return
1593        dlg = wx.SingleChoiceDialog(G2frame,'Select pivot atom for torsion sequence','Pivot atom',TargList)
1594        if dlg.ShowModal() == wx.ID_OK:
1595            Piv = atNames.index(TargList[dlg.GetSelection()])
1596            riding = FindRiding(atNames[Orig],atNames[Piv],neighDict)
1597            Riding = []
1598            for atm in riding:
1599                Riding.append(atNames.index(atm))
1600            rbData['rbSeq'].append([Orig,Piv,0.0,Riding])           
1601        dlg.Destroy()
1602        UpdateResidueRB()
1603
1604    def UpdateVectorRB(Scroll=0):
1605        AtInfo = data['Vector']['AtInfo']
1606        refChoice = {}
1607        if 'DELETED' in str(Status):   #seems to be no other way to do this (wx bug)
1608            if GSASIIpath.GetConfigValue('debug'):
1609                print 'wx error: Rigid Body/Status not cleanly deleted after Refine'
1610            return
1611        SetStatusLine(' You may use e.g. "c60" or "s60" for a vector entry')
1612        def rbNameSizer(rbId,rbData):
1613
1614            def OnRBName(event):
1615                event.Skip()
1616                Obj = event.GetEventObject()
1617                rbData['RBname'] = Obj.GetValue()
1618               
1619            def OnDelRB(event):
1620                Obj = event.GetEventObject()
1621                rbId = Indx[Obj.GetId()]
1622                del data['Vector'][rbId]
1623                data['RBIds']['Vector'].remove(rbId)
1624                rbData['useCount'] -= 1
1625                wx.CallAfter(UpdateVectorRB)
1626               
1627            def OnPlotRB(event):
1628                Obj = event.GetEventObject()
1629                Obj.SetValue(False)
1630                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,rbData,plotDefaults)
1631           
1632            nameSizer = wx.BoxSizer(wx.HORIZONTAL)
1633            nameSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Rigid body name: '),
1634                0,wx.ALIGN_CENTER_VERTICAL)
1635            RBname = wx.TextCtrl(VectorRBDisplay,-1,rbData['RBname'])
1636            Indx[RBname.GetId()] = rbId
1637            RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName)
1638            RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName)
1639            nameSizer.Add(RBname,0,wx.ALIGN_CENTER_VERTICAL)
1640            nameSizer.Add((5,0),)
1641            plotRB = wx.CheckBox(VectorRBDisplay,-1,'Plot?')
1642            Indx[plotRB.GetId()] = rbId
1643            plotRB.Bind(wx.EVT_CHECKBOX,OnPlotRB)
1644            nameSizer.Add(plotRB,0,wx.ALIGN_CENTER_VERTICAL)
1645            nameSizer.Add((5,0),)
1646            if not rbData['useCount']:
1647                delRB = wx.CheckBox(VectorRBDisplay,-1,'Delete?')
1648                Indx[delRB.GetId()] = rbId
1649                delRB.Bind(wx.EVT_CHECKBOX,OnDelRB)
1650                nameSizer.Add(delRB,0,wx.ALIGN_CENTER_VERTICAL)
1651            return nameSizer
1652           
1653        def rbRefAtmSizer(rbId,rbData):
1654           
1655            def OnRefSel(event):
1656                Obj = event.GetEventObject()
1657                iref = Indx[Obj.GetId()]
1658                sel = Obj.GetValue()
1659                rbData['rbRef'][iref] = atNames.index(sel)
1660                FillRefChoice(rbId,rbData)
1661           
1662            refAtmSizer = wx.BoxSizer(wx.HORIZONTAL)
1663            atNames = [name+str(i) for i,name in enumerate(rbData['rbTypes'])]
1664            rbRef = rbData.get('rbRef',[0,1,2,False])
1665            rbData['rbRef'] = rbRef
1666            if rbData['useCount']:
1667                refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1,
1668                    'Orientation reference atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \
1669                     atNames[rbRef[1]],atNames[rbRef[2]])),0)
1670            else:
1671                refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1,
1672                    'Orientation reference atoms A-B-C: '),0,wx.ALIGN_CENTER_VERTICAL)
1673                for i in range(3):
1674                    choices = [atNames[j] for j in refChoice[rbId][i]]
1675                    refSel = wx.ComboBox(VectorRBDisplay,-1,value='',
1676                        choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN)
1677                    refSel.SetValue(atNames[rbRef[i]])
1678                    refSel.Bind(wx.EVT_COMBOBOX, OnRefSel)
1679                    Indx[refSel.GetId()] = i
1680                    refAtmSizer.Add(refSel,0,wx.ALIGN_CENTER_VERTICAL)
1681            return refAtmSizer
1682                       
1683        def rbVectMag(rbId,imag,rbData):
1684           
1685            def OnRBVectorMag(event):
1686                event.Skip()
1687                Obj = event.GetEventObject()
1688                rbId,imag = Indx[Obj.GetId()]
1689                try:
1690                    val = float(Obj.GetValue())
1691                    if val <= 0.:
1692                        raise ValueError
1693                    rbData['VectMag'][imag] = val
1694                except ValueError:
1695                    pass
1696                Obj.SetValue('%8.4f'%(val))
1697                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1698                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbId],plotDefaults)
1699               
1700            def OnRBVectorRef(event):
1701                Obj = event.GetEventObject()
1702                rbId,imag = Indx[Obj.GetId()]
1703                rbData['VectRef'][imag] = Obj.GetValue()
1704                       
1705            magSizer = wx.wx.BoxSizer(wx.HORIZONTAL)
1706            magSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Translation magnitude: '),
1707                0,wx.ALIGN_CENTER_VERTICAL)
1708            magValue = wx.TextCtrl(VectorRBDisplay,-1,'%8.4f'%(rbData['VectMag'][imag]))
1709            Indx[magValue.GetId()] = [rbId,imag]
1710            magValue.Bind(wx.EVT_TEXT_ENTER,OnRBVectorMag)
1711            magValue.Bind(wx.EVT_KILL_FOCUS,OnRBVectorMag)
1712            magSizer.Add(magValue,0,wx.ALIGN_CENTER_VERTICAL)
1713            magSizer.Add((5,0),)
1714            magref = wx.CheckBox(VectorRBDisplay,-1,label=' Refine?') 
1715            magref.SetValue(rbData['VectRef'][imag])
1716            magref.Bind(wx.EVT_CHECKBOX,OnRBVectorRef)
1717            Indx[magref.GetId()] = [rbId,imag]
1718            magSizer.Add(magref,0,wx.ALIGN_CENTER_VERTICAL)
1719            return magSizer
1720           
1721        def rbVectors(rbId,imag,mag,XYZ,rbData):
1722
1723            def TypeSelect(event):
1724                AtInfo = data['Vector']['AtInfo']
1725                r,c = event.GetRow(),event.GetCol()
1726                if vecGrid.GetColLabelValue(c) == 'Type':
1727                    PE = G2elemGUI.PickElement(G2frame,oneOnly=True)
1728                    if PE.ShowModal() == wx.ID_OK:
1729                        if PE.Elem != 'None':
1730                            El = PE.Elem.strip().lower().capitalize()
1731                            if El not in AtInfo:
1732                                Info = G2elem.GetAtomInfo(El)
1733                                AtInfo[El] = [Info['Drad'],Info['Color']]
1734                            rbData['rbTypes'][r] = El
1735                            vecGrid.SetCellValue(r,c,El)
1736                    PE.Destroy()
1737                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1738
1739            def ChangeCell(event):
1740                r,c =  event.GetRow(),event.GetCol()
1741                if r >= 0 and (0 <= c < 3):
1742                    try:
1743                        val = float(vecGrid.GetCellValue(r,c))
1744                        rbData['rbVect'][imag][r][c] = val
1745                    except ValueError:
1746                        pass
1747                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbId],plotDefaults)
1748                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1749
1750            vecSizer = wx.BoxSizer()
1751            Types = 3*[wg.GRID_VALUE_FLOAT+':10,5',]+[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',]
1752            colLabels = ['Vector x','Vector y','Vector z','Type','Cart x','Cart y','Cart z']
1753            table = []
1754            rowLabels = []
1755            for ivec,xyz in enumerate(rbData['rbVect'][imag]):
1756                table.append(list(xyz)+[rbData['rbTypes'][ivec],]+list(XYZ[ivec]))
1757                rowLabels.append(str(ivec))
1758            vecTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1759            vecGrid = G2G.GSGrid(VectorRBDisplay)
1760            vecGrid.SetTable(vecTable, True)
1761            vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell)
1762            if not imag:
1763                vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect)
1764            attr = wx.grid.GridCellAttr()
1765            attr.SetEditor(G2G.GridFractionEditor(vecGrid))
1766            for c in range(3):
1767                vecGrid.SetColAttr(c, attr)
1768            for row in range(vecTable.GetNumberRows()):
1769                if imag:
1770                    vecGrid.SetCellStyle(row,3,VERY_LIGHT_GREY,True)                   
1771                for col in [4,5,6]:
1772                    vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True)
1773            vecGrid.SetMargins(0,0)
1774            vecGrid.AutoSizeColumns(False)
1775            vecSizer.Add(vecGrid)
1776            return vecSizer
1777       
1778        def FillRefChoice(rbId,rbData):
1779            choiceIds = [i for i in range(len(rbData['rbTypes']))]
1780           
1781            rbRef = rbData.get('rbRef',[-1,-1,-1,False])
1782            for i in range(3):
1783                choiceIds.remove(rbRef[i])
1784            refChoice[rbId] = [choiceIds[:],choiceIds[:],choiceIds[:]]
1785            for i in range(3):
1786                refChoice[rbId][i].append(rbRef[i])
1787                refChoice[rbId][i].sort()     
1788           
1789        #VectorRB.DestroyChildren() # bad, deletes scrollbars on Mac!
1790        if VectorRB.GetSizer():
1791            VectorRB.GetSizer().Clear(True)
1792        VectorRBDisplay = wx.Panel(VectorRB)
1793        VectorRBSizer = wx.BoxSizer(wx.VERTICAL)
1794        for rbId in data['RBIds']['Vector']:
1795            if rbId != 'AtInfo':
1796                rbData = data['Vector'][rbId]
1797                FillRefChoice(rbId,rbData)
1798                VectorRBSizer.Add(rbNameSizer(rbId,rbData),0)
1799                VectorRBSizer.Add(rbRefAtmSizer(rbId,rbData),0)
1800                XYZ = np.array([[0.,0.,0.] for Ty in rbData['rbTypes']])
1801                for imag,mag in enumerate(rbData['VectMag']):
1802                    XYZ += mag*rbData['rbVect'][imag]
1803                    VectorRBSizer.Add(rbVectMag(rbId,imag,rbData),0)
1804                    VectorRBSizer.Add(rbVectors(rbId,imag,mag,XYZ,rbData),0)
1805                VectorRBSizer.Add((5,5),0)
1806                data['Vector'][rbId]['rbXYZ'] = XYZ       
1807        VectorRBSizer.Layout()   
1808        VectorRBDisplay.SetSizer(VectorRBSizer,True)
1809        Size = VectorRBSizer.GetMinSize()
1810        Size[0] += 40
1811        Size[1] = max(Size[1],450) + 20
1812        VectorRBDisplay.SetSize(Size)
1813        VectorRB.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1814        VectorRB.Scroll(0,Scroll)
1815        Size[1] = min(Size[1],450)
1816        G2frame.dataFrame.setSizePosLeft(Size)
1817       
1818    def UpdateResidueRB():
1819        AtInfo = data['Residue']['AtInfo']
1820        refChoice = {}
1821        RefObjs = []
1822
1823        def rbNameSizer(rbId,rbData):
1824
1825            def OnRBName(event):
1826                Obj = event.GetEventObject()
1827                rbData['RBname'] = Obj.GetValue()
1828               
1829            def OnDelRB(event):
1830                Obj = event.GetEventObject()
1831                rbId = Indx[Obj.GetId()]
1832                del data['Residue'][rbId]
1833                data['RBIds']['Residue'].remove(rbId)
1834                wx.CallAfter(UpdateResidueRB)
1835               
1836            def OnPlotRB(event):
1837                Obj = event.GetEventObject()
1838                Obj.SetValue(False)
1839                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1840           
1841            nameSizer = wx.BoxSizer(wx.HORIZONTAL)
1842            nameSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Residue name: '),
1843                0,wx.ALIGN_CENTER_VERTICAL)
1844            RBname = wx.TextCtrl(ResidueRBDisplay,-1,rbData['RBname'])
1845            Indx[RBname.GetId()] = rbId
1846            RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName)
1847            RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName)
1848            nameSizer.Add(RBname,0,wx.ALIGN_CENTER_VERTICAL)
1849            nameSizer.Add((5,0),)
1850            plotRB = wx.CheckBox(ResidueRBDisplay,-1,'Plot?')
1851            Indx[plotRB.GetId()] = rbId
1852            plotRB.Bind(wx.EVT_CHECKBOX,OnPlotRB)
1853            nameSizer.Add(plotRB,0,wx.ALIGN_CENTER_VERTICAL)
1854            nameSizer.Add((5,0),)
1855            if not rbData['useCount']:
1856                delRB = wx.CheckBox(ResidueRBDisplay,-1,'Delete?')
1857                Indx[delRB.GetId()] = rbId
1858                delRB.Bind(wx.EVT_CHECKBOX,OnDelRB)
1859                nameSizer.Add(delRB,0,wx.ALIGN_CENTER_VERTICAL)
1860            return nameSizer
1861           
1862        def rbResidues(rbId,rbData):
1863           
1864            def TypeSelect(event):
1865                AtInfo = data['Residue']['AtInfo']
1866                r,c = event.GetRow(),event.GetCol()
1867                if vecGrid.GetColLabelValue(c) == 'Type':
1868                    PE = G2elemGUI.PickElement(G2frame,oneOnly=True)
1869                    if PE.ShowModal() == wx.ID_OK:
1870                        if PE.Elem != 'None':
1871                            El = PE.Elem.strip().lower().capitalize()
1872                            if El not in AtInfo:
1873                                Info = G2elem.GetAtomInfo(El)
1874                                AtInfo[El] = [Info['Drad']['Color']]
1875                            rbData['rbTypes'][r] = El
1876                            vecGrid.SetCellValue(r,c,El)
1877                    PE.Destroy()
1878
1879            def ChangeCell(event):
1880                r,c =  event.GetRow(),event.GetCol()
1881                if r >= 0 and (0 <= c < 3):
1882                    try:
1883                        val = float(vecGrid.GetCellValue(r,c))
1884                        rbData['rbVect'][imag][r][c] = val
1885                    except ValueError:
1886                        pass
1887                       
1888            def RowSelect(event):
1889                r,c =  event.GetRow(),event.GetCol()
1890                if c < 0:                   #only row clicks
1891                    for vecgrid in resList:
1892                        vecgrid.ClearSelection()
1893                    vecGrid.SelectRow(r,True)
1894
1895            def OnRefSel(event):
1896                Obj = event.GetEventObject()
1897                iref,res,jref = Indx[Obj.GetId()]
1898                sel = Obj.GetValue()
1899                ind = atNames.index(sel)
1900                rbData['rbRef'][iref] = ind
1901                FillRefChoice(rbId,rbData)
1902                for i,ref in enumerate(RefObjs[jref]):
1903                    ref.SetItems([atNames[j] for j in refChoice[rbId][i]])
1904                    ref.SetValue(atNames[rbData['rbRef'][i]])
1905                if not iref:     #origin change
1906                    rbXYZ = rbData['rbXYZ']
1907                    rbXYZ -= rbXYZ[ind]
1908                    res.ClearSelection()
1909                    resTable = res.GetTable()
1910                    for r in range(res.GetNumberRows()):
1911                        row = resTable.GetRowValues(r)
1912                        row[2:4] = rbXYZ[r]
1913                        resTable.SetRowValues(r,row)
1914                    res.ForceRefresh()
1915                    G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1916               
1917            Types = 2*[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',]
1918            colLabels = ['Name','Type','Cart x','Cart y','Cart z']
1919            table = []
1920            rowLabels = []
1921            for ivec,xyz in enumerate(rbData['rbXYZ']):
1922                table.append([rbData['atNames'][ivec],]+[rbData['rbTypes'][ivec],]+list(xyz))
1923                rowLabels.append(str(ivec))
1924            vecTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1925            vecGrid = G2G.GSGrid(ResidueRBDisplay)
1926            Indx[vecGrid.GetId()] = rbId
1927            resList.append(vecGrid)
1928            vecGrid.SetTable(vecTable, True)
1929            vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell)
1930            vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect)
1931            vecGrid.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK, RowSelect)
1932            attr = wx.grid.GridCellAttr()
1933            attr.SetEditor(G2G.GridFractionEditor(vecGrid))
1934            for c in range(3):
1935                vecGrid.SetColAttr(c, attr)
1936            for row in range(vecTable.GetNumberRows()):
1937                for col in range(5):
1938                    vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True)
1939            vecGrid.SetMargins(0,0)
1940            vecGrid.AutoSizeColumns(False)
1941            vecSizer = wx.BoxSizer()
1942            vecSizer.Add(vecGrid)
1943           
1944            refAtmSizer = wx.BoxSizer(wx.HORIZONTAL)
1945            atNames = rbData['atNames']
1946            rbRef = rbData['rbRef']
1947            if rbData['rbRef'][3] or rbData['useCount']:
1948                refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
1949                    'Orientation reference atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \
1950                     atNames[rbRef[1]],atNames[rbRef[2]])),0)
1951            else:
1952                refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
1953                    'Orientation reference atoms A-B-C: '),0,wx.ALIGN_CENTER_VERTICAL)
1954                refObj = [0,0,0]
1955                for i in range(3):
1956                    choices = [atNames[j] for j in refChoice[rbId][i]]
1957                    refSel = wx.ComboBox(ResidueRBDisplay,-1,value='',
1958                        choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN)
1959                    refSel.SetValue(atNames[rbRef[i]])
1960                    refSel.Bind(wx.EVT_COMBOBOX, OnRefSel)
1961                    Indx[refSel.GetId()] = [i,vecGrid,len(RefObjs)]
1962                    refObj[i] = refSel
1963                    refAtmSizer.Add(refSel,0,wx.ALIGN_CENTER_VERTICAL)
1964                RefObjs.append(refObj)
1965           
1966            mainSizer = wx.BoxSizer(wx.VERTICAL)
1967            mainSizer.Add(refAtmSizer)
1968            mainSizer.Add(vecSizer)
1969            return mainSizer
1970           
1971        def SeqSizer(angSlide,rbId,iSeq,Seq,atNames):
1972           
1973            def ChangeAngle(event):
1974                event.Skip()
1975                Obj = event.GetEventObject()
1976                rbId,Seq = Indx[Obj.GetId()][:2]
1977                val = Seq[2]
1978                try:
1979                    val = float(Obj.GetValue())
1980                    Seq[2] = val
1981                except ValueError:
1982                    pass
1983                Obj.SetValue('%8.2f'%(val))
1984                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,data['Residue'][rbId],plotDefaults)
1985               
1986            def OnRadBtn(event):
1987                Obj = event.GetEventObject()
1988                Seq,iSeq,angId = Indx[Obj.GetId()]
1989                data['Residue'][rbId]['SelSeq'] = [iSeq,angId]
1990                angSlide.SetValue(int(100*Seq[2]))
1991               
1992            def OnDelBtn(event):
1993                Obj = event.GetEventObject()
1994                rbId,Seq = Indx[Obj.GetId()]
1995                data['Residue'][rbId]['rbSeq'].remove(Seq)       
1996                wx.CallAfter(UpdateResidueRB)
1997           
1998            seqSizer = wx.FlexGridSizer(0,5,2,2)
1999            seqSizer.AddGrowableCol(3,0)
2000            iBeg,iFin,angle,iMove = Seq
2001            ang = wx.TextCtrl(ResidueRBDisplay,-1,'%8.2f'%(angle),size=(50,20))
2002            if not iSeq:
2003                radBt = wx.RadioButton(ResidueRBDisplay,-1,'',style=wx.RB_GROUP)
2004                data['Residue'][rbId]['SelSeq'] = [iSeq,ang.GetId()]
2005            else:
2006                radBt = wx.RadioButton(ResidueRBDisplay,-1,'')
2007            radBt.Bind(wx.EVT_RADIOBUTTON,OnRadBtn)                   
2008            seqSizer.Add(radBt)
2009            delBt = wx.RadioButton(ResidueRBDisplay,-1,'')
2010            delBt.Bind(wx.EVT_RADIOBUTTON,OnDelBtn)
2011            seqSizer.Add(delBt)
2012            bond = wx.TextCtrl(ResidueRBDisplay,-1,'%s %s'%(atNames[iBeg],atNames[iFin]),size=(50,20))
2013            seqSizer.Add(bond,0,wx.ALIGN_CENTER_VERTICAL)
2014            Indx[radBt.GetId()] = [Seq,iSeq,ang.GetId()]
2015            Indx[delBt.GetId()] = [rbId,Seq]
2016            Indx[ang.GetId()] = [rbId,Seq,ang]
2017            ang.Bind(wx.EVT_TEXT_ENTER,ChangeAngle)
2018            ang.Bind(wx.EVT_KILL_FOCUS,ChangeAngle)
2019            seqSizer.Add(ang,0,wx.ALIGN_CENTER_VERTICAL)
2020            atms = ''
2021            for i in iMove:   
2022                atms += ' %s,'%(atNames[i])
2023            moves = wx.TextCtrl(ResidueRBDisplay,-1,atms[:-1],size=(200,20))
2024            seqSizer.Add(moves,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.RIGHT)
2025            return seqSizer
2026           
2027        def SlideSizer():
2028           
2029            def OnSlider(event):
2030                Obj = event.GetEventObject()
2031                rbData = Indx[Obj.GetId()]
2032                iSeq,angId = rbData['SelSeq']
2033                val = float(Obj.GetValue())/100.
2034                rbData['rbSeq'][iSeq][2] = val
2035                Indx[angId][2].SetValue('%8.2f'%(val))
2036                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
2037           
2038            slideSizer = wx.BoxSizer(wx.HORIZONTAL)
2039            slideSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Selected torsion angle:'),0)
2040            iSeq,angId = rbData['SelSeq']
2041            angSlide = wx.Slider(ResidueRBDisplay,-1,
2042                int(100*rbData['rbSeq'][iSeq][2]),0,36000,size=(200,20),
2043                style=wx.SL_HORIZONTAL)
2044            angSlide.Bind(wx.EVT_SLIDER, OnSlider)
2045            Indx[angSlide.GetId()] = rbData
2046            slideSizer.Add(angSlide,0)           
2047            return slideSizer,angSlide
2048           
2049        def FillRefChoice(rbId,rbData):
2050            choiceIds = [i for i in range(len(rbData['atNames']))]
2051            for seq in rbData['rbSeq']:
2052                for i in seq[3]:
2053                    try:
2054                        choiceIds.remove(i)
2055                    except ValueError:
2056                        pass
2057            rbRef = rbData['rbRef']
2058            for i in range(3):
2059                try:
2060                    choiceIds.remove(rbRef[i])
2061                except ValueError:
2062                    pass
2063            refChoice[rbId] = [choiceIds[:],choiceIds[:],choiceIds[:]]
2064            for i in range(3):
2065                refChoice[rbId][i].append(rbRef[i])
2066                refChoice[rbId][i].sort()     
2067           
2068        #ResidueRB.DestroyChildren() # bad, deletes scrollbars on Mac!
2069        if ResidueRB.GetSizer():
2070            ResidueRB.GetSizer().Clear(True)
2071        ResidueRBDisplay = wx.Panel(ResidueRB)
2072        ResidueRBSizer = wx.BoxSizer(wx.VERTICAL)
2073        for rbId in data['RBIds']['Residue']:
2074            rbData = data['Residue'][rbId]
2075            FillRefChoice(rbId,rbData)
2076            ResidueRBSizer.Add(rbNameSizer(rbId,rbData),0)
2077            ResidueRBSizer.Add(rbResidues(rbId,rbData),0)
2078            ResidueRBSizer.Add((5,5),0)
2079            if rbData['rbSeq']:
2080                slideSizer,angSlide = SlideSizer()
2081            if len(rbData['rbSeq']):
2082                ResidueRBSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
2083                    'Sel  Del  Bond             Angle      Riding atoms'),
2084                    0,wx.ALIGN_CENTER_VERTICAL)                       
2085            for iSeq,Seq in enumerate(rbData['rbSeq']):
2086                ResidueRBSizer.Add(SeqSizer(angSlide,rbId,iSeq,Seq,rbData['atNames']))
2087            if rbData['rbSeq']:
2088                ResidueRBSizer.Add(slideSizer,)
2089            ResidueRBSizer.Add(wx.StaticText(ResidueRBDisplay,-1,100*'-'))
2090
2091        ResidueRBSizer.Add((5,25),)
2092        ResidueRBSizer.Layout()   
2093        ResidueRBDisplay.SetSizer(ResidueRBSizer,True)
2094        Size = ResidueRBSizer.GetMinSize()
2095        Size[0] += 40
2096        Size[1] = max(Size[1],450) + 20
2097        ResidueRBDisplay.SetSize(Size)
2098        ResidueRB.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
2099        Size[1] = min(600,Size[1])
2100        G2frame.dataFrame.setSizePosLeft(Size)
2101       
2102    def SetStatusLine(text):
2103        Status.SetStatusText(text)                                     
2104
2105    if G2frame.dataDisplay:
2106        G2frame.dataDisplay.Destroy()
2107    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RigidBodyMenu)
2108    G2frame.dataFrame.SetLabel('Rigid bodies')
2109    if not G2frame.dataFrame.GetStatusBar():
2110        Status = G2frame.dataFrame.CreateStatusBar()
2111    SetStatusLine('')
2112
2113    G2frame.dataDisplay = G2G.GSNoteBook(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize())
2114
2115    VectorRB = wx.ScrolledWindow(G2frame.dataDisplay)
2116    G2frame.dataDisplay.AddPage(VectorRB,'Vector rigid bodies')
2117    ResidueRB = wx.ScrolledWindow(G2frame.dataDisplay)
2118    G2frame.dataDisplay.AddPage(ResidueRB,'Residue rigid bodies')
2119    UpdateVectorRB()
2120    G2frame.dataDisplay.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged)
2121    wx.CallAfter(OnPageChanged,None)
Note: See TracBrowser for help on using the repository browser.