source: trunk/GSASIIconstrGUI.py @ 859

Last change on this file since 859 was 859, checked in by vondreele, 9 years ago

still more rigid body fixes
add thermal motion models
allow copy of RB parms
import of rb models from gpx files
seq refinement results fixes

File size: 62.2 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIconstrGUI - constraint GUI routines
3########### SVN repository information ###################
4# $Date: 2012-12-05 15:38:26 -0600 (Wed, 05 Dec 2012) $
5# $Author: vondreele $
6# $Revision: 810 $
7# $URL: https://subversion.xor.aps.anl.gov/pyGSAS/trunk/GSASIIconstrGUI.py $
8# $Id: GSASIIconstrGUI.py 810 2012-12-05 21:38:26Z vondreele $
9########### SVN repository information ###################
10import sys
11import wx
12import wx.grid as wg
13import time
14import random as ran
15import numpy as np
16import numpy.ma as ma
17import os.path
18import GSASIIpath
19GSASIIpath.SetVersionNumber("$Revision: 810 $")
20import GSASIIElem as G2elem
21import GSASIIElemGUI as G2elemGUI
22import GSASIIstruct as G2str
23import GSASIImapvars as G2mv
24import GSASIIgrid as G2gd
25import GSASIIplot as G2plt
26VERY_LIGHT_GREY = wx.Colour(235,235,235)
27
28class MultiIntegerDialog(wx.Dialog):
29   
30    def __init__(self,parent,title,prompts,values):
31        wx.Dialog.__init__(self,parent,-1,title, 
32            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
33        self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
34        self.values = values
35        self.prompts = prompts
36        self.Draw()
37       
38    def Draw(self):
39       
40        def OnValItem(event):
41            Obj = event.GetEventObject()
42            ind = Indx[Obj.GetId()]
43            try:
44                val = int(Obj.GetValue())
45                if val <= 0:
46                    raise ValueError
47            except ValueError:
48                val = self.values[ind]
49            self.values[ind] = val
50            Obj.SetValue('%d'%(val))
51           
52        self.panel.Destroy()
53        self.panel = wx.Panel(self)
54        mainSizer = wx.BoxSizer(wx.VERTICAL)
55        Indx = {}
56        for ival,[prompt,value] in enumerate(zip(self.prompts,self.values)):
57            mainSizer.Add(wx.StaticText(self.panel,-1,prompt),0,wx.ALIGN_CENTER)
58            valItem = wx.TextCtrl(self.panel,-1,value='%d'%(value),style=wx.TE_PROCESS_ENTER)
59            mainSizer.Add(valItem,0,wx.ALIGN_CENTER)
60            Indx[valItem.GetId()] = ival
61            valItem.Bind(wx.EVT_TEXT_ENTER,OnValItem)
62            valItem.Bind(wx.EVT_KILL_FOCUS,OnValItem)
63        OkBtn = wx.Button(self.panel,-1,"Ok")
64        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
65        CancelBtn = wx.Button(self.panel,-1,'Cancel')
66        CancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
67        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
68        btnSizer.Add((20,20),1)
69        btnSizer.Add(OkBtn)
70        btnSizer.Add(CancelBtn)
71        btnSizer.Add((20,20),1)
72        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
73        self.panel.SetSizer(mainSizer)
74        self.panel.Fit()
75        self.Fit()
76
77    def GetValues(self):
78        return self.values
79       
80    def OnOk(self,event):
81        parent = self.GetParent()
82        parent.Raise()
83        self.EndModal(wx.ID_OK)             
84       
85    def OnCancel(self,event):
86        parent = self.GetParent()
87        parent.Raise()
88        self.EndModal(wx.ID_CANCEL)
89       
90################################################################################
91#####  Constraints
92################################################################################           
93       
94def UpdateConstraints(G2frame,data):
95    '''Called when Constraints tree item is selected.
96    Displays the constraints in the data window
97    '''
98    if not data:
99        data.update({'Hist':[],'HAP':[],'Phase':[]})       #empty dict - fill it
100    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
101    AtomDict = dict([Phases[phase]['pId'],Phases[phase]['Atoms']] for phase in Phases)
102    Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable = G2str.GetPhaseData(Phases,Print=False)
103    phaseList = []
104    for item in phaseDict:
105        if item.split(':')[2] not in ['Ax','Ay','Az','Amul','AI/A','Atype','SHorder']:
106            phaseList.append(item)
107    phaseList.sort()
108    phaseAtNames = {}
109    phaseAtTypes = {}
110    TypeList = []
111    for item in phaseList:
112        Split = item.split(':')
113        if Split[2][:2] in ['AU','Af','dA']:
114            Id = int(Split[0])
115            phaseAtNames[item] = AtomDict[Id][int(Split[3])][0]
116            phaseAtTypes[item] = AtomDict[Id][int(Split[3])][1]
117            if phaseAtTypes[item] not in TypeList:
118                TypeList.append(phaseAtTypes[item])
119        else:
120            phaseAtNames[item] = ''
121            phaseAtTypes[item] = ''
122           
123    hapVary,hapDict,controlDict = G2str.GetHistogramPhaseData(Phases,Histograms,Print=False)
124    hapList = hapDict.keys()
125    hapList.sort()
126    histVary,histDict,controlDict = G2str.GetHistogramData(Histograms,Print=False)
127    histList = []
128    for item in histDict:
129        if item.split(':')[2] not in ['Omega','Type','Chi','Phi','Azimuth','Gonio. radius','Lam1','Lam2','Back']:
130            histList.append(item)
131    histList.sort()
132    Indx = {}
133    scope = {}                          #filled out later
134    G2frame.Page = [0,'phs']
135   
136    def GetPHlegends(Phases,Histograms):
137        plegend = '\n In p::name'
138        hlegend = '\n In :h:name'
139        phlegend = '\n In p:h:name'
140        for phase in Phases:
141            plegend += '\n p:: = '+str(Phases[phase]['pId'])+':: for '+phase
142            count = 0
143            for histogram in Phases[phase]['Histograms']:
144                if count < 3:
145                    phlegend += '\n p:h: = '+str(Phases[phase]['pId'])+':'+str(Histograms[histogram]['hId'])+': for '+phase+' in '+histogram
146                else:
147                    phlegend += '\n ... etc.'
148                    break
149                count += 1
150        count = 0
151        for histogram in Histograms:
152            if count < 3:
153                hlegend += '\n :h: = :'+str(Histograms[histogram]['hId'])+': for '+histogram
154            else:
155                hlegend += '\n ... etc.'
156                break
157            count += 1
158        return plegend,hlegend,phlegend
159       
160    def FindEquivVarb(name,nameList):
161        outList = []
162        phlist = []
163        items = name.split(':')
164        namelist = [items[2],]
165        if 'dA' in name:
166            namelist = ['dAx','dAy','dAz']
167        elif 'AU' in name:
168            namelist = ['AUiso','AU11','AU22','AU33','AU12','AU13','AU23']
169        for item in nameList:
170            keys = item.split(':')
171            if keys[0] not in phlist:
172                phlist.append(keys[0])
173            if keys[2] in namelist and item != name:
174                outList.append(item)
175        if items[1]:
176            for key in phlist:
177                outList.append(key+':all:'+items[2])
178        return outList
179       
180    def SelectVarbs(page,FrstVarb,varList,legend,constType):
181        '''Select variables used in Constraints after one variable has
182        been selected which determines the appropriate variables to be
183        used here. Then creates the constraint and adds it to the
184        constraints list.
185        Called from OnAddEquivalence, OnAddFunction & OnAddConstraint
186        '''
187        #future -  add 'all:all:name', '0:all:name', etc. to the varList
188        if page[1] == 'phs':
189            atchoice = [item+' for '+phaseAtNames[item] for item in varList]
190            atchoice += [FrstVarb+' for all']
191            atchoice += [FrstVarb+' for all '+atype for atype in TypeList]
192            dlg = wx.MultiChoiceDialog(G2frame,'Select more variables:'+legend,
193                'Constrain '+FrstVarb+' and...',atchoice)
194        else:
195            dlg = wx.MultiChoiceDialog(G2frame,'Select more variables:'+legend,
196                'Constrain '+FrstVarb+' and...',varList)
197        varbs = [FrstVarb,]
198        if dlg.ShowModal() == wx.ID_OK:
199            sel = dlg.GetSelections()
200            try:
201                for x in sel:
202                    if ':all:' in varList[x]:       #a histogram 'all' - supercedes any individual selection
203                        varbs = [FrstVarb,]
204                        items = varList[x].split(':')
205                        for item in varList:
206                            if items[0] == item.split(':')[0] and ':all:' not in item:
207                                varbs.append(item)
208                        break
209                    else:
210                        varbs.append(varList[x])
211            except IndexError:      # one of the 'all' chosen - supercedes any individual selection
212                varbs = [FrstVarb,]
213                Atypes = []
214                for x in sel:
215                    item = atchoice[x]
216                    if 'all' in item:
217                        Atypes.append(item.split('all')[1].strip())
218                if '' in Atypes:
219                    varbs += varList
220                else:
221                    for item in varList:
222                        if phaseAtTypes[item] in Atypes:
223                            varbs.append(item) 
224        dlg.Destroy()
225        if len(varbs) > 1:
226            if 'equivalence' in constType:
227                constr = [[1.0,FrstVarb]]
228                for item in varbs[1:]:
229                    constr += [[1.0,item]]
230                return [constr+[None,None,'e']]      # list of equivalent variables & mults
231            elif 'function' in constType:
232                constr = map(list,zip([1.0 for i in range(len(varbs))],varbs))
233                return [constr+[None,False,'f']]         #just one constraint
234            else:       #'constraint'
235                constr = map(list,zip([1.0 for i in range(len(varbs))],varbs))
236                return [constr+[1.0,None,'c']]          #just one constraint - default sum to one
237        return []
238
239    def CheckAddedConstraint(newcons):
240        '''Check a new constraint that has just been input.
241        If there is an error display a message and give the user a
242        choice to keep or discard the last entry (why keep? -- they
243        may want to delete something else or edit multipliers).
244        Since the varylist is not available, no warning messages
245        should be generated.
246        Returns True if constraint should be added
247        '''
248        allcons = []
249        for key in 'Hist','HAP','Phase':
250            allcons += data[key]
251        allcons += newcons
252        if not len(allcons): return True
253        G2mv.InitVars()   
254        constDictList,fixedList,ignored = G2str.ProcessConstraints(allcons)
255        errmsg, warnmsg = G2mv.CheckConstraints('',constDictList,fixedList)
256        if errmsg:
257            res = G2frame.ErrorDialog('Constraint Error',
258                'Error with newly added constraint:\n'+errmsg+
259                '\n\nDiscard newly added constraint?',parent=G2frame.dataFrame,
260                wtype=wx.YES_NO)
261            return res != wx.ID_YES
262        elif warnmsg:
263            print 'Unexpected contraint warning:\n',warnmsg
264        return True
265
266    def CheckChangedConstraint():
267        '''Check all constraints after an edit has been made.
268        If there is an error display a message and give the user a
269        choice to keep or discard the last edit.
270        Since the varylist is not available, no warning messages
271        should be generated.
272        Returns True if the edit should be retained
273        '''
274        allcons = []
275        for key in 'Hist','HAP','Phase':
276            allcons += data[key]
277        if not len(allcons): return True
278        G2mv.InitVars()   
279        constDictList,fixedList,ignored = G2str.ProcessConstraints(allcons)
280        errmsg, warnmsg = G2mv.CheckConstraints('',constDictList,fixedList)
281        if errmsg:
282            res = G2frame.ErrorDialog('Constraint Error',
283                'Error after editing constraint:\n'+errmsg+
284                '\n\nDiscard last constraint edit?',parent=G2frame.dataFrame,
285                wtype=wx.YES_NO)
286            return res != wx.ID_YES
287        elif warnmsg:
288            print 'Unexpected contraint warning:\n',warnmsg
289        return True
290             
291    def OnAddHold(event):
292        '''add a Hold constraint'''
293        for phase in Phases:
294            Phase = Phases[phase]
295            Atoms = Phase['Atoms']
296        constr = []
297        page = G2frame.Page
298        choice = scope[page[1]]
299        if page[1] == 'phs':
300            atchoice = [item+' for '+phaseAtNames[item] for item in choice[2]]
301            dlg = wx.SingleChoiceDialog(G2frame,'Select 1st variable:'+choice[1],choice[0],atchoice)
302        else:   
303            dlg = wx.SingleChoiceDialog(G2frame,'Select 1st variable:'+choice[1],choice[0],choice[2])
304        if dlg.ShowModal() == wx.ID_OK:
305            sel = dlg.GetSelection()
306            FrstVarb = choice[2][sel]
307            newcons = [[[0.0,FrstVarb],None,None,'h']]
308            if CheckAddedConstraint(newcons):
309                data[choice[3]] += newcons
310        dlg.Destroy()
311        choice[4]()
312       
313    def OnAddEquivalence(event):
314        '''add an Equivalence constraint'''
315        constr = []
316        page = G2frame.Page
317        choice = scope[page[1]]
318        if page[1] == 'phs':
319            atchoice = [item+' for '+phaseAtNames[item] for item in choice[2]]
320            dlg = wx.SingleChoiceDialog(G2frame,'Select 1st variable:'+choice[1],choice[0],atchoice)
321        else:   
322            dlg = wx.SingleChoiceDialog(G2frame,'Select 1st variable:'+choice[1],choice[0],choice[2])
323        if dlg.ShowModal() == wx.ID_OK:
324            sel = dlg.GetSelection()
325            FrstVarb = choice[2][sel]
326            moreVarb = FindEquivVarb(FrstVarb,choice[2])
327            newcons = SelectVarbs(page,FrstVarb,moreVarb,choice[1],'equivalence')
328            if len(newcons) > 0:
329                if CheckAddedConstraint(newcons):
330                    data[choice[3]] += newcons
331        dlg.Destroy()
332        choice[4]()
333   
334    def OnAddFunction(event):
335        '''add a Function (new variable) constraint'''
336        constr = []
337        page = G2frame.Page
338        choice = scope[page[1]]
339        if page[1] == 'phs':
340            atchoice = [item+' for '+phaseAtNames[item] for item in choice[2]]
341            dlg = wx.SingleChoiceDialog(G2frame,'Select 1st variable:'+choice[1],choice[0],atchoice)
342        else:   
343            dlg = wx.SingleChoiceDialog(G2frame,'Select 1st variable:'+choice[1],choice[0],choice[2])
344        if dlg.ShowModal() == wx.ID_OK:
345            sel = dlg.GetSelection()
346            FrstVarb = choice[2][sel]
347            moreVarb = FindEquivVarb(FrstVarb,choice[2])
348            newcons = SelectVarbs(page,FrstVarb,moreVarb,choice[1],'function')
349            if len(newcons) > 0:
350                if CheckAddedConstraint(newcons):
351                    data[choice[3]] += newcons
352        dlg.Destroy()
353        choice[4]()
354                       
355    def OnAddConstraint(event):
356        '''add a constraint equation to the constraints list'''
357        constr = []
358        page = G2frame.Page
359        choice = scope[page[1]]
360        if page[1] == 'phs':
361            atchoice = [item+' for '+phaseAtNames[item] for item in choice[2]]
362            dlg = wx.SingleChoiceDialog(G2frame,'Select 1st variable:'+choice[1],choice[0],atchoice)
363        else:   
364            dlg = wx.SingleChoiceDialog(G2frame,'Select 1st variable:'+choice[1],choice[0],choice[2])
365        if dlg.ShowModal() == wx.ID_OK:
366            sel = dlg.GetSelection()
367            FrstVarb = choice[2][sel]
368            moreVarb = FindEquivVarb(FrstVarb,choice[2])
369            newcons = SelectVarbs(page,FrstVarb,moreVarb,choice[1],'constraint')
370            if len(newcons) > 0:
371                if CheckAddedConstraint(newcons):
372                    data[choice[3]] += newcons
373        dlg.Destroy()
374        choice[4]()
375                       
376    def ConstSizer(name,pageDisplay):
377        '''This creates a sizer displaying all of the constraints entered
378        '''
379        constSizer = wx.FlexGridSizer(1,4,0,0)
380        maxlen = 70 # characters before wrapping a constraint
381        for Id,item in enumerate(data[name]):
382            eqString = ['',]
383            if item[-1] == 'h':
384                constSizer.Add((5,5),0)              # blank space for edit button
385                typeString = ' FIXED   '
386                eqString[-1] = item[0][1]+'   '
387            elif isinstance(item[-1],str):
388                constEdit = wx.Button(pageDisplay,-1,'Edit',style=wx.BU_EXACTFIT)
389                constEdit.Bind(wx.EVT_BUTTON,OnConstEdit)
390                constSizer.Add(constEdit)            # edit button
391                Indx[constEdit.GetId()] = [Id,name]
392                if item[-1] == 'f':
393                    for term in item[:-3]:
394                        if len(eqString[-1]) > maxlen:
395                            eqString.append(' ')
396                        m = term[0]
397                        if eqString[-1] != '':
398                            if m >= 0:
399                                eqString[-1] += ' + '
400                            else:
401                                eqString[-1] += ' - '
402                                m = abs(m)
403                        eqString[-1] += '%.3f*%s '%(m,term[1])
404                    typeString = ' NEWVAR  '
405                    eqString[-1] += ' = New Variable   '
406                elif item[-1] == 'c':
407                    for term in item[:-3]:
408                        if len(eqString[-1]) > maxlen:
409                            eqString.append(' ')
410                        if eqString[-1] != '':
411                            if term[0] > 0:
412                                eqString[-1] += ' + '
413                            else:
414                                eqString[-1] += ' - '
415                        eqString[-1] += '%.3f*%s '%(abs(term[0]),term[1])
416                    typeString = ' CONSTR  '
417                    eqString[-1] += ' = %.3f'%(item[-3])+'  '
418                elif item[-1] == 'e':
419                    for term in item[:-3]:
420                        if term[0] == 0: term[0] = 1.0
421                        if len(eqString[-1]) > maxlen:
422                            eqString.append(' ')
423                        if eqString[-1] == '':
424                            eqString[-1] += '%s '%(term[1])
425                            first = term[0]
426                        else:
427                            eqString[-1] += ' = %.3f*%s '%(first/term[0],term[1])
428                    typeString = ' EQUIV   '
429                else:
430                    print 'Unexpected constraint',item
431            else:
432                print 'Removing old-style constraints'
433                data[name] = []
434                return constSizer
435            constDel = wx.Button(pageDisplay,-1,'Delete',style=wx.BU_EXACTFIT)
436            constDel.Bind(wx.EVT_BUTTON,OnConstDel)
437            Indx[constDel.GetId()] = [Id,name]
438            constSizer.Add(constDel)             # delete button
439            constSizer.Add(wx.StaticText(pageDisplay,-1,typeString),0,wx.ALIGN_CENTER_VERTICAL)
440            EqSizer = wx.BoxSizer(wx.VERTICAL)
441            for s in eqString:
442                EqSizer.Add(wx.StaticText(pageDisplay,-1,s),0,wx.ALIGN_CENTER_VERTICAL)
443            constSizer.Add(EqSizer,0,wx.ALIGN_CENTER_VERTICAL)
444            # if item[-1] == 'f':
445            #     constRef = wx.CheckBox(pageDisplay,-1,label=' Refine?')
446            #     constRef.SetValue(item[-2])
447            #     constRef.Bind(wx.EVT_CHECKBOX,OnConstRef)
448            #     Indx[constRef.GetId()] = item
449            #     constSizer.Add(constRef)
450            # else:
451            #     constSizer.Add((5,5),0)
452        return constSizer
453               
454    # def OnConstRef(event):
455    #     Obj = event.GetEventObject()
456    #     Indx[Obj.GetId()][-2] = Obj.GetValue()
457       
458    def OnConstDel(event):
459        Obj = event.GetEventObject()
460        Id,name = Indx[Obj.GetId()]
461        del(data[name][Id])
462        OnPageChanged(None)       
463       
464    def OnConstEdit(event):
465        '''Called to edit an individual contraint by the Edit button'''
466        Obj = event.GetEventObject()
467        Id,name = Indx[Obj.GetId()]
468        sep = '*'
469        if data[name][Id][-1] == 'f':
470            items = data[name][Id][:-3]+[[],]
471            constType = 'New Variable'
472            lbl = 'Enter value for each term in constraint; sum = new variable'
473        elif data[name][Id][-1] == 'c':
474            items = data[name][Id][:-3]+[
475                [data[name][Id][-3],'fixed value ='],[]]
476            constType = 'Constraint'
477            lbl = 'Edit value for each term in constant constraint sum'
478        elif data[name][Id][-1] == 'e':
479            items = data[name][Id][:-3]+[[],]
480            constType = 'Equivalence'
481            lbl = 'The following terms are set to be equal:'
482            sep = '/'
483        else:
484            return
485        dlg = G2frame.ConstraintDialog(G2frame.dataFrame,constType,lbl,items,sep)
486        try:
487            if dlg.ShowModal() == wx.ID_OK:
488                prev = data[name][Id]
489                result = dlg.GetData()
490                if data[name][Id][-1] == 'c':
491                    data[name][Id][:-3] = result[:-2]
492                    data[name][Id][-3] = result[-2][0]
493                else:
494                    data[name][Id][:-3] = result[:-1]
495                if not CheckChangedConstraint():
496                    data[name][Id] = prev
497        except:
498            import traceback
499            print traceback.format_exc()
500        finally:
501            dlg.Destroy()           
502        OnPageChanged(None)                     
503   
504    def UpdateHAPConstr():
505        '''Responds to press on Histogram/Phase Constraints tab,
506        shows constraints in data window'''
507        HAPConstr.DestroyChildren()
508        HAPDisplay = wx.Panel(HAPConstr)
509        HAPSizer = wx.BoxSizer(wx.VERTICAL)
510        HAPSizer.Add((5,5),0)
511        HAPSizer.Add(ConstSizer('HAP',HAPDisplay))
512        HAPDisplay.SetSizer(HAPSizer,True)
513        Size = HAPSizer.GetMinSize()
514        Size[0] += 40
515        Size[1] = max(Size[1],250) + 20
516        HAPDisplay.SetSize(Size)
517        # scroll bar not working, at least not on Mac
518        HAPConstr.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
519        Size[1] = min(Size[1],250)
520        G2frame.dataFrame.setSizePosLeft(Size)
521       
522    def UpdateHistConstr():
523        '''Responds to press on Histogram Constraints tab,
524        shows constraints in data window'''
525        HistConstr.DestroyChildren()
526        HistDisplay = wx.Panel(HistConstr)
527        HistSizer = wx.BoxSizer(wx.VERTICAL)
528        HistSizer.Add((5,5),0)       
529        HistSizer.Add(ConstSizer('Hist',HistDisplay))
530        HistDisplay.SetSizer(HistSizer,True)
531        Size = HistSizer.GetMinSize()
532        Size[0] += 40
533        Size[1] = max(Size[1],250) + 20
534        HistDisplay.SetSize(Size)
535        HistConstr.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
536        Size[1] = min(Size[1],250)
537        G2frame.dataFrame.setSizePosLeft(Size)
538       
539    def UpdatePhaseConstr():
540        '''Responds to press on Phase Constraint tab,
541        shows constraints in data window'''
542        PhaseConstr.DestroyChildren()
543        PhaseDisplay = wx.Panel(PhaseConstr)
544        PhaseSizer = wx.BoxSizer(wx.VERTICAL)
545        PhaseSizer.Add((5,5),0)       
546        PhaseSizer.Add(ConstSizer('Phase',PhaseDisplay))
547        PhaseDisplay.SetSizer(PhaseSizer,True)
548        Size = PhaseSizer.GetMinSize()
549        Size[0] += 40
550        Size[1] = max(Size[1],250) + 20
551        PhaseDisplay.SetSize(Size)
552        PhaseConstr.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
553        Size[1] = min(Size[1],250)
554        G2frame.dataFrame.setSizePosLeft(Size)
555   
556    def OnPageChanged(event):
557        if event:       #page change event!
558            page = event.GetSelection()
559        else:
560            page = G2frame.dataDisplay.GetSelection()
561        oldPage = G2frame.dataDisplay.ChangeSelection(page)
562        text = G2frame.dataDisplay.GetPageText(page)
563        if text == 'Histogram/Phase constraints':
564            G2frame.Page = [page,'hap']
565            UpdateHAPConstr()
566        elif text == 'Histogram constraints':
567            G2frame.Page = [page,'hst']
568            UpdateHistConstr()
569        elif text == 'Phase constraints':
570            G2frame.Page = [page,'phs']
571            UpdatePhaseConstr()
572
573    def SetStatusLine(text):
574        Status.SetStatusText(text)                                     
575       
576    plegend,hlegend,phlegend = GetPHlegends(Phases,Histograms)
577    scope = {'hst':['Histogram contraints:',hlegend,histList,'Hist',UpdateHistConstr],
578        'hap':['Histogram * Phase contraints:',phlegend,hapList,'HAP',UpdateHAPConstr],
579        'phs':['Phase contraints:',plegend,phaseList,'Phase',UpdatePhaseConstr]}
580    if G2frame.dataDisplay:
581        G2frame.dataDisplay.Destroy()
582    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.ConstraintMenu)
583    G2frame.dataFrame.SetLabel('Constraints')
584    if not G2frame.dataFrame.GetStatusBar():
585        Status = G2frame.dataFrame.CreateStatusBar()
586    SetStatusLine('')
587   
588    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.ConstraintMenu)
589    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAddConstraint, id=G2gd.wxID_CONSTRAINTADD)
590    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAddFunction, id=G2gd.wxID_FUNCTADD)
591    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAddEquivalence, id=G2gd.wxID_EQUIVADD)
592    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAddHold, id=G2gd.wxID_HOLDADD)
593    G2frame.dataDisplay = G2gd.GSNoteBook(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize())
594   
595    PhaseConstr = wx.ScrolledWindow(G2frame.dataDisplay)
596    G2frame.dataDisplay.AddPage(PhaseConstr,'Phase constraints')
597    HAPConstr = wx.ScrolledWindow(G2frame.dataDisplay)
598    G2frame.dataDisplay.AddPage(HAPConstr,'Histogram/Phase constraints')
599    HistConstr = wx.ScrolledWindow(G2frame.dataDisplay)
600    G2frame.dataDisplay.AddPage(HistConstr,'Histogram constraints')
601    UpdatePhaseConstr()
602
603    G2frame.dataDisplay.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, OnPageChanged)
604    # validate all the constrants -- should not see any errors here normally
605    allcons = []
606    for key in 'Hist','HAP','Phase':
607        allcons += data[key]
608    if not len(allcons): return
609    G2mv.InitVars()   
610    constDictList,fixedList,ignored = G2str.ProcessConstraints(allcons)
611    errmsg, warnmsg = G2mv.CheckConstraints('',constDictList,fixedList)
612    if errmsg:
613        G2frame.ErrorDialog('Constraint Error','Error in constraints:\n'+errmsg,
614            parent=G2frame.dataFrame)
615    elif warnmsg:
616        print 'Unexpected contraint warning:\n',warnmsg
617       
618################################################################################
619#### Rigid bodies
620################################################################################
621
622def UpdateRigidBodies(G2frame,data):
623    '''Called when Rigid bodies tree item is selected.
624    Displays the rigid bodies in the data window
625    '''
626    if not data:
627        data.update({'Vector':{'AtInfo':{}},'Residue':{'AtInfo':{}}})       #empty dict - fill it
628           
629    global resList
630    Indx = {}
631    resList = []
632    plotDefaults = {'oldxy':[0.,0.],'Quaternion':[1.,0.,0.,0.],'cameraPos':30.,'viewDir':[0,0,1],}
633
634    def OnPageChanged(event):
635        global resList
636        resList = []
637        if event:       #page change event!
638            page = event.GetSelection()
639        else:
640            page = G2frame.dataDisplay.GetSelection()
641        oldPage = G2frame.dataDisplay.ChangeSelection(page)
642        text = G2frame.dataDisplay.GetPageText(page)
643        if text == 'Vector rigid bodies':
644            G2frame.dataFrame.RigidBodyMenu.Remove(0)   #NB: wx.MenuBar.Replace gives error
645            G2frame.dataFrame.RigidBodyMenu.Insert(0,G2frame.dataFrame.VectorRBEdit,title='Edit')
646            G2frame.Page = [page,'vrb']
647            UpdateVectorRB()
648        elif text == 'Residue rigid bodies':
649            G2frame.dataFrame.RigidBodyMenu.Remove(0)
650            G2frame.dataFrame.RigidBodyMenu.Insert(0,G2frame.dataFrame.ResidueRBMenu,title='Edit')
651            G2frame.Page = [page,'rrb']
652            UpdateResidueRB()
653        elif text == 'Z-matrix rigid bodies':
654            G2frame.dataFrame.RigidBodyMenu.Remove(0)
655            G2frame.dataFrame.RigidBodyMenu.Insert(0,G2frame.dataFrame.ZMatrixRBMenu,title='Edit')
656            G2frame.Page = [page,'zrb']
657            UpdateZMatrixRB()
658           
659    def getMacroFile(macName):
660        defDir = os.path.join(os.path.split(__file__)[0],'GSASIImacros')
661        dlg = wx.FileDialog(G2frame,message='Choose '+macName+' rigid body macro file',
662            defaultDir=defDir,defaultFile="",wildcard="GSAS-II macro file (*.mac)|*.mac",
663            style=wx.OPEN | wx.CHANGE_DIR)
664        try:
665            if dlg.ShowModal() == wx.ID_OK:
666                macfile = dlg.GetPath()
667                macro = open(macfile,'Ur')
668                head = macro.readline()
669                if macName not in head:
670                    print head
671                    print '**** ERROR - wrong restraint macro file selected, try again ****'
672                    macro = []
673            else: # cancel was pressed
674                macro = []
675        finally:
676            dlg.Destroy()
677        return macro        #advanced past 1st line
678       
679    def getTextFile():
680        defDir = os.path.join(os.path.split(__file__)[0],'GSASIImacros')
681        dlg = wx.FileDialog(G2frame,'Choose rigid body text file', '.', '',
682            "GSAS-II text file (*.txt)|*.txt|XYZ file (*.xyz)|*.xyz|"
683            "Sybyl mol2 file (*.mol2)|*.mol2|PDB file (*.pdb;*.ent)|*.pdb;*.ent",
684            wx.OPEN | wx.CHANGE_DIR)
685        try:
686            if dlg.ShowModal() == wx.ID_OK:
687                txtfile = dlg.GetPath()
688                ext = os.path.splitext(txtfile)[1]
689                text = open(txtfile,'Ur')
690            else: # cancel was pressed
691                ext = ''
692                text = []
693        finally:
694            dlg.Destroy()
695        if 'ent' in ext:
696            ext = '.pdb'
697        return text,ext.lower()
698       
699    def OnAddRigidBody(event):
700        page = G2frame.dataDisplay.GetSelection()
701        if 'Vector' in G2frame.dataDisplay.GetPageText(page):
702            AddVectorRB()
703        elif 'Residue' in G2frame.dataDisplay.GetPageText(page):
704            AddResidueRB()
705           
706    def OnImportRigidBody(event):
707        page = G2frame.dataDisplay.GetSelection()
708        if 'Vector' in G2frame.dataDisplay.GetPageText(page):
709            pass
710        elif 'Residue' in G2frame.dataDisplay.GetPageText(page):
711            ImportResidueRB()
712           
713    def AddVectorRB():
714        AtInfo = data['Vector']['AtInfo']
715        dlg = MultiIntegerDialog(G2frame.dataDisplay,'New Rigid Body',['No. atoms','No. translations'],[1,1])
716        if dlg.ShowModal() == wx.ID_OK:
717            nAtoms,nTrans = dlg.GetValues()
718            vectorRB = data['Vector']
719            rbId = ran.randint(0,sys.maxint)
720            vecMag = [1.0 for i in range(nTrans)]
721            vecRef = [False for i in range(nTrans)]
722            vecVal = [np.zeros((nAtoms,3)) for j in range(nTrans)]
723            rbTypes = ['C' for i in range(nAtoms)]
724            Info = G2elem.GetAtomInfo('C')
725            AtInfo['C'] = [Info['Drad'],Info['Color']]
726            data['Vector'][rbId] = {'RBname':'UNKRB','VectMag':vecMag,'rbXYZ':np.zeros((nAtoms,3)),
727                'rbRef':[0,1,2,False],'VectRef':vecRef,'rbTypes':rbTypes,'rbVect':vecVal,'useCount':0}
728        dlg.Destroy()
729        UpdateVectorRB()
730       
731    def AddResidueRB():
732        AtInfo = data['Residue']['AtInfo']
733        macro = getMacroFile('rigid body')
734        if not macro:
735            return
736        macStr = macro.readline()
737        while macStr:
738            items = macStr.split()
739            if 'I' == items[0]:
740                rbId = ran.randint(0,sys.maxint)
741                rbName = items[1]
742                rbTypes = []
743                rbXYZ = []
744                rbSeq = []
745                atNames = []
746                nAtms,nSeq,nOrig,mRef,nRef = [int(items[i]) for i in [2,3,4,5,6]]
747                for iAtm in range(nAtms):
748                    macStr = macro.readline().split()
749                    atName = macStr[0]
750                    atType = macStr[1]
751                    atNames.append(atName)
752                    rbXYZ.append([float(macStr[i]) for i in [2,3,4]])
753                    rbTypes.append(atType)
754                    if atType not in AtInfo:
755                        Info = G2elem.GetAtomInfo(atType)
756                        AtInfo[atType] = [Info['Drad'],Info['Color']]
757                rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[nOrig-1])
758                for iSeq in range(nSeq):
759                    macStr = macro.readline().split()
760                    mSeq = int(macStr[0])
761                    for jSeq in range(mSeq):
762                        macStr = macro.readline().split()
763                        iBeg = int(macStr[0])-1
764                        iFin = int(macStr[1])-1
765                        angle = 0.0
766                        nMove = int(macStr[2])
767                        iMove = [int(macStr[i])-1 for i in range(3,nMove+3)]
768                        rbSeq.append([iBeg,iFin,angle,iMove])
769                data['Residue'][rbId] = {'RBname':rbName,'rbXYZ':rbXYZ,'rbTypes':rbTypes,
770                    'atNames':atNames,'rbRef':[nOrig-1,mRef-1,nRef-1,True],'rbSeq':rbSeq,
771                    'SelSeq':[0,0],'useCount':0}
772                print 'Rigid body '+rbName+' added'
773            macStr = macro.readline()
774        macro.close()
775        UpdateResidueRB()
776       
777    def ImportResidueRB():
778        AtInfo = data['Residue']['AtInfo']
779        text,ext = getTextFile()
780        if not text:
781            return
782        rbId = ran.randint(0,sys.maxint)
783        rbTypes = []
784        rbXYZ = []
785        rbSeq = []
786        atNames = []
787        txtStr = text.readline()
788        if 'xyz' in ext:
789            txtStr = text.readline()
790            txtStr = text.readline()
791        elif 'mol2' in ext:
792            while 'ATOM' not in txtStr:
793                txtStr = text.readline()
794            txtStr = text.readline()
795        elif 'pdb' in ext:
796            while 'ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]:
797                txtStr = text.readline()
798                print txtStr
799        items = txtStr.split()
800        while len(items):
801            if 'txt' in ext:
802                atName = items[0]
803                atType = items[1]
804                rbXYZ.append([float(items[i]) for i in [2,3,4]])
805            elif 'xyz' in ext:
806                atType = items[0]
807                rbXYZ.append([float(items[i]) for i in [1,2,3]])
808                atName = atType+str(len(rbXYZ))
809            elif 'mol2' in ext:
810                atType = items[1]
811                atName = items[1]+items[0]
812                rbXYZ.append([float(items[i]) for i in [2,3,4]])
813            elif 'pdb' in ext:
814                atType = items[-1]
815                atName = items[2]
816                xyz = txtStr[30:55].split()                   
817                rbXYZ.append([float(x) for x in xyz])
818            atNames.append(atName)
819            rbTypes.append(atType)
820            if atType not in AtInfo:
821                Info = G2elem.GetAtomInfo(atType)
822                AtInfo[atType] = [Info['Drad'],Info['Color']]
823            txtStr = text.readline()
824            if 'mol2' in ext and 'BOND' in txtStr:
825                break
826            if 'pdb' in ext and ('ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]):
827                break
828            items = txtStr.split()
829        rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[0])
830        data['Residue'][rbId] = {'RBname':'UNKRB','rbXYZ':rbXYZ,'rbTypes':rbTypes,
831            'atNames':atNames,'rbRef':[0,1,2,False],'rbSeq':[],'SelSeq':[0,0],'useCount':0}
832        print 'Rigid body UNKRB added'
833        text.close()
834        UpdateResidueRB()
835       
836    def FindNeighbors(Orig,XYZ,atTypes,atNames,AtInfo):
837        Radii = []
838        for Atype in atTypes:
839            Radii.append(AtInfo[Atype][0])
840        Radii = np.array(Radii)
841        Neigh = []
842        Dx = XYZ-XYZ[Orig]
843        dist = np.sqrt(np.sum(Dx**2,axis=1))
844        sumR = Radii[Orig]+Radii
845        IndB = ma.nonzero(ma.masked_greater(dist-0.85*sumR,0.))
846        for j in IndB[0]:
847            if j != Orig:
848                Neigh.append(atNames[j])
849        return Neigh
850       
851    def FindAllNeighbors(XYZ,atTypes,atNames,AtInfo):
852        NeighDict = {}
853        for iat,xyz in enumerate(atNames):
854            NeighDict[atNames[iat]] = FindNeighbors(iat,XYZ,atTypes,atNames,AtInfo)
855        return NeighDict
856       
857    def FindRiding(Orig,Pivot,NeighDict):
858        riding = [Orig,Pivot]
859        iAdd = 1
860        new = True
861        while new:
862            newAtms = NeighDict[riding[iAdd]]
863            for At in newAtms:
864                new = False
865                if At not in riding:
866                    riding.append(At)
867                    new = True
868            iAdd += 1
869            if iAdd < len(riding):
870                new = True
871        return riding[2:]
872                       
873    def OnDefineTorsSeq(event):
874        rbKeys = data['Residue'].keys()
875        rbKeys.remove('AtInfo')
876        rbNames = [data['Residue'][k]['RBname'] for k in rbKeys]
877        rbIds = dict(zip(rbNames,rbKeys))
878        rbNames.sort()
879        rbId = 0
880        if len(rbNames) > 1:
881            dlg = wx.SingleChoiceDialog(G2frame,'Select rigid body for torsion sequence','Torsion sequence',rbNames)
882            if dlg.ShowModal() == wx.ID_OK:
883                sel = dlg.GetSelection()
884                rbId = rbIds[rbNames[sel]]
885                rbData = data['Residue'][rbId]
886            dlg.Destroy()
887        else:
888            rbId = rbIds[rbNames[0]]
889            rbData = data['Residue'][rbId]
890        if not len(rbData):
891            return
892        atNames = rbData['atNames']
893        AtInfo = data['Residue']['AtInfo']
894        atTypes = rbData['rbTypes']
895        XYZ = rbData['rbXYZ']
896        neighDict = FindAllNeighbors(XYZ,atTypes,atNames,AtInfo)
897        TargList = []           
898        dlg = wx.SingleChoiceDialog(G2frame,'Select origin atom for torsion sequence','Origin atom',rbData['atNames'])
899        if dlg.ShowModal() == wx.ID_OK:
900            Orig = dlg.GetSelection()
901            xyz = XYZ[Orig]
902            TargList = neighDict[atNames[Orig]]
903        dlg.Destroy()
904        if not len(TargList):
905            return
906        dlg = wx.SingleChoiceDialog(G2frame,'Select pivot atom for torsion sequence','Pivot atom',TargList)
907        if dlg.ShowModal() == wx.ID_OK:
908            Piv = atNames.index(TargList[dlg.GetSelection()])
909            riding = FindRiding(atNames[Orig],atNames[Piv],neighDict)
910            Riding = []
911            for atm in riding:
912                Riding.append(atNames.index(atm))
913            rbData['rbSeq'].append([Orig,Piv,0.0,Riding])           
914        dlg.Destroy()
915        UpdateResidueRB()
916       
917
918    def UpdateVectorRB(Scroll=0):
919        AtInfo = data['Vector']['AtInfo']
920        refChoice = {}
921        SetStatusLine(' You may use e.g. "sind(60)", "cos(60)", "c60" or "s60" for a vector entry')
922        def rbNameSizer(rbId,rbData):
923
924            def OnRBName(event):
925                Obj = event.GetEventObject()
926                rbId = Indx[Obj.GetId()]
927                rbData['RBname'] = Obj.GetValue()
928               
929            def OnDelRB(event):
930                Obj = event.GetEventObject()
931                rbId = Indx[Obj.GetId()]
932                del data['Vector'][rbId]
933                wx.CallAfter(UpdateVectorRB)
934               
935            def OnPlotRB(event):
936                Obj = event.GetEventObject()
937                rbId = Indx[Obj.GetId()]
938                Obj.SetValue(False)
939                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,rbData,plotDefaults)
940           
941            nameSizer = wx.BoxSizer(wx.HORIZONTAL)
942            nameSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Rigid body name: '),
943                0,wx.ALIGN_CENTER_VERTICAL)
944            RBname = wx.TextCtrl(VectorRBDisplay,-1,rbData['RBname'])
945            Indx[RBname.GetId()] = rbId
946            RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName)
947            RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName)
948            nameSizer.Add(RBname,0,wx.ALIGN_CENTER_VERTICAL)
949            nameSizer.Add((5,0),)
950            plotRB = wx.CheckBox(VectorRBDisplay,-1,'Plot?')
951            Indx[plotRB.GetId()] = rbId
952            plotRB.Bind(wx.EVT_CHECKBOX,OnPlotRB)
953            nameSizer.Add(plotRB,0,wx.ALIGN_CENTER_VERTICAL)
954            nameSizer.Add((5,0),)
955            if not rbData['useCount']:
956                delRB = wx.CheckBox(VectorRBDisplay,-1,'Delete?')
957                Indx[delRB.GetId()] = rbId
958                delRB.Bind(wx.EVT_CHECKBOX,OnDelRB)
959                nameSizer.Add(delRB,0,wx.ALIGN_CENTER_VERTICAL)
960            return nameSizer
961           
962        def rbRefAtmSizer(rbId,rbData):
963           
964            def OnRefSel(event):
965                Obj = event.GetEventObject()
966                iref = Indx[Obj.GetId()]
967                sel = Obj.GetValue()
968                rbData['rbRef'][iref] = atNames.index(sel)
969                FillRefChoice(rbId,rbData)
970           
971            refAtmSizer = wx.BoxSizer(wx.HORIZONTAL)
972            atNames = [name+str(i) for i,name in enumerate(rbData['rbTypes'])]
973            rbRef = rbData.get('rbRef',[0,1,2,False])
974            rbData['rbRef'] = rbRef
975            if rbData['useCount']:
976                refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1,
977                    'Orientation reference atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \
978                     atNames[rbRef[1]],atNames[rbRef[2]])),0)
979            else:
980                refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1,
981                    'Orientation reference atoms A-B-C: '),0,wx.ALIGN_CENTER_VERTICAL)
982                for i in range(3):
983                    choices = [atNames[j] for j in refChoice[rbId][i]]
984                    refSel = wx.ComboBox(VectorRBDisplay,-1,value='',
985                        choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN)
986                    refSel.SetValue(atNames[rbRef[i]])
987                    refSel.Bind(wx.EVT_COMBOBOX, OnRefSel)
988                    Indx[refSel.GetId()] = i
989                    refAtmSizer.Add(refSel,0,wx.ALIGN_CENTER_VERTICAL)
990            return refAtmSizer
991                       
992        def rbVectMag(rbId,imag,rbData):
993           
994            def OnRBVectorMag(event):
995                Obj = event.GetEventObject()
996                rbId,imag = Indx[Obj.GetId()]
997                try:
998                    val = float(Obj.GetValue())
999                    if val <= 0.:
1000                        raise ValueError
1001                    rbData['VectMag'][imag] = val
1002                except ValueError:
1003                    pass
1004                Obj.SetValue('%8.4f'%(val))
1005                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1006                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbId],plotDefaults)
1007               
1008            def OnRBVectorRef(event):
1009                Obj = event.GetEventObject()
1010                rbId,imag = Indx[Obj.GetId()]
1011                rbData['VectRef'][imag] = Obj.GetValue()
1012                       
1013            magSizer = wx.wx.BoxSizer(wx.HORIZONTAL)
1014            magSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Translation magnitude: '),
1015                0,wx.ALIGN_CENTER_VERTICAL)
1016            magValue = wx.TextCtrl(VectorRBDisplay,-1,'%8.4f'%(rbData['VectMag'][imag]))
1017            Indx[magValue.GetId()] = [rbId,imag]
1018            magValue.Bind(wx.EVT_TEXT_ENTER,OnRBVectorMag)
1019            magValue.Bind(wx.EVT_KILL_FOCUS,OnRBVectorMag)
1020            magSizer.Add(magValue,0,wx.ALIGN_CENTER_VERTICAL)
1021            magSizer.Add((5,0),)
1022            magref = wx.CheckBox(VectorRBDisplay,-1,label=' Refine?') 
1023            magref.SetValue(rbData['VectRef'][imag])
1024            magref.Bind(wx.EVT_CHECKBOX,OnRBVectorRef)
1025            Indx[magref.GetId()] = [rbId,imag]
1026            magSizer.Add(magref,0,wx.ALIGN_CENTER_VERTICAL)
1027            return magSizer
1028           
1029        def rbVectors(rbId,imag,mag,XYZ,rbData):
1030
1031            def TypeSelect(event):
1032                Obj = event.GetEventObject()
1033                AtInfo = data['Vector']['AtInfo']
1034                r,c = event.GetRow(),event.GetCol()
1035                if vecGrid.GetColLabelValue(c) == 'Type':
1036                    PE = G2elemGUI.PickElement(G2frame,oneOnly=True)
1037                    if PE.ShowModal() == wx.ID_OK:
1038                        if PE.Elem != 'None':
1039                            El = PE.Elem.strip().lower().capitalize()
1040                            if El not in AtInfo:
1041                                Info = G2elem.GetAtomInfo(El)
1042                                AtInfo[El] = [Info['Drad'],Info['Color']]
1043                            rbData['rbTypes'][r] = El
1044                            vecGrid.SetCellValue(r,c,El)
1045                    PE.Destroy()
1046                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1047
1048            def ChangeCell(event):
1049                Obj = event.GetEventObject()
1050                r,c =  event.GetRow(),event.GetCol()
1051                if r >= 0 and (0 <= c < 3):
1052                    try:
1053                        val = float(vecGrid.GetCellValue(r,c))
1054                        rbData['rbVect'][imag][r][c] = val
1055                    except ValueError:
1056                        pass
1057                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbId],plotDefaults)
1058                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1059
1060            vecSizer = wx.BoxSizer()
1061            Types = 3*[wg.GRID_VALUE_FLOAT+':10,5',]+[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',]
1062            colLabels = ['Vector x','Vector y','Vector z','Type','Cart x','Cart y','Cart z']
1063            table = []
1064            rowLabels = []
1065            for ivec,xyz in enumerate(rbData['rbVect'][imag]):
1066                table.append(list(xyz)+[rbData['rbTypes'][ivec],]+list(XYZ[ivec]))
1067                rowLabels.append(str(ivec))
1068            vecTable = G2gd.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1069            vecGrid = G2gd.GSGrid(VectorRBDisplay)
1070            vecGrid.SetTable(vecTable, True)
1071            vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell)
1072            if not imag:
1073                vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect)
1074            attr = wx.grid.GridCellAttr()
1075            attr.SetEditor(G2gd.GridFractionEditor(vecGrid))
1076            for c in range(3):
1077                vecGrid.SetColAttr(c, attr)
1078            for row in range(vecTable.GetNumberRows()):
1079                if imag:
1080                    vecGrid.SetCellStyle(row,3,VERY_LIGHT_GREY,True)                   
1081                for col in [4,5,6]:
1082                    vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True)
1083            vecGrid.SetMargins(0,0)
1084            vecGrid.AutoSizeColumns(False)
1085            vecSizer.Add(vecGrid)
1086            return vecSizer
1087       
1088        def FillRefChoice(rbId,rbData):
1089            choiceIds = [i for i in range(len(rbData['rbTypes']))]
1090           
1091            rbRef = rbData.get('rbRef',[-1,-1,-1,False])
1092            for i in range(3):
1093                choiceIds.remove(rbRef[i])
1094            refChoice[rbId] = [choiceIds[:],choiceIds[:],choiceIds[:]]
1095            for i in range(3):
1096                refChoice[rbId][i].append(rbRef[i])
1097                refChoice[rbId][i].sort()     
1098           
1099        VectorRB.DestroyChildren()
1100        VectorRBDisplay = wx.Panel(VectorRB)
1101        VectorRBSizer = wx.BoxSizer(wx.VERTICAL)
1102        for rbId in data['Vector']:
1103            if rbId != 'AtInfo':
1104                rbData = data['Vector'][rbId]
1105                FillRefChoice(rbId,rbData)
1106                VectorRBSizer.Add(rbNameSizer(rbId,rbData),0)
1107                VectorRBSizer.Add(rbRefAtmSizer(rbId,rbData),0)
1108                XYZ = np.array([[0.,0.,0.] for Ty in rbData['rbTypes']])
1109                for imag,mag in enumerate(rbData['VectMag']):
1110                    XYZ += mag*rbData['rbVect'][imag]
1111                    VectorRBSizer.Add(rbVectMag(rbId,imag,rbData),0)
1112                    VectorRBSizer.Add(rbVectors(rbId,imag,mag,XYZ,rbData),0)
1113                VectorRBSizer.Add((5,5),0)
1114                data['Vector'][rbId]['rbXYZ'] = XYZ       
1115        VectorRBSizer.Layout()   
1116        VectorRBDisplay.SetSizer(VectorRBSizer,True)
1117        Size = VectorRBSizer.GetMinSize()
1118        Size[0] += 40
1119        Size[1] = max(Size[1],450) + 20
1120        VectorRBDisplay.SetSize(Size)
1121        VectorRB.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1122        VectorRB.Scroll(0,Scroll)
1123        Size[1] = min(Size[1],450)
1124        G2frame.dataFrame.setSizePosLeft(Size)
1125       
1126    def UpdateResidueRB():
1127        AtInfo = data['Residue']['AtInfo']
1128        refChoice = {}
1129        RefObjs = []
1130
1131        def rbNameSizer(rbId,rbData):
1132
1133            def OnRBName(event):
1134                Obj = event.GetEventObject()
1135                rbId = Indx[Obj.GetId()]
1136                rbData['RBname'] = Obj.GetValue()
1137               
1138            def OnDelRB(event):
1139                Obj = event.GetEventObject()
1140                rbId = Indx[Obj.GetId()]
1141                del data['Residue'][rbId]
1142                wx.CallAfter(UpdateResidueRB)
1143               
1144            def OnPlotRB(event):
1145                Obj = event.GetEventObject()
1146                rbId = Indx[Obj.GetId()]
1147                Obj.SetValue(False)
1148                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1149           
1150            nameSizer = wx.BoxSizer(wx.HORIZONTAL)
1151            nameSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Residue name: '),
1152                0,wx.ALIGN_CENTER_VERTICAL)
1153            RBname = wx.TextCtrl(ResidueRBDisplay,-1,rbData['RBname'])
1154            Indx[RBname.GetId()] = rbId
1155            RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName)
1156            RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName)
1157            nameSizer.Add(RBname,0,wx.ALIGN_CENTER_VERTICAL)
1158            nameSizer.Add((5,0),)
1159            plotRB = wx.CheckBox(ResidueRBDisplay,-1,'Plot?')
1160            Indx[plotRB.GetId()] = rbId
1161            plotRB.Bind(wx.EVT_CHECKBOX,OnPlotRB)
1162            nameSizer.Add(plotRB,0,wx.ALIGN_CENTER_VERTICAL)
1163            nameSizer.Add((5,0),)
1164            if not rbData['useCount']:
1165                delRB = wx.CheckBox(ResidueRBDisplay,-1,'Delete?')
1166                Indx[delRB.GetId()] = rbId
1167                delRB.Bind(wx.EVT_CHECKBOX,OnDelRB)
1168                nameSizer.Add(delRB,0,wx.ALIGN_CENTER_VERTICAL)
1169            return nameSizer
1170           
1171        def rbResidues(rbId,rbData):
1172           
1173            def TypeSelect(event):
1174                AtInfo = data['Residue']['AtInfo']
1175                r,c = event.GetRow(),event.GetCol()
1176                if vecGrid.GetColLabelValue(c) == 'Type':
1177                    PE = G2elemGUI.PickElement(G2frame,oneOnly=True)
1178                    if PE.ShowModal() == wx.ID_OK:
1179                        if PE.Elem != 'None':
1180                            El = PE.Elem.strip().lower().capitalize()
1181                            if El not in AtInfo:
1182                                Info = G2elem.GetAtomInfo(El)
1183                                AtInfo[El] = [Info['Drad']['Color']]
1184                            rbData['rbTypes'][r] = El
1185                            vecGrid.SetCellValue(r,c,El)
1186                    PE.Destroy()
1187
1188            def ChangeCell(event):
1189                r,c =  event.GetRow(),event.GetCol()
1190                if r >= 0 and (0 <= c < 3):
1191                    try:
1192                        val = float(vecGrid.GetCellValue(r,c))
1193                        rbData['rbVect'][imag][r][c] = val
1194                    except ValueError:
1195                        pass
1196                       
1197            def RowSelect(event):
1198                r,c =  event.GetRow(),event.GetCol()
1199                if c < 0:                   #only row clicks
1200                    for vecgrid in resList:
1201                        vecgrid.ClearSelection()
1202                    vecGrid.SelectRow(r,True)
1203
1204            def OnRefSel(event):
1205                Obj = event.GetEventObject()
1206                iref,res,jref = Indx[Obj.GetId()]
1207                sel = Obj.GetValue()
1208                ind = atNames.index(sel)
1209                rbData['rbRef'][iref] = ind
1210                FillRefChoice(rbId,rbData)
1211                for i,ref in enumerate(RefObjs[jref]):
1212                    ref.SetItems([atNames[j] for j in refChoice[rbId][i]])
1213                    ref.SetValue(atNames[rbData['rbRef'][i]])
1214                if not iref:     #origin change
1215                    rbXYZ = rbData['rbXYZ']
1216                    rbXYZ -= rbXYZ[ind]
1217                    res.ClearSelection()
1218                    resTable = res.GetTable()
1219                    for r in range(res.GetNumberRows()):
1220                        row = resTable.GetRowValues(r)
1221                        row[2:4] = rbXYZ[r]
1222                        resTable.SetRowValues(r,row)
1223                    res.ForceRefresh()
1224                    G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1225               
1226            Types = 2*[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',]
1227            colLabels = ['Name','Type','Cart x','Cart y','Cart z']
1228            table = []
1229            rowLabels = []
1230            for ivec,xyz in enumerate(rbData['rbXYZ']):
1231                table.append([rbData['atNames'][ivec],]+[rbData['rbTypes'][ivec],]+list(xyz))
1232                rowLabels.append(str(ivec))
1233            vecTable = G2gd.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1234            vecGrid = G2gd.GSGrid(ResidueRBDisplay)
1235            Indx[vecGrid.GetId()] = rbId
1236            resList.append(vecGrid)
1237            vecGrid.SetTable(vecTable, True)
1238            vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell)
1239            vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect)
1240            vecGrid.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK, RowSelect)
1241            attr = wx.grid.GridCellAttr()
1242            attr.SetEditor(G2gd.GridFractionEditor(vecGrid))
1243            for c in range(3):
1244                vecGrid.SetColAttr(c, attr)
1245            for row in range(vecTable.GetNumberRows()):
1246                for col in range(5):
1247                    vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True)
1248            vecGrid.SetMargins(0,0)
1249            vecGrid.AutoSizeColumns(False)
1250            vecSizer = wx.BoxSizer()
1251            vecSizer.Add(vecGrid)
1252           
1253            refAtmSizer = wx.BoxSizer(wx.HORIZONTAL)
1254            atNames = rbData['atNames']
1255            rbRef = rbData['rbRef']
1256            if rbData['rbRef'][3] or rbData['useCount']:
1257                refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
1258                    'Orientation reference atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \
1259                     atNames[rbRef[1]],atNames[rbRef[2]])),0)
1260            else:
1261                refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
1262                    'Orientation reference atoms A-B-C: '),0,wx.ALIGN_CENTER_VERTICAL)
1263                refObj = [0,0,0]
1264                for i in range(3):
1265                    choices = [atNames[j] for j in refChoice[rbId][i]]
1266                    refSel = wx.ComboBox(ResidueRBDisplay,-1,value='',
1267                        choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN)
1268                    refSel.SetValue(atNames[rbRef[i]])
1269                    refSel.Bind(wx.EVT_COMBOBOX, OnRefSel)
1270                    Indx[refSel.GetId()] = [i,vecGrid,len(RefObjs)]
1271                    refObj[i] = refSel
1272                    refAtmSizer.Add(refSel,0,wx.ALIGN_CENTER_VERTICAL)
1273                RefObjs.append(refObj)
1274           
1275            mainSizer = wx.BoxSizer(wx.VERTICAL)
1276            mainSizer.Add(refAtmSizer)
1277            mainSizer.Add(vecSizer)
1278            return mainSizer
1279           
1280        def SeqSizer(angSlide,rbId,iSeq,Seq,atNames):
1281           
1282            def ChangeAngle(event):
1283                Obj = event.GetEventObject()
1284                rbId,Seq = Indx[Obj.GetId()][:2]
1285                val = Seq[2]
1286                try:
1287                    val = float(Obj.GetValue())
1288                    Seq[2] = val
1289                except ValueError:
1290                    pass
1291                Obj.SetValue('%8.2f'%(val))
1292                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,data['Residue'][rbId],plotDefaults)
1293               
1294            def OnRadBtn(event):
1295                Obj = event.GetEventObject()
1296                Seq,iSeq,angId = Indx[Obj.GetId()]
1297                data['Residue'][rbId]['SelSeq'] = [iSeq,angId]
1298                angSlide.SetValue(int(100*Seq[2]))
1299           
1300            seqSizer = wx.FlexGridSizer(0,4,2,2)
1301            seqSizer.AddGrowableCol(3,0)
1302            iBeg,iFin,angle,iMove = Seq
1303            ang = wx.TextCtrl(ResidueRBDisplay,-1,'%8.2f'%(angle),size=(50,20))
1304            if not iSeq:
1305                radBt = wx.RadioButton(ResidueRBDisplay,-1,'',style=wx.RB_GROUP)
1306                data['Residue'][rbId]['SelSeq'] = [iSeq,ang.GetId()]
1307            else:
1308                radBt = wx.RadioButton(ResidueRBDisplay,-1,'')
1309            radBt.Bind(wx.EVT_RADIOBUTTON,OnRadBtn)                   
1310            seqSizer.Add(radBt)
1311            bond = wx.TextCtrl(ResidueRBDisplay,-1,'%s %s'%(atNames[iBeg],atNames[iFin]),size=(50,20))
1312            seqSizer.Add(bond,0,wx.ALIGN_CENTER_VERTICAL)
1313            Indx[radBt.GetId()] = [Seq,iSeq,ang.GetId()]
1314            Indx[ang.GetId()] = [rbId,Seq,ang]
1315            ang.Bind(wx.EVT_TEXT_ENTER,ChangeAngle)
1316            ang.Bind(wx.EVT_KILL_FOCUS,ChangeAngle)
1317            seqSizer.Add(ang,0,wx.ALIGN_CENTER_VERTICAL)
1318            atms = ''
1319            for i in iMove:   
1320                atms += ' %s,'%(atNames[i])
1321            moves = wx.TextCtrl(ResidueRBDisplay,-1,atms[:-1],size=(200,20))
1322            seqSizer.Add(moves,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.RIGHT)
1323            return seqSizer
1324           
1325        def SlideSizer():
1326           
1327            def OnSlider(event):
1328                Obj = event.GetEventObject()
1329                rbData = Indx[Obj.GetId()]
1330                iSeq,angId = rbData['SelSeq']
1331                val = float(Obj.GetValue())/100.
1332                rbData['rbSeq'][iSeq][2] = val
1333                Indx[angId][2].SetValue('%8.2f'%(val))
1334                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1335           
1336            slideSizer = wx.BoxSizer(wx.HORIZONTAL)
1337            slideSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Selected torsion angle:'),0)
1338            iSeq,angId = rbData['SelSeq']
1339            angSlide = wx.Slider(ResidueRBDisplay,-1,
1340                int(100*rbData['rbSeq'][iSeq][2]),0,36000,size=(200,20),
1341                style=wx.SL_HORIZONTAL)
1342            angSlide.Bind(wx.EVT_SLIDER, OnSlider)
1343            Indx[angSlide.GetId()] = rbData
1344            slideSizer.Add(angSlide,0)           
1345            return slideSizer,angSlide
1346           
1347        def FillRefChoice(rbId,rbData):
1348            choiceIds = [i for i in range(len(rbData['atNames']))]
1349            for seq in rbData['rbSeq']:
1350                for i in seq[3]:
1351                    try:
1352                        choiceIds.remove(i)
1353                    except ValueError:
1354                        pass
1355            rbRef = rbData['rbRef']
1356            for i in range(3):
1357                try:
1358                    choiceIds.remove(rbRef[i])
1359                except ValueError:
1360                    pass
1361            refChoice[rbId] = [choiceIds[:],choiceIds[:],choiceIds[:]]
1362            for i in range(3):
1363                refChoice[rbId][i].append(rbRef[i])
1364                refChoice[rbId][i].sort()     
1365           
1366        ResidueRB.DestroyChildren()
1367        ResidueRBDisplay = wx.Panel(ResidueRB)
1368        ResidueRBSizer = wx.BoxSizer(wx.VERTICAL)
1369        rbKeys = data['Residue'].keys()
1370        rbKeys.remove('AtInfo')
1371        rbNames = [data['Residue'][k]['RBname'] for k in rbKeys]
1372        rbIds = dict(zip(rbNames,rbKeys))
1373        rbNames.sort()
1374        for name in rbNames:
1375            rbId = rbIds[name]
1376            rbData = data['Residue'][rbId]
1377            FillRefChoice(rbId,rbData)
1378            ResidueRBSizer.Add(rbNameSizer(rbId,rbData),0)
1379            ResidueRBSizer.Add(rbResidues(rbId,rbData),0)
1380            ResidueRBSizer.Add((5,5),0)
1381            if rbData['rbSeq']:
1382                slideSizer,angSlide = SlideSizer()
1383            if len(rbData['rbSeq']):
1384                ResidueRBSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
1385                    'Sel  Bond             Angle      Riding atoms'),
1386                    0,wx.ALIGN_CENTER_VERTICAL)                       
1387            for iSeq,Seq in enumerate(rbData['rbSeq']):
1388                ResidueRBSizer.Add(SeqSizer(angSlide,rbId,iSeq,Seq,rbData['atNames']))
1389            if rbData['rbSeq']:
1390                ResidueRBSizer.Add(slideSizer,)
1391            ResidueRBSizer.Add(wx.StaticText(ResidueRBDisplay,-1,100*'-'))
1392
1393        ResidueRBSizer.Add((5,25),)
1394        ResidueRBSizer.Layout()   
1395        ResidueRBDisplay.SetSizer(ResidueRBSizer,True)
1396        Size = ResidueRBSizer.GetMinSize()
1397        Size[0] += 40
1398        Size[1] = max(Size[1],450) + 20
1399        ResidueRBDisplay.SetSize(Size)
1400        ResidueRB.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1401        Size[1] = min(600,Size[1])
1402        G2frame.dataFrame.setSizePosLeft(Size)
1403       
1404    def SetStatusLine(text):
1405        Status.SetStatusText(text)                                     
1406
1407    if G2frame.dataDisplay:
1408        G2frame.dataDisplay.Destroy()
1409    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RigidBodyMenu)
1410    G2frame.dataFrame.SetLabel('Rigid bodies')
1411    if not G2frame.dataFrame.GetStatusBar():
1412        Status = G2frame.dataFrame.CreateStatusBar()
1413    SetStatusLine('')
1414
1415    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RigidBodyMenu)
1416    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAddRigidBody, id=G2gd.wxID_RIGIDBODYADD)   
1417    G2frame.dataFrame.Bind(wx.EVT_MENU, OnImportRigidBody, id=G2gd.wxID_RIGIDBODYIMPORT)
1418    G2frame.dataFrame.Bind(wx.EVT_MENU, OnDefineTorsSeq, id=G2gd.wxID_RESIDUETORSSEQ)
1419    G2frame.dataDisplay = G2gd.GSNoteBook(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize())
1420    G2frame.dataDisplay.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, OnPageChanged)
1421
1422    VectorRB = wx.ScrolledWindow(G2frame.dataDisplay)
1423    G2frame.dataDisplay.AddPage(VectorRB,'Vector rigid bodies')
1424    ResidueRB = wx.ScrolledWindow(G2frame.dataDisplay)
1425    G2frame.dataDisplay.AddPage(ResidueRB,'Residue rigid bodies')
1426    UpdateVectorRB()
1427   
Note: See TracBrowser for help on using the repository browser.