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

Last change on this file since 2909 was 2909, checked in by toby, 6 years ago

improve window title; still working on help

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