source: trunk/GSASIIconstrGUI.py @ 1943

Last change on this file since 1943 was 1943, checked in by vondreele, 7 years ago

remove H-atom riding constraint editing; unusable at present

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