source: trunk/GSASIIconstrGUI.py @ 903

Last change on this file since 903 was 889, checked in by vondreele, 12 years ago

add torsion angle derivatives in RB
small fixes to other RB stuff

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