source: trunk/GSASIIconstrGUI.py @ 927

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

more G2str fixes

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