source: trunk/GSASIIconstrGUI.py @ 1160

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

finish ISODISPLACE fixes; improve show var window; improve help window; add refine checkbox for newvars in constraints display

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