source: trunk/GSASIIconstrGUI.py @ 1138

Last change on this file since 1138 was 1138, checked in by toby, 8 years ago

major constraints revision

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