source: branch/logging/GSASIIconstrGUI.py @ 2317

Last change on this file since 2317 was 1497, checked in by toby, 11 years ago

logging more complete

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