source: trunk/GSASIIgrid.py @ 2351

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

Add MakeRDF to Background File menu - nothing yet
fix Comments display to remove extra \n

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 201.9 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIgrid - data display routines
3########### SVN repository information ###################
4# $Date: 2016-06-28 21:55:27 +0000 (Tue, 28 Jun 2016) $
5# $Author: vondreele $
6# $Revision: 2351 $
7# $URL: trunk/GSASIIgrid.py $
8# $Id: GSASIIgrid.py 2351 2016-06-28 21:55:27Z vondreele $
9########### SVN repository information ###################
10'''
11*GSASIIgrid: Basic GUI routines*
12--------------------------------
13
14'''
15import wx
16import wx.grid as wg
17#import wx.wizard as wz
18#import wx.aui
19import wx.lib.scrolledpanel as wxscroll
20import time
21import copy
22import cPickle
23import sys
24import os
25import random as ran
26import numpy as np
27import numpy.ma as ma
28import scipy.optimize as so
29import GSASIIpath
30GSASIIpath.SetVersionNumber("$Revision: 2351 $")
31import GSASIImath as G2mth
32import GSASIIIO as G2IO
33import GSASIIstrIO as G2stIO
34import GSASIIlattice as G2lat
35import GSASIIplot as G2plt
36import GSASIIpwdGUI as G2pdG
37import GSASIIimgGUI as G2imG
38import GSASIIphsGUI as G2phG
39import GSASIIspc as G2spc
40import GSASIImapvars as G2mv
41import GSASIIconstrGUI as G2cnstG
42import GSASIIrestrGUI as G2restG
43import GSASIIpy3 as G2py3
44import GSASIIobj as G2obj
45import GSASIIexprGUI as G2exG
46import GSASIIlog as log
47import GSASIIctrls as G2G
48
49# trig functions in degrees
50sind = lambda x: np.sin(x*np.pi/180.)
51tand = lambda x: np.tan(x*np.pi/180.)
52cosd = lambda x: np.cos(x*np.pi/180.)
53
54# Define a short name for convenience
55WACV = wx.ALIGN_CENTER_VERTICAL
56
57[ wxID_FOURCALC, wxID_FOURSEARCH, wxID_FOURCLEAR, wxID_PEAKSMOVE, wxID_PEAKSCLEAR, 
58    wxID_CHARGEFLIP, wxID_PEAKSUNIQUE, wxID_PEAKSDELETE, wxID_PEAKSDA,
59    wxID_PEAKSDISTVP, wxID_PEAKSVIEWPT, wxID_FINDEQVPEAKS,wxID_SHOWBONDS,wxID_MULTIMCSA,
60    wxID_SINGLEMCSA,wxID_4DCHARGEFLIP,wxID_TRANSFORMSTRUCTURE,
61] = [wx.NewId() for item in range(17)]
62
63[ wxID_PWDRADD, wxID_HKLFADD, wxID_PWDANALYSIS, wxID_PWDCOPY, wxID_PLOTCTRLCOPY, 
64    wxID_DATADELETE,wxID_DATACOPY,wxID_DATACOPYFLAGS,wxID_DATASELCOPY,wxID_DATAUSE,
65] = [wx.NewId() for item in range(10)]
66
67[ wxID_ATOMSEDITADD, wxID_ATOMSEDITINSERT, wxID_ATOMSEDITDELETE, wxID_ATOMSREFINE, 
68    wxID_ATOMSMODIFY, wxID_ATOMSTRANSFORM, wxID_ATOMSVIEWADD, wxID_ATOMVIEWINSERT,
69    wxID_RELOADDRAWATOMS,wxID_ATOMSDISAGL,wxID_ATOMMOVE,wxID_MAKEMOLECULE,
70    wxID_ASSIGNATMS2RB,wxID_ATOMSPDISAGL, wxID_ISODISP,wxID_ADDHATOM,wxID_UPDATEHATOM,
71    wxID_WAVEVARY,wxID_ATOMSROTATE,
72] = [wx.NewId() for item in range(19)]
73
74[ wxID_DRAWATOMSTYLE, wxID_DRAWATOMLABEL, wxID_DRAWATOMCOLOR, wxID_DRAWATOMRESETCOLOR, 
75    wxID_DRAWVIEWPOINT, wxID_DRAWTRANSFORM, wxID_DRAWDELETE, wxID_DRAWFILLCELL, 
76    wxID_DRAWADDEQUIV, wxID_DRAWFILLCOORD, wxID_DRAWDISAGLTOR,  wxID_DRAWPLANE,
77    wxID_DRAWDISTVP,
78] = [wx.NewId() for item in range(13)]
79
80[ wxID_DRAWRESTRBOND, wxID_DRAWRESTRANGLE, wxID_DRAWRESTRPLANE, wxID_DRAWRESTRCHIRAL,
81] = [wx.NewId() for item in range(4)]
82
83[ wxID_ADDMCSAATOM,wxID_ADDMCSARB,wxID_CLEARMCSARB,wxID_MOVEMCSA,wxID_MCSACLEARRESULTS,
84] = [wx.NewId() for item in range(5)]
85
86[ wxID_CLEARTEXTURE,wxID_REFINETEXTURE,
87] = [wx.NewId() for item in range(2)]
88
89[ wxID_LOADDIFFAX,wxID_LAYERSIMULATE,wxID_SEQUENCESIMULATE, wxID_COPYPHASE,
90] = [wx.NewId() for item in range(4)]
91
92[ wxID_PAWLEYLOAD, wxID_PAWLEYESTIMATE, wxID_PAWLEYUPDATE,
93] = [wx.NewId() for item in range(3)]
94
95[ wxID_IMCALIBRATE,wxID_IMRECALIBRATE,wxID_IMINTEGRATE, wxID_IMCLEARCALIB,wxID_IMRECALIBALL, 
96    wxID_IMCOPYCONTROLS, wxID_INTEGRATEALL, wxID_IMSAVECONTROLS, wxID_IMLOADCONTROLS, wxID_IMAUTOINTEG,
97    wxID_IMCOPYSELECTED, wxID_SAVESELECTEDCONTROLS,
98] = [wx.NewId() for item in range(12)]
99
100[ wxID_MASKCOPY, wxID_MASKSAVE, wxID_MASKLOAD, wxID_NEWMASKSPOT,wxID_NEWMASKARC,wxID_NEWMASKRING,
101    wxID_NEWMASKFRAME, wxID_NEWMASKPOLY,  wxID_MASKLOADNOT,
102] = [wx.NewId() for item in range(9)]
103
104[ wxID_STRSTACOPY, wxID_STRSTAFIT, wxID_STRSTASAVE, wxID_STRSTALOAD,wxID_STRSTSAMPLE,
105    wxID_APPENDDZERO,wxID_STRSTAALLFIT,wxID_UPDATEDZERO,
106] = [wx.NewId() for item in range(8)]
107
108[ wxID_BACKCOPY,wxID_LIMITCOPY, wxID_SAMPLECOPY, wxID_SAMPLECOPYSOME, wxID_BACKFLAGCOPY, wxID_SAMPLEFLAGCOPY,
109    wxID_SAMPLESAVE, wxID_SAMPLELOAD,wxID_ADDEXCLREGION,wxID_SETSCALE,wxID_SAMPLE1VAL,wxID_ALLSAMPLELOAD,
110    wxID_MAKEBACKRDF,
111] = [wx.NewId() for item in range(13)]
112
113[ wxID_INSTPRMRESET,wxID_CHANGEWAVETYPE,wxID_INSTCOPY, wxID_INSTFLAGCOPY, wxID_INSTLOAD,
114    wxID_INSTSAVE, wxID_INST1VAL, wxID_INSTCALIB,wxID_INSTSAVEALL,
115] = [wx.NewId() for item in range(9)]
116
117[ wxID_UNDO,wxID_LSQPEAKFIT,wxID_LSQONECYCLE,wxID_RESETSIGGAM,wxID_CLEARPEAKS,wxID_AUTOSEARCH,
118    wxID_PEAKSCOPY, wxID_SEQPEAKFIT,
119] = [wx.NewId() for item in range(8)]
120
121[  wxID_INDXRELOAD, wxID_INDEXPEAKS, wxID_REFINECELL, wxID_COPYCELL, wxID_MAKENEWPHASE,
122    wxID_EXPORTCELLS,
123] = [wx.NewId() for item in range(6)]
124
125[ wxID_CONSTRAINTADD,wxID_EQUIVADD,wxID_HOLDADD,wxID_FUNCTADD,wxID_ADDRIDING,
126  wxID_CONSPHASE, wxID_CONSHIST, wxID_CONSHAP, wxID_CONSGLOBAL,wxID_EQUIVALANCEATOMS,
127] = [wx.NewId() for item in range(10)]
128
129[ wxID_RESTRAINTADD, wxID_RESTSELPHASE,wxID_RESTDELETE, wxID_RESRCHANGEVAL, 
130    wxID_RESTCHANGEESD,wxID_AARESTRAINTADD,wxID_AARESTRAINTPLOT,
131] = [wx.NewId() for item in range(7)]
132
133[ wxID_RIGIDBODYADD,wxID_DRAWDEFINERB,wxID_RIGIDBODYIMPORT,wxID_RESIDUETORSSEQ,
134    wxID_AUTOFINDRESRB,wxID_GLOBALRESREFINE,wxID_RBREMOVEALL,wxID_COPYRBPARMS,
135    wxID_GLOBALTHERM,wxID_VECTORBODYADD
136] = [wx.NewId() for item in range(10)]
137
138[ wxID_RENAMESEQSEL,wxID_SAVESEQSEL,wxID_SAVESEQSELCSV,wxID_SAVESEQCSV,wxID_PLOTSEQSEL,
139  wxID_ORGSEQSEL,wxADDSEQVAR,wxDELSEQVAR,wxEDITSEQVAR,wxCOPYPARFIT,wxID_AVESEQSEL,
140  wxADDPARFIT,wxDELPARFIT,wxEDITPARFIT,wxDOPARFIT,wxADDSEQDIST,wxADDSEQANGLE
141] = [wx.NewId() for item in range(17)]
142
143[ wxID_MODELCOPY,wxID_MODELFIT,wxID_MODELADD,wxID_ELEMENTADD,wxID_ELEMENTDELETE,
144    wxID_ADDSUBSTANCE,wxID_LOADSUBSTANCE,wxID_DELETESUBSTANCE,wxID_COPYSUBSTANCE,
145    wxID_MODELUNDO,wxID_MODELFITALL,wxID_MODELCOPYFLAGS,
146] = [wx.NewId() for item in range(12)]
147
148[ wxID_SELECTPHASE,wxID_PWDHKLPLOT,wxID_PWD3DHKLPLOT,wxID_3DALLHKLPLOT,wxID_MERGEHKL,
149] = [wx.NewId() for item in range(5)]
150
151[ wxID_PDFCOPYCONTROLS, wxID_PDFSAVECONTROLS, wxID_PDFLOADCONTROLS, 
152    wxID_PDFCOMPUTE, wxID_PDFCOMPUTEALL, wxID_PDFADDELEMENT, wxID_PDFDELELEMENT,
153] = [wx.NewId() for item in range(7)]
154
155[ wxID_MCRON,wxID_MCRLIST,wxID_MCRSAVE,wxID_MCRPLAY,
156] = [wx.NewId() for item in range(4)]
157
158VERY_LIGHT_GREY = wx.Colour(235,235,235)
159
160commonTrans = {'abc':np.eye(3),'a-cb':np.array([[1,0,0],[0,0,-1],[0,1,0]]),
161    'ba-c':np.array([[0,1,0],[1,0,0],[0,0,-1]]),'-cba':np.array([[0,0,-1],[0,1,0],[1,0,0]]),
162    'bca':np.array([[0,1,0],[0,0,1],[1,0,0]]),'cab':np.array([[0,0,1],[1,0,0],[0,1,0]]),
163    'P->R':np.array([[1,-1,0],[0,1,-1],[1,1,1]]),'R->P':np.array([[2./3,1./3,1./3],[-1./3,1./3,1./3],[-1./3,-2./3,1./3]]),
164    'P->A':np.array([[-1,0,0],[0,-1,1],[0,1,1]]),'R->O':np.array([[-1,0,0],[0,-1,0],[0,0,1]]),
165    'P->B':np.array([[-1,0,1],[0,-1,0],[1,0,1]]),'B->P':np.array([[-.5,0,.5],[0,-1,0],[.5,0,.5]]),
166    'P->C':np.array([[1,1,0],[1,-1,0],[0,0,-1]]),'C->P':np.array([[.5,.5,0],[.5,-.5,0],[0,0,-1]]),
167    'P->F':np.array([[-1,1,1],[1,-1,1],[1,1,-1]]),'F->P':np.array([[0,.5,.5],[.5,0,.5],[.5,.5,0]]),   
168    'P->I':np.array([[0,1,1],[1,0,1],[1,1,0]]),'I->P':np.array([[-.5,.5,.5],[.5,-.5,.5],[.5,.5,-.5]]),   
169    'A->P':np.array([[-1,0,0],[0,-.5,.5],[0,.5,.5]]),'O->R':np.array([[-1,0,0],[0,-1,0],[0,0,1]]), 
170    'abc*':np.eye(3), }
171commonNames = ['abc','bca','cab','a-cb','ba-c','-cba','P->A','A->P','P->B','B->P','P->C','C->P',
172    'P->I','I->P','P->F','F->P','P->R','R->P','R->O','O->R','abc*',]
173
174# Should SGMessageBox, SymOpDialog, DisAglDialog be moved?
175
176################################################################################
177#### GSAS-II class definitions
178################################################################################
179
180class SGMessageBox(wx.Dialog):
181    ''' Special version of MessageBox that displays space group & super space group text
182    in two blocks
183    '''
184    def __init__(self,parent,title,text,table,):
185        wx.Dialog.__init__(self,parent,wx.ID_ANY,title,pos=wx.DefaultPosition,
186            style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
187        self.text=text
188        self.table = table
189        self.panel = wx.Panel(self)
190        mainSizer = wx.BoxSizer(wx.VERTICAL)
191        mainSizer.Add((0,10))
192        for line in text:
193            mainSizer.Add(wx.StaticText(self.panel,label='     %s     '%(line)),0,WACV)
194        ncol = self.table[0].count(',')+1
195        tableSizer = wx.FlexGridSizer(0,2*ncol+3,0,0)
196        for j,item in enumerate(self.table):
197            num,flds = item.split(')')
198            tableSizer.Add(wx.StaticText(self.panel,label='     %s  '%(num+')')),0,WACV|wx.ALIGN_LEFT)           
199            flds = flds.replace(' ','').split(',')
200            for i,fld in enumerate(flds):
201                if i < ncol-1:
202                    tableSizer.Add(wx.StaticText(self.panel,label='%s, '%(fld)),0,WACV|wx.ALIGN_RIGHT)
203                else:
204                    tableSizer.Add(wx.StaticText(self.panel,label='%s'%(fld)),0,WACV|wx.ALIGN_RIGHT)
205            if not j%2:
206                tableSizer.Add((20,0))
207        mainSizer.Add(tableSizer,0,wx.ALIGN_LEFT)
208        btnsizer = wx.StdDialogButtonSizer()
209        OKbtn = wx.Button(self.panel, wx.ID_OK)
210        OKbtn.SetDefault()
211        btnsizer.AddButton(OKbtn)
212        btnsizer.Realize()
213        mainSizer.Add((0,10))
214        mainSizer.Add(btnsizer,0,wx.ALIGN_CENTER)
215        self.panel.SetSizer(mainSizer)
216        self.panel.Fit()
217        self.Fit()
218        size = self.GetSize()
219        self.SetSize([size[0]+20,size[1]])
220
221    def Show(self):
222        '''Use this method after creating the dialog to post it
223        '''
224        self.ShowModal()
225        return
226
227################################################################################
228class SymOpDialog(wx.Dialog):
229    '''Class to select a symmetry operator
230    '''
231    def __init__(self,parent,SGData,New=True,ForceUnit=False):
232        wx.Dialog.__init__(self,parent,-1,'Select symmetry operator',
233            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
234        panel = wx.Panel(self)
235        self.SGData = SGData
236        self.New = New
237        self.Force = ForceUnit
238        self.OpSelected = [0,0,0,[0,0,0],False,False]
239        mainSizer = wx.BoxSizer(wx.VERTICAL)
240        if ForceUnit:
241            choice = ['No','Yes']
242            self.force = wx.RadioBox(panel,-1,'Force to unit cell?',choices=choice)
243            self.force.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
244            mainSizer.Add(self.force,0,WACV|wx.TOP,5)
245#        if SGData['SGInv']:
246        choice = ['No','Yes']
247        self.inv = wx.RadioBox(panel,-1,'Choose inversion?',choices=choice)
248        self.inv.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
249        mainSizer.Add(self.inv,0,WACV)
250        if SGData['SGLatt'] != 'P':
251            LattOp = G2spc.Latt2text(SGData['SGLatt']).split(';')
252            self.latt = wx.RadioBox(panel,-1,'Choose cell centering?',choices=LattOp)
253            self.latt.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
254            mainSizer.Add(self.latt,0,WACV)
255        if SGData['SGLaue'] in ['-1','2/m','mmm','4/m','4/mmm']:
256            Ncol = 2
257        else:
258            Ncol = 3
259        OpList = []
260        for Opr in SGData['SGOps']:
261            OpList.append(G2spc.MT2text(Opr))
262        self.oprs = wx.RadioBox(panel,-1,'Choose space group operator?',choices=OpList,
263            majorDimension=Ncol)
264        self.oprs.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
265        mainSizer.Add(self.oprs,0,WACV|wx.BOTTOM,5)
266        mainSizer.Add(wx.StaticText(panel,-1,"   Choose unit cell?"),0,WACV)
267        cellSizer = wx.BoxSizer(wx.HORIZONTAL)
268        cellName = ['X','Y','Z']
269        self.cell = []
270        for i in range(3):
271            self.cell.append(wx.SpinCtrl(panel,-1,cellName[i],size=wx.Size(50,20)))
272            self.cell[-1].SetRange(-3,3)
273            self.cell[-1].SetValue(0)
274            self.cell[-1].Bind(wx.EVT_SPINCTRL, self.OnOpSelect)
275            cellSizer.Add(self.cell[-1],0,WACV)
276        mainSizer.Add(cellSizer,0,WACV|wx.BOTTOM,5)
277        if self.New:
278            choice = ['No','Yes']
279            self.new = wx.RadioBox(panel,-1,'Generate new positions?',choices=choice)
280            self.new.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
281            mainSizer.Add(self.new,0,WACV)
282
283        OkBtn = wx.Button(panel,-1,"Ok")
284        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
285        cancelBtn = wx.Button(panel,-1,"Cancel")
286        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
287        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
288        btnSizer.Add((20,20),1)
289        btnSizer.Add(OkBtn)
290        btnSizer.Add((20,20),1)
291        btnSizer.Add(cancelBtn)
292        btnSizer.Add((20,20),1)
293
294        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
295        panel.SetSizer(mainSizer)
296        panel.Fit()
297        self.Fit()
298
299    def OnOpSelect(self,event):
300#        if self.SGData['SGInv']:
301        self.OpSelected[0] = self.inv.GetSelection()
302        if self.SGData['SGLatt'] != 'P':
303            self.OpSelected[1] = self.latt.GetSelection()
304        self.OpSelected[2] = self.oprs.GetSelection()
305        for i in range(3):
306            self.OpSelected[3][i] = float(self.cell[i].GetValue())
307        if self.New:
308            self.OpSelected[4] = self.new.GetSelection()
309        if self.Force:
310            self.OpSelected[5] = self.force.GetSelection()
311
312    def GetSelection(self):
313        return self.OpSelected
314
315    def OnOk(self,event):
316        parent = self.GetParent()
317        parent.Raise()
318        self.EndModal(wx.ID_OK)
319
320    def OnCancel(self,event):
321        parent = self.GetParent()
322        parent.Raise()
323        self.EndModal(wx.ID_CANCEL)
324       
325################################################################################
326class TransformDialog(wx.Dialog):
327    ''' Phaae transformation
328   
329    :param wx.Frame parent: reference to parent frame (or None)
330    :param phase: phase data
331   
332    #NB: commonNames & commonTrans defined at top of this file
333    '''
334    def __init__(self,parent,phase):
335        wx.Dialog.__init__(self,parent,wx.ID_ANY,'Setup phase transformation', 
336            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
337        self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
338        self.Phase = copy.deepcopy(phase)   #will be a new phase!
339#        self.Super = phase['General']['Super']
340#        if self.Super:
341#            self.Trans = np.eye(4)
342#            self.Vec = np.zeros(4)
343#        else:
344        self.Trans = np.eye(3)
345        self.Vec = np.zeros(3)
346        self.oldSpGrp = phase['General']['SGData']['SpGrp']
347        self.oldSGdata = phase['General']['SGData']
348        self.newSpGrp = self.Phase['General']['SGData']['SpGrp']
349        self.oldCell = phase['General']['Cell'][1:8]
350        self.newCell = self.Phase['General']['Cell'][1:8]
351        self.Common = 'abc'
352        self.Draw()
353
354    def Draw(self):
355               
356        def OnMatValue(event):
357            Obj = event.GetEventObject()
358            ix,iy = Ind[Obj.GetId()]
359            val = Obj.GetValue()
360            try:
361                if '/' in val:
362                    vals = val.split('/')
363                    self.Trans[iy,ix] = float(vals[0])/float(vals[1])
364                else:   
365                    self.Trans[iy,ix] = float(Obj.GetValue())
366            except ValueError:
367                pass
368            Obj.SetValue('%5.3f'%(self.Trans[iy,ix]))
369           
370        def OnVecValue(event):
371            Obj = event.GetEventObject()
372            iy = Ind[Obj.GetId()]
373            val = Obj.GetValue()
374            try:
375                if '/' in val:
376                    vals = val.split('/')
377                    self.Vec[iy] = float(vals[0])/float(vals[1])
378                else:   
379                    self.Vec[iy] = float(Obj.GetValue())
380            except ValueError:
381                pass
382            Obj.SetValue('%5.3f'%(self.Vec[iy]))
383               
384        def OnCommon(event):
385            Obj = event.GetEventObject()
386            self.Common = Obj.GetValue()
387            if '*' in self.Common:
388                A,B = G2lat.cell2AB(self.oldCell[:6])
389                self.newCell[2:5] = [A[2,2],90.,90.]
390                a,b = G2lat.cell2AB(self.newCell[:6])
391                self.Trans = np.inner(a.T,B)    #correct!
392                self.newSpGrp = 'P 1'
393                SGErr,SGData = G2spc.SpcGroup(self.newSpGrp)
394                self.Phase['General']['SGData'] = SGData
395            else:
396                self.Trans = commonTrans[self.Common]
397            OnTest(event)
398       
399        def OnSpaceGroup(event):
400            Flds = SGTxt.GetValue().split()
401            #get rid of extra spaces between fields first
402            for fld in Flds: fld = fld.strip()
403            SpcGp = ' '.join(Flds)
404            if SpcGp == self.newSpGrp: #didn't change it!
405                return
406            # try a lookup on the user-supplied name
407            SpGrpNorm = G2spc.StandardizeSpcName(SpcGp)
408            if SpGrpNorm:
409                SGErr,SGData = G2spc.SpcGroup(SpGrpNorm)
410            else:
411                SGErr,SGData = G2spc.SpcGroup(SpcGp)
412            if SGErr:
413                text = [G2spc.SGErrors(SGErr)+'\nSpace Group set to previous']
414                SGTxt.SetValue(self.newSpGrp)
415                msg = 'Space Group Error'
416                Style = wx.ICON_EXCLAMATION
417                Text = '\n'.join(text)
418                wx.MessageBox(Text,caption=msg,style=Style)
419            else:
420                text,table = G2spc.SGPrint(SGData)
421                self.Phase['General']['SGData'] = SGData
422                self.newSpGrp = SpcGp
423                SGTxt.SetValue(self.Phase['General']['SGData']['SpGrp'])
424                msg = 'Space Group Information'
425                dlg = SGMessageBox(self.panel,msg,text,table)
426                dlg.Show()
427                dlg.Destroy()
428#            if self.Phase['General']['Type'] in ['modulated',]:
429#                self.Phase['General']['SuperSg'] = SetDefaultSSsymbol()
430#                self.Phase['General']['SSGData'] = G2spc.SSpcGroup(generalData['SGData'],generalData['SuperSg'])[1]
431
432        def OnTest(event):
433            self.newCell = G2lat.TransformCell(self.oldCell[:6],self.Trans)
434            wx.CallAfter(self.Draw)
435
436        self.panel.Destroy()
437        self.panel = wx.Panel(self)
438        Ind = {}
439        mainSizer = wx.BoxSizer(wx.VERTICAL)
440        MatSizer = wx.BoxSizer(wx.HORIZONTAL)
441        transSizer = wx.BoxSizer(wx.VERTICAL)
442        transSizer.Add(wx.StaticText(self.panel,label=" XYZ Transformation matrix & vector: M*X+V = X'"))
443#        if self.Super:
444#            Trmat = wx.FlexGridSizer(4,4,0,0)
445#        else:
446        commonSizer = wx.BoxSizer(wx.HORIZONTAL)
447        commonSizer.Add(wx.StaticText(self.panel,label=' Common transformations: '),0,WACV)
448        common = wx.ComboBox(self.panel,value=self.Common,choices=commonNames,
449            style=wx.CB_READONLY|wx.CB_DROPDOWN)
450        common.Bind(wx.EVT_COMBOBOX,OnCommon)
451        commonSizer.Add(common,0,WACV)
452        transSizer.Add(commonSizer)
453        Trmat = wx.FlexGridSizer(3,5,0,0)
454        for iy,line in enumerate(self.Trans):
455            for ix,val in enumerate(line):
456                item = wx.TextCtrl(self.panel,value='%5.3f'%(val),
457                    size=(50,25),style=wx.TE_PROCESS_ENTER)
458                Ind[item.GetId()] = [ix,iy]
459                item.Bind(wx.EVT_TEXT_ENTER,OnMatValue)
460                item.Bind(wx.EVT_KILL_FOCUS,OnMatValue)
461                Trmat.Add(item)
462            Trmat.Add((25,0),0)
463            vec = wx.TextCtrl(self.panel,value='%5.3f'%(self.Vec[iy]),
464                    size=(50,25),style=wx.TE_PROCESS_ENTER)
465            Ind[vec.GetId()] = [iy]       
466            vec.Bind(wx.EVT_TEXT_ENTER,OnVecValue)
467            vec.Bind(wx.EVT_KILL_FOCUS,OnVecValue)
468            Trmat.Add(vec)
469        transSizer.Add(Trmat)
470        MatSizer.Add((10,0),0)
471        MatSizer.Add(transSizer)
472        mainSizer.Add(MatSizer)
473        mainSizer.Add(wx.StaticText(self.panel,label=' Old lattice parameters:'),0,WACV)
474        mainSizer.Add(wx.StaticText(self.panel,label=
475            ' a = %.5f       b = %.5f      c = %.5f'%(self.oldCell[0],self.oldCell[1],self.oldCell[2])),0,WACV)
476        mainSizer.Add(wx.StaticText(self.panel,label=' alpha = %.3f beta = %.3f gamma = %.3f'%
477            (self.oldCell[3],self.oldCell[4],self.oldCell[5])),0,WACV)
478        mainSizer.Add(wx.StaticText(self.panel,label=' volume = %.3f'%(self.oldCell[6])),0,WACV)
479        mainSizer.Add(wx.StaticText(self.panel,label=' New lattice parameters:'),0,WACV)
480        mainSizer.Add(wx.StaticText(self.panel,label=
481            ' a = %.5f       b = %.5f      c = %.5f'%(self.newCell[0],self.newCell[1],self.newCell[2])),0,WACV)
482        mainSizer.Add(wx.StaticText(self.panel,label=' alpha = %.3f beta = %.3f gamma = %.3f'%
483            (self.newCell[3],self.newCell[4],self.newCell[5])),0,WACV)
484        mainSizer.Add(wx.StaticText(self.panel,label=' volume = %.3f'%(self.newCell[6])),0,WACV)
485        sgSizer = wx.BoxSizer(wx.HORIZONTAL)
486        sgSizer.Add(wx.StaticText(self.panel,label='  Space group: '),0,WACV)
487        SGTxt = wx.TextCtrl(self.panel,value=self.newSpGrp,style=wx.TE_PROCESS_ENTER)
488        SGTxt.Bind(wx.EVT_TEXT_ENTER,OnSpaceGroup)
489        SGTxt.Bind(wx.EVT_KILL_FOCUS,OnSpaceGroup)
490        sgSizer.Add(SGTxt,0,WACV)
491        mainSizer.Add(sgSizer,0,WACV)
492
493        TestBtn = wx.Button(self.panel,-1,"Test")
494        TestBtn.Bind(wx.EVT_BUTTON, OnTest)
495        OkBtn = wx.Button(self.panel,-1,"Ok")
496        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
497        cancelBtn = wx.Button(self.panel,-1,"Cancel")
498        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
499        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
500        btnSizer.Add((20,20),1)
501        btnSizer.Add(TestBtn)
502        btnSizer.Add((20,20),1)
503        btnSizer.Add(OkBtn)
504        btnSizer.Add((20,20),1)
505        btnSizer.Add(cancelBtn)
506        btnSizer.Add((20,20),1)
507       
508        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
509        self.panel.SetSizer(mainSizer)
510        self.panel.Fit()
511        self.Fit()
512       
513    def GetSelection(self):
514        self.Phase['General']['Name'] += ' %s'%(self.Common)
515        self.Phase['General']['Cell'][1:] = G2lat.TransformCell(self.oldCell[:6],self.Trans)           
516        return self.Phase,self.Trans,self.Vec
517
518    def OnOk(self,event):
519        parent = self.GetParent()
520        parent.Raise()
521        self.EndModal(wx.ID_OK)
522
523    def OnCancel(self,event):
524        parent = self.GetParent()
525        parent.Raise()
526        self.EndModal(wx.ID_CANCEL)
527       
528################################################################################
529class RotationDialog(wx.Dialog):
530    ''' Get Rotate & translate matrix & vector - currently not used
531    needs rethinking - possible use to rotate a group of atoms about some
532    vector/origin + translation
533   
534    '''
535    def __init__(self,parent):
536        wx.Dialog.__init__(self,parent,wx.ID_ANY,'Atom group rotation/translation', 
537            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
538        self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
539        self.Trans = np.eye(3)
540        self.Vec = np.zeros(3)
541        self.rotAngle = 0.
542        self.rotVec = np.array([0.,0.,1.])
543        self.Expand = ''
544        self.Draw()
545
546    def Draw(self):
547
548        def OnMatValue(event):
549            Obj = event.GetEventObject()
550            ix,iy = Ind[Obj.GetId()]
551            val = Obj.GetValue()
552            if '/' in val:
553                vals = val.split('/')
554                self.Trans[iy,ix] = float(vals[0])/float(vals[1])
555            else:   
556                self.Trans[iy,ix] = float(Obj.GetValue())
557            Obj.SetValue('%5.3f'%(self.Trans[iy,ix]))
558           
559           
560        def OnVecValue(event):
561            Obj = event.GetEventObject()
562            iy = Ind[Obj.GetId()]
563            val = Obj.GetValue()
564            if '/' in val:
565                vals = val.split('/')
566                self.Vec[iy] = float(vals[0])/float(vals[1])
567            else:   
568                self.Vec[iy] = float(Obj.GetValue())
569            Obj.SetValue('%5.3f'%(self.Vec[iy]))
570           
571        def OnExpand(event):
572            self.Expand = expand.GetValue()
573           
574        def OnRotAngle(event):
575            self.rotAngle = float(rotangle.GetValue())
576            rotangle.SetValue('%5.3f'%(self.rotAngle))
577            Q = G2mth.AVdeg2Q(self.rotAngle,self.rotVec)
578            self.Trans = G2mth.Q2Mat(Q)
579            self.Draw()
580           
581        def OnRotVec(event):
582            vals = rotvec.GetValue()
583            vals = vals.split()
584            self.rotVec = np.array([float(val) for val in vals])
585            rotvec.SetValue('%5.3f %5.3f %5.3f'%(self.rotVec[0],self.rotVec[1],self.rotVec[2]))
586            Q = G2mth.AVdeg2Q(self.rotAngle,self.rotVec)
587            self.Trans = G2mth.Q2Mat(Q)
588            self.Draw()
589           
590        self.panel.Destroy()
591        self.panel = wx.Panel(self)
592        Ind = {}
593        mainSizer = wx.BoxSizer(wx.VERTICAL)
594        MatSizer = wx.BoxSizer(wx.HORIZONTAL)
595        transSizer = wx.BoxSizer(wx.VERTICAL)
596        transSizer.Add(wx.StaticText(self.panel,label=" XYZ Transformation matrix && vector: "+ \
597            "\n B*M*A*(X-V)+V = X'\n A,B: Cartesian transformation matrices"))
598        Trmat = wx.FlexGridSizer(3,5,0,0)
599        for iy,line in enumerate(self.Trans):
600            for ix,val in enumerate(line):
601                item = wx.TextCtrl(self.panel,value='%5.3f'%(val),
602                    size=(50,25),style=wx.TE_PROCESS_ENTER)
603                Ind[item.GetId()] = [ix,iy]
604                item.Bind(wx.EVT_TEXT_ENTER,OnMatValue)
605                item.Bind(wx.EVT_KILL_FOCUS,OnMatValue)
606                Trmat.Add(item)
607            Trmat.Add((25,0),0)
608            vec = wx.TextCtrl(self.panel,value='%5.3f'%(self.Vec[iy]),
609                    size=(50,25),style=wx.TE_PROCESS_ENTER)
610            Ind[vec.GetId()] = [iy]       
611            vec.Bind(wx.EVT_TEXT_ENTER,OnVecValue)
612            vec.Bind(wx.EVT_KILL_FOCUS,OnVecValue)
613            Trmat.Add(vec)
614        transSizer.Add(Trmat)
615        MatSizer.Add((10,0),0)
616        MatSizer.Add(transSizer)
617        mainSizer.Add(MatSizer)
618        rotationBox = wx.BoxSizer(wx.HORIZONTAL)
619        rotationBox.Add(wx.StaticText(self.panel,label=' Rotation angle: '),0,WACV)
620        rotangle = wx.TextCtrl(self.panel,value='%5.3f'%(self.rotAngle),
621            size=(50,25),style=wx.TE_PROCESS_ENTER)
622        rotangle.Bind(wx.EVT_TEXT_ENTER,OnRotAngle)
623        rotangle.Bind(wx.EVT_KILL_FOCUS,OnRotAngle)
624        rotationBox.Add(rotangle,0,WACV)
625        rotationBox.Add(wx.StaticText(self.panel,label=' about vector: '),0,WACV)
626        rotvec = wx.TextCtrl(self.panel,value='%5.3f %5.3f %5.3f'%(self.rotVec[0],self.rotVec[1],self.rotVec[2]),
627            size=(100,25),style=wx.TE_PROCESS_ENTER)
628        rotvec.Bind(wx.EVT_TEXT_ENTER,OnRotVec)
629        rotvec.Bind(wx.EVT_KILL_FOCUS,OnRotVec)
630        rotationBox.Add(rotvec,0,WACV)
631        mainSizer.Add(rotationBox,0,WACV)
632        expandChoice = ['','xy','xz','yz','xyz']
633        expandBox = wx.BoxSizer(wx.HORIZONTAL)
634        expandBox.Add(wx.StaticText(self.panel,label=' Expand -1 to +1 on: '),0,WACV)
635        expand = wx.ComboBox(self.panel,value=self.Expand,choices=expandChoice,
636            style=wx.CB_READONLY|wx.CB_DROPDOWN)
637        expand.Bind(wx.EVT_COMBOBOX,OnExpand)
638        expandBox.Add(expand,0,WACV)
639        expandBox.Add(wx.StaticText(self.panel,label=' and find unique atoms '),0,WACV)       
640        mainSizer.Add(expandBox)
641               
642        OkBtn = wx.Button(self.panel,-1,"Ok")
643        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
644        cancelBtn = wx.Button(self.panel,-1,"Cancel")
645        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
646        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
647        btnSizer.Add((20,20),1)
648        btnSizer.Add(OkBtn)
649        btnSizer.Add((20,20),1)
650        btnSizer.Add(cancelBtn)
651        btnSizer.Add((20,20),1)
652       
653        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
654        self.panel.SetSizer(mainSizer)
655        self.panel.Fit()
656        self.Fit()
657
658    def GetSelection(self):
659        return self.Trans,self.Vec,self.Expand
660
661    def OnOk(self,event):
662        parent = self.GetParent()
663        parent.Raise()
664        self.EndModal(wx.ID_OK)
665
666    def OnCancel(self,event):
667        parent = self.GetParent()
668        parent.Raise()
669        self.EndModal(wx.ID_CANCEL)   
670       
671################################################################################
672class DIFFaXcontrols(wx.Dialog):
673    ''' Solicit items needed to prepare DIFFaX control.dif file
674    '''
675    def __init__(self,parent,ctrls,parms=None):
676        wx.Dialog.__init__(self,parent,wx.ID_ANY,'DIFFaX controls', 
677            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
678        self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
679        self.ctrls = ctrls
680        self.calcType = 'powder pattern'
681        self.plane = 'h0l'
682        self.planeChoice = ['h0l','0kl','hhl','h-hl',]
683        self.lmax = '2'
684        self.lmaxChoice = [str(i+1) for i in range(6)]
685        self.Parms = parms
686        self.Parm = None
687        if self.Parms != None:
688            self.Parm = self.Parms[0]
689        self.parmRange = [0.,1.]
690        self.parmStep = 2
691        self.Inst = 'Gaussian'
692        self.Draw()
693       
694    def Draw(self):
695       
696        def OnCalcType(event):
697            self.calcType = calcType.GetValue()
698            wx.CallAfter(self.Draw)
699           
700        def OnPlane(event):
701            self.plane = plane.GetValue()
702           
703        def OnMaxL(event):
704            self.lmax = lmax.GetValue()
705           
706        def OnParmSel(event):
707            self.Parm = parmsel.GetValue()
708           
709        def OnNumStep(event):
710            self.parmStep = int(numStep.GetValue())
711           
712        def OnParmRange(event):
713            vals = parmrange.GetValue().split()
714            try:
715                vals = [float(vals[0]),float(vals[1])]
716            except ValueError:
717                vals = self.parmRange
718            parmrange.SetValue('%.3f %.3f'%(vals[0],vals[1]))
719            self.parmRange = vals
720           
721        def OnInstSel(event):
722            self.Inst = instsel.GetValue()
723       
724        self.panel.Destroy()
725        self.panel = wx.Panel(self)
726        mainSizer = wx.BoxSizer(wx.VERTICAL)
727        mainSizer.Add(wx.StaticText(self.panel,label=' Controls for DIFFaX'),0,WACV)
728        if self.Parms:
729            mainSizer.Add(wx.StaticText(self.panel,label=' Sequential powder pattern simulation'),0,WACV)
730        else:
731            calcChoice = ['powder pattern','selected area']
732            calcSizer = wx.BoxSizer(wx.HORIZONTAL)
733            calcSizer.Add(wx.StaticText(self.panel,label=' Select calculation type: '),0,WACV)
734            calcType = wx.ComboBox(self.panel,value=self.calcType,choices=calcChoice,
735                style=wx.CB_READONLY|wx.CB_DROPDOWN)
736            calcType.Bind(wx.EVT_COMBOBOX,OnCalcType)
737            calcSizer.Add(calcType,0,WACV)
738            mainSizer.Add(calcSizer)
739        if self.Parms:
740            parmSel = wx.BoxSizer(wx.HORIZONTAL)
741            parmSel.Add(wx.StaticText(self.panel,label=' Select parameter to vary: '),0,WACV)
742            parmsel = wx.ComboBox(self.panel,value=self.Parm,choices=self.Parms,
743                style=wx.CB_READONLY|wx.CB_DROPDOWN)
744            parmsel.Bind(wx.EVT_COMBOBOX,OnParmSel)
745            parmSel.Add(parmsel,0,WACV)
746            mainSizer.Add(parmSel)
747            mainSizer.Add(wx.StaticText(self.panel,label=' Enter parameter range & no. steps: '),0,WACV)
748            parmRange =  wx.BoxSizer(wx.HORIZONTAL)
749            numChoice = [str(i+1) for i in range(10)]
750            parmrange = wx.TextCtrl(self.panel,value='%.3f %.3f'%(self.parmRange[0],self.parmRange[1]),
751                style=wx.TE_PROCESS_ENTER)
752            parmrange.Bind(wx.EVT_TEXT_ENTER,OnParmRange)
753            parmrange.Bind(wx.EVT_KILL_FOCUS,OnParmRange)
754            parmRange.Add(parmrange,0,WACV)
755            numStep = wx.ComboBox(self.panel,value=str(self.parmStep),choices=numChoice,
756                style=wx.CB_READONLY|wx.CB_DROPDOWN)
757            numStep.Bind(wx.EVT_COMBOBOX,OnNumStep)
758            parmRange.Add(numStep,0,WACV)
759            mainSizer.Add(parmRange)           
760        if 'selected' in self.calcType:
761            planeSizer = wx.BoxSizer(wx.HORIZONTAL)
762            planeSizer.Add(wx.StaticText(self.panel,label=' Select plane: '),0,WACV)
763            plane = wx.ComboBox(self.panel,value=self.plane,choices=self.planeChoice,
764                style=wx.CB_READONLY|wx.CB_DROPDOWN)
765            plane.Bind(wx.EVT_COMBOBOX,OnPlane)
766            planeSizer.Add(plane,0,WACV)
767            planeSizer.Add(wx.StaticText(self.panel,label=' Max. l index: '),0,WACV)
768            lmax = wx.ComboBox(self.panel,value=self.lmax,choices=self.lmaxChoice,
769                style=wx.CB_READONLY|wx.CB_DROPDOWN)
770            lmax.Bind(wx.EVT_COMBOBOX,OnMaxL)
771            planeSizer.Add(lmax,0,WACV)           
772            mainSizer.Add(planeSizer)
773        else:
774            instChoice = ['None','Mean Gaussian','Gaussian',]
775            instSizer = wx.BoxSizer(wx.HORIZONTAL)
776            instSizer.Add(wx.StaticText(self.panel,label=' Select instrument broadening: '),0,WACV)
777            instsel = wx.ComboBox(self.panel,value=self.Inst,choices=instChoice,
778                style=wx.CB_READONLY|wx.CB_DROPDOWN)
779            instsel.Bind(wx.EVT_COMBOBOX,OnInstSel)
780            instSizer.Add(instsel,0,WACV)
781            mainSizer.Add(instSizer)
782        OkBtn = wx.Button(self.panel,-1,"Ok")
783        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
784        cancelBtn = wx.Button(self.panel,-1,"Cancel")
785        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
786        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
787        btnSizer.Add((20,20),1)
788        btnSizer.Add(OkBtn)
789        btnSizer.Add((20,20),1)
790        btnSizer.Add(cancelBtn)
791        btnSizer.Add((20,20),1)
792       
793        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
794        self.panel.SetSizer(mainSizer)
795        self.panel.Fit()
796        self.Fit()
797       
798    def GetSelection(self):
799        if 'powder' in self.calcType:
800            return 'PWDR',self.Inst,self.Parm,self.parmRange,self.parmStep
801        elif 'selected' in self.calcType:
802            return 'SADP',self.plane,self.lmax
803
804    def OnOk(self,event):
805        parent = self.GetParent()
806        parent.Raise()
807        self.EndModal(wx.ID_OK)
808
809    def OnCancel(self,event):
810        parent = self.GetParent()
811        parent.Raise()
812        self.EndModal(wx.ID_CANCEL)
813           
814       
815################################################################################
816class MergeDialog(wx.Dialog):
817    ''' HKL transformation & merge dialog
818   
819    :param wx.Frame parent: reference to parent frame (or None)
820    :param data: HKLF data
821   
822    #NB: commonNames & commonTrans defined at top of this file     
823    '''       
824    def __init__(self,parent,data):
825        wx.Dialog.__init__(self,parent,wx.ID_ANY,'Setup HKLF merge', 
826            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
827        self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
828        self.data = data
829        self.Super = data[1]['Super']
830        if self.Super:
831            self.Trans = np.eye(4)
832        else:
833            self.Trans = np.eye(3)
834        self.Cent = 'noncentrosymmetric'
835        self.Laue = '1'
836        self.Class = 'triclinic'
837        self.Common = 'abc'
838        self.Draw()
839       
840    def Draw(self):
841               
842        def OnMatValue(event):
843            Obj = event.GetEventObject()
844            ix,iy = Ind[Obj.GetId()]
845            self.Trans[ix,iy] = float(Obj.GetValue())
846               
847        def OnCent(event):
848            Obj = event.GetEventObject()
849            self.Cent = Obj.GetValue()
850            self.Laue = ''
851            wx.CallAfter(self.Draw)
852           
853        def OnLaue(event):
854            Obj = event.GetEventObject()
855            self.Laue = Obj.GetValue()
856            wx.CallAfter(self.Draw)
857           
858        def OnClass(event):
859            Obj = event.GetEventObject()
860            self.Class = Obj.GetValue()
861            self.Laue = ''
862            wx.CallAfter(self.Draw)
863           
864        def OnCommon(event):
865            Obj = event.GetEventObject()
866            self.Common = Obj.GetValue()
867            self.Trans = commonTrans[self.Common]
868            wx.CallAfter(self.Draw)
869       
870        self.panel.Destroy()
871        self.panel = wx.Panel(self)
872        Ind = {}
873        mainSizer = wx.BoxSizer(wx.VERTICAL)
874        MatSizer = wx.BoxSizer(wx.HORIZONTAL)
875        transSizer = wx.BoxSizer(wx.VERTICAL)
876        transSizer.Add(wx.StaticText(self.panel,label=" HKL Transformation matrix: M*H = H'"))
877        if self.Super:
878            Trmat = wx.FlexGridSizer(4,4,0,0)
879        else:
880            commonSizer = wx.BoxSizer(wx.HORIZONTAL)
881            commonSizer.Add(wx.StaticText(self.panel,label=' Common transformations: '),0,WACV)
882            common = wx.ComboBox(self.panel,value=self.Common,choices=commonNames[:-1], #not the last one!
883                style=wx.CB_READONLY|wx.CB_DROPDOWN)
884            common.Bind(wx.EVT_COMBOBOX,OnCommon)
885            commonSizer.Add(common,0,WACV)
886            transSizer.Add(commonSizer)
887            Trmat = wx.FlexGridSizer(3,3,0,0)
888        for iy,line in enumerate(self.Trans):
889            for ix,val in enumerate(line):
890                item = wx.TextCtrl(self.panel,value='%5.3f'%(val),
891                    size=(50,25),style=wx.TE_PROCESS_ENTER)
892                Ind[item.GetId()] = [ix,iy]
893                item.Bind(wx.EVT_TEXT_ENTER,OnMatValue)
894                item.Bind(wx.EVT_KILL_FOCUS,OnMatValue)
895                Trmat.Add(item)
896        transSizer.Add(Trmat)
897        MatSizer.Add((10,0),0)
898        MatSizer.Add(transSizer)
899        mainSizer.Add(MatSizer)
900        laueClass = ['triclinic','monoclinic','orthorhombic','trigonal(H)','tetragonal','hexagonal','cubic']
901        centroLaue = {'triclinic':['-1',],'monoclinic':['2/m','1 1 2/m','2/m 1 1',],
902            'orthorhombic':['m m m',],'trigonal(H)':['-3','-3 m 1','-3 1 m',],    \
903            'tetragonal':['4/m','4/m m m',],'hexagonal':['6/m','6/m m m',],'cubic':['m 3','m 3 m']}
904        noncentroLaue = {'triclinic':['1',],'monoclinic':['2','2 1 1','1 1 2','m','m 1 1','1 1 m',],
905            'orthorhombic':['2 2 2','m m 2','m 2 m','2 m m',],
906            'trigonal(H)':['3','3 1 2','3 2 1','3 m 1','3 1 m',],
907            'tetragonal':['4','-4','4 2 2','4 m m','-4 2 m','-4 m 2',], \
908            'hexagonal':['6','-6','6 2 2','6 m m','-6 m 2','-6 2 m',],'cubic':['2 3','4 3 2','-4 3 m']}
909        centChoice = ['noncentrosymmetric','centrosymmetric']
910        mainSizer.Add(wx.StaticText(self.panel,label=' Select Laue class for new lattice:'),0,WACV)
911        Class = wx.ComboBox(self.panel,value=self.Class,choices=laueClass,
912            style=wx.CB_READONLY|wx.CB_DROPDOWN)
913        Class.Bind(wx.EVT_COMBOBOX,OnClass)
914        mainSizer.Add(Class,0,WACV)
915        mainSizer.Add(wx.StaticText(self.panel,label=' Target Laue symmetry:'),0,WACV)
916        Cent = wx.ComboBox(self.panel,value=self.Cent,choices=centChoice,
917            style=wx.CB_READONLY|wx.CB_DROPDOWN)
918        Cent.Bind(wx.EVT_COMBOBOX,OnCent)
919        mergeSizer = wx.BoxSizer(wx.HORIZONTAL)
920        mergeSizer.Add(Cent,0,WACV)
921        mergeSizer.Add((10,0),0)
922        Choice = centroLaue[self.Class]
923        if 'non' in self.Cent:
924            Choice = noncentroLaue[self.Class]
925        Laue = wx.ComboBox(self.panel,value=self.Laue,choices=Choice,
926            style=wx.CB_READONLY|wx.CB_DROPDOWN)
927        Laue.Bind(wx.EVT_COMBOBOX,OnLaue)
928        mergeSizer.Add(Laue,0,WACV)
929        mainSizer.Add(mergeSizer)
930
931        OkBtn = wx.Button(self.panel,-1,"Ok")
932        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
933        cancelBtn = wx.Button(self.panel,-1,"Cancel")
934        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
935        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
936        btnSizer.Add((20,20),1)
937        if self.Laue:
938            btnSizer.Add(OkBtn)
939            btnSizer.Add((20,20),1)
940        btnSizer.Add(cancelBtn)
941        btnSizer.Add((20,20),1)
942       
943        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
944        self.panel.SetSizer(mainSizer)
945        self.panel.Fit()
946        self.Fit()
947       
948    def GetSelection(self):
949        return self.Trans,self.Cent,self.Laue
950
951    def OnOk(self,event):
952        parent = self.GetParent()
953        parent.Raise()
954        self.EndModal(wx.ID_OK)
955
956    def OnCancel(self,event):
957        parent = self.GetParent()
958        parent.Raise()
959        self.EndModal(wx.ID_CANCEL)
960
961       
962################################################################################
963class AddHatomDialog(wx.Dialog):
964    '''H atom addition dialog. After :meth:`ShowModal` returns, the results
965    are found in dict :attr:`self.data`, which is accessed using :meth:`GetData`.
966   
967    :param wx.Frame parent: reference to parent frame (or None)
968    :param dict Neigh: a dict of atom names with list of atom name, dist pairs for neighboring atoms
969    :param dict phase: a dict containing the phase as defined by
970      :ref:`Phase Tree Item <Phase_table>`   
971    '''
972    def __init__(self,parent,Neigh,phase):
973        wx.Dialog.__init__(self,parent,wx.ID_ANY,'H atom add', 
974            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
975        self.panel = wxscroll.ScrolledPanel(self)         #just a dummy - gets destroyed in Draw!
976        self.Neigh = Neigh
977        self.phase = phase
978        self.Hatoms = []
979        self.Draw(self.Neigh,self.phase)
980           
981    def Draw(self,Neigh,phase):
982        '''Creates the contents of the dialog. Normally called
983        by :meth:`__init__`.
984        '''
985        def OnHSelect(event):
986            Obj = event.GetEventObject()
987            item,i = Indx[Obj.GetId()]
988            for obj in Indx[item]:
989                obj.SetValue(False)
990            Obj.SetValue(True)
991            self.Neigh[item][2] = i
992           
993        def OnBond(event):
994            Obj = event.GetEventObject()
995            inei,ibond = Indx[Obj.GetId()]
996            self.Neigh[inei][1][0][ibond][2] = Obj.GetValue()
997           
998        self.panel.Destroy()
999        self.panel = wxscroll.ScrolledPanel(self,style = wx.DEFAULT_DIALOG_STYLE)
1000        mainSizer = wx.BoxSizer(wx.VERTICAL)
1001        mainSizer.Add(wx.StaticText(self.panel,-1,'H atom add controls for phase %s:'%(phase['General']['Name'])),
1002            0,wx.LEFT|wx.TOP,10)
1003        mainSizer.Add(wx.StaticText(self.panel,-1,'NB: Check selections as they may not be correct'),0,WACV|wx.LEFT,10)
1004        mainSizer.Add(wx.StaticText(self.panel,-1," Atom:  Add # H's          Use: Neighbors, dist"),0,wx.TOP|wx.LEFT,5)
1005        nHatms = ['0','1','2','3']
1006        dataSizer = wx.FlexGridSizer(0,3,0,0)
1007        Indx = {}
1008        for inei,neigh in enumerate(Neigh):
1009            dataSizer.Add(wx.StaticText(self.panel,-1,' %s:  '%(neigh[0])),0,WACV)
1010            nH = 1      #for O atom
1011            if 'C' in neigh[0] or 'N' in neigh[0]:
1012                nH = 4-len(neigh[1][0])
1013            checks = wx.BoxSizer(wx.HORIZONTAL)
1014            Ids = []
1015            for i in range(nH+1):
1016                nHs = wx.CheckBox(self.panel,-1,label=nHatms[i])
1017                if i == neigh[2]:
1018                    nHs.SetValue(True)
1019                Indx[nHs.GetId()] = [inei,i]
1020                Ids.append(nHs)
1021                nHs.Bind(wx.EVT_CHECKBOX, OnHSelect)
1022                checks.Add(nHs,0,WACV)
1023            Indx[inei] = Ids
1024            dataSizer.Add(checks,0,WACV)
1025            lineSizer = wx.BoxSizer(wx.HORIZONTAL)
1026            for ib,bond in enumerate(neigh[1][0]):
1027                Bond = wx.CheckBox(self.panel,-1,label=': %s, %.3f'%(bond[0],bond[1]))
1028                Bond.SetValue(bond[2])
1029                Indx[Bond.GetId()] = [inei,ib]
1030                Bond.Bind(wx.EVT_CHECKBOX,OnBond)               
1031                lineSizer.Add(Bond,0,WACV)               
1032            dataSizer.Add(lineSizer,0,WACV|wx.RIGHT,10)
1033        mainSizer.Add(dataSizer,0,wx.LEFT,5)
1034
1035        CancelBtn = wx.Button(self.panel,-1,'Cancel')
1036        CancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1037        OkBtn = wx.Button(self.panel,-1,'Ok')
1038        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1039        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1040        btnSizer.Add((20,20),1)
1041        btnSizer.Add(OkBtn)
1042        btnSizer.Add((20,20),1)
1043        btnSizer.Add(CancelBtn)
1044        btnSizer.Add((20,20),1)
1045        mainSizer.Add(btnSizer,0,wx.BOTTOM|wx.TOP, 10)
1046        size = np.array(self.GetSize())
1047        self.panel.SetupScrolling()
1048        self.panel.SetSizer(mainSizer)
1049        self.panel.SetAutoLayout(1)
1050        size = [size[0]-5,size[1]-20]       #this fiddling is needed for older wx!
1051        self.panel.SetSize(size)
1052       
1053    def GetData(self):
1054        'Returns the values from the dialog'
1055        for neigh in self.Neigh:
1056            for ibond,bond in enumerate(neigh[1][0]):
1057                if not bond[2]:
1058                    neigh[1][1][1][ibond] = 0   #deselected bond
1059            neigh[1][1][1] = [a for a in  neigh[1][1][1] if a]
1060        return self.Neigh       #has #Hs to add for each entry
1061       
1062    def OnOk(self,event):
1063        'Called when the OK button is pressed'
1064        parent = self.GetParent()
1065        parent.Raise()
1066        self.EndModal(wx.ID_OK)             
1067
1068    def OnCancel(self,event):
1069        parent = self.GetParent()
1070        parent.Raise()
1071        self.EndModal(wx.ID_CANCEL)
1072
1073################################################################################
1074class DisAglDialog(wx.Dialog):
1075    '''Distance/Angle Controls input dialog. After
1076    :meth:`ShowModal` returns, the results are found in
1077    dict :attr:`self.data`, which is accessed using :meth:`GetData`.
1078
1079    :param wx.Frame parent: reference to parent frame (or None)
1080    :param dict data: a dict containing the current
1081      search ranges or an empty dict, which causes default values
1082      to be used.
1083      Will be used to set element `DisAglCtls` in
1084      :ref:`Phase Tree Item <Phase_table>`
1085    :param dict default:  A dict containing the default
1086      search ranges for each element.
1087    '''
1088    def __init__(self,parent,data,default,Reset=True):
1089        wx.Dialog.__init__(self,parent,wx.ID_ANY,
1090                           'Distance Angle Controls', 
1091            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1092        self.default = default
1093        self.Reset = Reset
1094        self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
1095        self._default(data,self.default)
1096        self.Draw(self.data)
1097               
1098    def _default(self,data,default):
1099        '''Set starting values for the search values, either from
1100        the input array or from defaults, if input is null
1101        '''
1102        if data:
1103            self.data = copy.deepcopy(data) # don't mess with originals
1104        else:
1105            self.data = {}
1106            self.data['Name'] = default['Name']
1107            self.data['Factors'] = [0.85,0.85]
1108            self.data['AtomTypes'] = default['AtomTypes']
1109            self.data['BondRadii'] = default['BondRadii'][:]
1110            self.data['AngleRadii'] = default['AngleRadii'][:]
1111
1112    def Draw(self,data):
1113        '''Creates the contents of the dialog. Normally called
1114        by :meth:`__init__`.
1115        '''
1116        self.panel.Destroy()
1117        self.panel = wx.Panel(self)
1118        mainSizer = wx.BoxSizer(wx.VERTICAL)
1119        mainSizer.Add(wx.StaticText(self.panel,-1,'Controls for phase '+data['Name']),
1120            0,WACV|wx.LEFT,10)
1121        mainSizer.Add((10,10),1)
1122       
1123        radiiSizer = wx.FlexGridSizer(0,3,5,5)
1124        radiiSizer.Add(wx.StaticText(self.panel,-1,' Type'),0,WACV)
1125        radiiSizer.Add(wx.StaticText(self.panel,-1,'Bond radii'),0,WACV)
1126        radiiSizer.Add(wx.StaticText(self.panel,-1,'Angle radii'),0,WACV)
1127        self.objList = {}
1128        for id,item in enumerate(self.data['AtomTypes']):
1129            radiiSizer.Add(wx.StaticText(self.panel,-1,' '+item),0,WACV)
1130            bRadii = wx.TextCtrl(self.panel,-1,value='%.3f'%(data['BondRadii'][id]),style=wx.TE_PROCESS_ENTER)
1131            self.objList[bRadii.GetId()] = ['BondRadii',id]
1132            bRadii.Bind(wx.EVT_TEXT_ENTER,self.OnRadiiVal)
1133            bRadii.Bind(wx.EVT_KILL_FOCUS,self.OnRadiiVal)
1134            radiiSizer.Add(bRadii,0,WACV)
1135            aRadii = wx.TextCtrl(self.panel,-1,value='%.3f'%(data['AngleRadii'][id]),style=wx.TE_PROCESS_ENTER)
1136            self.objList[aRadii.GetId()] = ['AngleRadii',id]
1137            aRadii.Bind(wx.EVT_TEXT_ENTER,self.OnRadiiVal)
1138            aRadii.Bind(wx.EVT_KILL_FOCUS,self.OnRadiiVal)
1139            radiiSizer.Add(aRadii,0,WACV)
1140        mainSizer.Add(radiiSizer,0,wx.EXPAND)
1141        factorSizer = wx.FlexGridSizer(0,2,5,5)
1142        Names = ['Bond','Angle']
1143        for i,name in enumerate(Names):
1144            factorSizer.Add(wx.StaticText(self.panel,-1,name+' search factor'),0,WACV)
1145            bondFact = wx.TextCtrl(self.panel,-1,value='%.3f'%(data['Factors'][i]),style=wx.TE_PROCESS_ENTER)
1146            self.objList[bondFact.GetId()] = ['Factors',i]
1147            bondFact.Bind(wx.EVT_TEXT_ENTER,self.OnRadiiVal)
1148            bondFact.Bind(wx.EVT_KILL_FOCUS,self.OnRadiiVal)
1149            factorSizer.Add(bondFact)
1150        mainSizer.Add(factorSizer,0,wx.EXPAND)
1151       
1152        OkBtn = wx.Button(self.panel,-1,"Ok")
1153        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1154        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1155        btnSizer.Add((20,20),1)
1156        btnSizer.Add(OkBtn)
1157        if self.Reset:
1158            ResetBtn = wx.Button(self.panel,-1,'Reset')
1159            ResetBtn.Bind(wx.EVT_BUTTON, self.OnReset)
1160            btnSizer.Add(ResetBtn)
1161        btnSizer.Add((20,20),1)
1162        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1163        self.panel.SetSizer(mainSizer)
1164        self.panel.Fit()
1165        self.Fit()
1166   
1167    def OnRadiiVal(self,event):
1168        Obj = event.GetEventObject()
1169        item = self.objList[Obj.GetId()]
1170        try:
1171            self.data[item[0]][item[1]] = float(Obj.GetValue())
1172        except ValueError:
1173            pass
1174        Obj.SetValue("%.3f"%(self.data[item[0]][item[1]]))          #reset in case of error
1175       
1176    def GetData(self):
1177        'Returns the values from the dialog'
1178        return self.data
1179       
1180    def OnOk(self,event):
1181        'Called when the OK button is pressed'
1182        parent = self.GetParent()
1183        parent.Raise()
1184        self.EndModal(wx.ID_OK)             
1185       
1186    def OnReset(self,event):
1187        'Called when the Reset button is pressed'
1188        data = {}
1189        self._default(data,self.default)
1190        self.Draw(self.data)
1191               
1192################################################################################
1193class ShowLSParms(wx.Dialog):
1194    '''Create frame to show least-squares parameters
1195    '''
1196    def __init__(self,parent,title,parmDict,varyList,fullVaryList,
1197                 size=(300,430)):
1198        wx.Dialog.__init__(self,parent,wx.ID_ANY,title,size=size,
1199                           style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1200        mainSizer = wx.BoxSizer(wx.VERTICAL)
1201
1202        panel = wxscroll.ScrolledPanel(
1203            self, wx.ID_ANY,
1204            #size=size,
1205            style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
1206        num = len(varyList)
1207        mainSizer.Add(wx.StaticText(self,wx.ID_ANY,'Number of refined variables: '+str(num)))
1208        if len(varyList) != len(fullVaryList):
1209            num = len(fullVaryList) - len(varyList)
1210            mainSizer.Add(wx.StaticText(self,wx.ID_ANY,' + '+str(num)+' parameters are varied via constraints'))
1211        subSizer = wx.FlexGridSizer(cols=4,hgap=2,vgap=2)
1212        parmNames = parmDict.keys()
1213        parmNames.sort()
1214        subSizer.Add((-1,-1))
1215        subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'Parameter name  '))
1216        subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'refine?'))
1217        subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'value'),0,wx.ALIGN_RIGHT)
1218        explainRefine = False
1219        for name in parmNames:
1220            # skip entries without numerical values
1221            if isinstance(parmDict[name],basestring): continue
1222            try:
1223                value = G2py3.FormatSigFigs(parmDict[name])
1224            except TypeError:
1225                value = str(parmDict[name])+' -?' # unexpected
1226                #continue
1227            v = G2obj.getVarDescr(name)
1228            if v is None or v[-1] is None:
1229                subSizer.Add((-1,-1))
1230            else:               
1231                ch = G2G.HelpButton(panel,G2obj.fmtVarDescr(name))
1232                subSizer.Add(ch,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1)
1233            subSizer.Add(wx.StaticText(panel,wx.ID_ANY,str(name)))
1234            if name in varyList:
1235                subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'R'))
1236            elif name in fullVaryList:
1237                subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'C'))
1238                explainRefine = True
1239            else:
1240                subSizer.Add((-1,-1))
1241            subSizer.Add(wx.StaticText(panel,wx.ID_ANY,value),0,wx.ALIGN_RIGHT)
1242
1243        # finish up ScrolledPanel
1244        panel.SetSizer(subSizer)
1245        panel.SetAutoLayout(1)
1246        panel.SetupScrolling()
1247        mainSizer.Add(panel,1, wx.ALL|wx.EXPAND,1)
1248
1249        if explainRefine:
1250            mainSizer.Add(
1251                wx.StaticText(self,wx.ID_ANY,
1252                          '"R" indicates a refined variable\n'+
1253                          '"C" indicates generated from a constraint'
1254                          ),
1255                0, wx.ALL,0)
1256        # make OK button
1257        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
1258        btn = wx.Button(self, wx.ID_CLOSE,"Close") 
1259        btn.Bind(wx.EVT_BUTTON,self._onClose)
1260        btnsizer.Add(btn)
1261        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)
1262        # Allow window to be enlarged but not made smaller
1263        self.SetSizer(mainSizer)
1264        self.SetMinSize(self.GetSize())
1265
1266    def _onClose(self,event):
1267        self.EndModal(wx.ID_CANCEL)
1268 
1269################################################################################
1270class DataFrame(wx.Frame):
1271    '''Create the data item window and all the entries in menus used in
1272    that window. For Linux and windows, the menu entries are created for the
1273    current data item window, but in the Mac the menu is accessed from all
1274    windows. This means that a different menu is posted depending on which
1275    data item is posted. On the Mac, all the menus contain the data tree menu
1276    items, but additional menus are added specific to the data item.
1277
1278    Note that while the menus are created here,
1279    the binding for the menus is done later in various GSASII*GUI modules,
1280    where the functions to be called are defined.
1281    '''
1282    def Bind(self,eventtype,handler,*args,**kwargs):
1283        '''Override the Bind() function: on the Mac the binding is to
1284        the main window, so that menus operate with any window on top.
1285        For other platforms, either wrap calls that will be logged
1286        or call the default wx.Frame Bind() to bind to the menu item directly.
1287
1288        Note that bindings can be made to objects by Id or by direct reference to the
1289        object. As a convention, when bindings are to objects, they are not logged
1290        but when bindings are by Id, they are logged.
1291        '''
1292        if sys.platform == "darwin": # mac
1293            self.G2frame.Bind(eventtype,handler,*args,**kwargs)
1294            return
1295        if eventtype == wx.EVT_MENU and 'id' in kwargs:
1296            menulabels = log.SaveMenuCommand(kwargs['id'],self.G2frame,handler)
1297            if menulabels:
1298                #print 'intercepting bind for',handler,menulabels,kwargs['id']
1299                wx.Frame.Bind(self,eventtype,self.G2frame.MenuBinding,*args,**kwargs)
1300                return
1301            wx.Frame.Bind(self,eventtype,handler,*args,**kwargs)     
1302       
1303    def PrefillDataMenu(self,menu,helpType,helpLbl=None,empty=False):
1304        '''Create the "standard" part of data frame menus. Note that on Linux and
1305        Windows nothing happens here. On Mac, this menu duplicates the
1306        tree menu, but adds an extra help command for the data item and a separator.
1307        '''
1308        self.datamenu = menu
1309        self.G2frame.dataMenuBars.append(menu)
1310        self.helpType = helpType
1311        self.helpLbl = helpLbl
1312        if sys.platform == "darwin": # mac                         
1313            self.G2frame.FillMainMenu(menu) # add the data tree menu items
1314            if not empty:
1315                menu.Append(wx.Menu(title=''),title='|') # add a separator
1316       
1317    def PostfillDataMenu(self,empty=False):
1318        '''Create the "standard" part of data frame menus. Note that on Linux and
1319        Windows, this is the standard help Menu. On Mac, this menu duplicates the
1320        tree menu, but adds an extra help command for the data item and a separator.
1321        '''
1322        menu = self.datamenu
1323        helpType = self.helpType
1324        helpLbl = self.helpLbl
1325        if sys.platform == "darwin": # mac
1326            if not empty:
1327                menu.Append(wx.Menu(title=''),title='|') # add another separator
1328            menu.Append(G2G.AddHelp(self.G2frame,helpType=helpType, helpLbl=helpLbl),
1329                        title='&Help')
1330        else: # other
1331            menu.Append(menu=G2G.MyHelp(self,helpType=helpType, helpLbl=helpLbl),
1332                        title='&Help')
1333
1334    def _init_menus(self):
1335        'define all GSAS-II data frame menus'
1336
1337        # for use where no menu or data frame help is provided
1338        self.BlankMenu = wx.MenuBar()
1339       
1340        # Controls
1341        self.ControlsMenu = wx.MenuBar()
1342        self.PrefillDataMenu(self.ControlsMenu,helpType='Controls',empty=True)
1343        self.PostfillDataMenu(empty=True)
1344       
1345        # Notebook
1346        self.DataNotebookMenu = wx.MenuBar() 
1347        self.PrefillDataMenu(self.DataNotebookMenu,helpType='Notebook',empty=True)
1348        self.PostfillDataMenu(empty=True)
1349       
1350        # Comments
1351        self.DataCommentsMenu = wx.MenuBar()
1352        self.PrefillDataMenu(self.DataCommentsMenu,helpType='Comments',empty=True)
1353        self.PostfillDataMenu(empty=True)
1354       
1355        # Constraints - something amiss here - get weird wx C++ error after refine!
1356        self.ConstraintMenu = wx.MenuBar()
1357        self.PrefillDataMenu(self.ConstraintMenu,helpType='Constraints')
1358        self.ConstraintTab = wx.Menu(title='')
1359        self.ConstraintMenu.Append(menu=self.ConstraintTab, title='Select tab')
1360        for id,txt in (
1361            (wxID_CONSPHASE,'Phase'),
1362            (wxID_CONSHAP,'Histogram/Phase'),
1363            (wxID_CONSHIST,'Histogram'),
1364            (wxID_CONSGLOBAL,'Global')):
1365            self.ConstraintTab.Append(
1366                id=id, kind=wx.ITEM_NORMAL,text=txt,
1367                help='Select '+txt+' constraint editing tab')
1368        self.ConstraintEdit = wx.Menu(title='')
1369        self.ConstraintMenu.Append(menu=self.ConstraintEdit, title='Edit')
1370        self.ConstraintEdit.Append(id=wxID_HOLDADD, kind=wx.ITEM_NORMAL,text='Add hold',
1371            help='Add hold on a parameter value')
1372        self.ConstraintEdit.Append(id=wxID_EQUIVADD, kind=wx.ITEM_NORMAL,text='Add equivalence',
1373            help='Add equivalence between parameter values')
1374        self.ConstraintEdit.Append(id=wxID_CONSTRAINTADD, kind=wx.ITEM_NORMAL,text='Add constraint',
1375            help='Add constraint on parameter values')
1376        self.ConstraintEdit.Append(id=wxID_FUNCTADD, kind=wx.ITEM_NORMAL,text='Add New Var',
1377            help='Add variable composed of existing parameter')
1378        self.ConstraintEdit.Append(id=wxID_EQUIVALANCEATOMS, kind=wx.ITEM_NORMAL,text='Add atom equivalence',
1379            help='Add equivalences between atom parameter values')
1380        self.ConstraintEdit.Enable(wxID_EQUIVALANCEATOMS,False)
1381#        self.ConstraintEdit.Append(id=wxID_ADDRIDING, kind=wx.ITEM_NORMAL,text='Add H riding constraints',
1382#            help='Add H atom riding constraints between atom parameter values')
1383#        self.ConstraintEdit.Enable(wxID_ADDRIDING,False)
1384        self.PostfillDataMenu()
1385
1386        # item = self.ConstraintEdit.Append(id=wx.ID_ANY,kind=wx.ITEM_NORMAL,text='Update GUI')
1387        # def UpdateGSASIIconstrGUI(event):
1388        #     import GSASIIconstrGUI
1389        #     reload(GSASIIconstrGUI)
1390        #     import GSASIIobj
1391        #     reload(GSASIIobj)
1392        # self.Bind(wx.EVT_MENU,UpdateGSASIIconstrGUI,id=item.GetId())
1393
1394        # Rigid bodies
1395        self.RigidBodyMenu = wx.MenuBar()
1396        self.PrefillDataMenu(self.RigidBodyMenu,helpType='Rigid bodies')
1397        self.ResidueRBMenu = wx.Menu(title='')
1398        self.ResidueRBMenu.Append(id=wxID_RIGIDBODYIMPORT, kind=wx.ITEM_NORMAL,text='Import XYZ',
1399            help='Import rigid body XYZ from file')
1400        self.ResidueRBMenu.Append(id=wxID_RESIDUETORSSEQ, kind=wx.ITEM_NORMAL,text='Define sequence',
1401            help='Define torsion sequence')
1402        self.ResidueRBMenu.Append(id=wxID_RIGIDBODYADD, kind=wx.ITEM_NORMAL,text='Import residues',
1403            help='Import residue rigid bodies from macro file')
1404        self.RigidBodyMenu.Append(menu=self.ResidueRBMenu, title='Edit Body')
1405        self.PostfillDataMenu()
1406
1407        self.VectorBodyMenu = wx.MenuBar()
1408        self.PrefillDataMenu(self.VectorBodyMenu,helpType='Vector rigid bodies')
1409        self.VectorRBEdit = wx.Menu(title='')
1410        self.VectorRBEdit.Append(id=wxID_VECTORBODYADD, kind=wx.ITEM_NORMAL,text='Add rigid body',
1411            help='Add vector rigid body')
1412        self.VectorBodyMenu.Append(menu=self.VectorRBEdit, title='Edit Vector Body')
1413        self.PostfillDataMenu()
1414
1415                   
1416        # Restraints
1417        self.RestraintTab = wx.Menu(title='')
1418        self.RestraintEdit = wx.Menu(title='')
1419        self.RestraintEdit.Append(id=wxID_RESTSELPHASE, kind=wx.ITEM_NORMAL,text='Select phase',
1420            help='Select phase')
1421        self.RestraintEdit.Append(id=wxID_RESTRAINTADD, kind=wx.ITEM_NORMAL,text='Add restraints',
1422            help='Add restraints')
1423        self.RestraintEdit.Enable(wxID_RESTRAINTADD,True)    #gets disabled if macromolecule phase
1424        self.RestraintEdit.Append(id=wxID_AARESTRAINTADD, kind=wx.ITEM_NORMAL,text='Add residue restraints',
1425            help='Add residue based restraints for macromolecules from macro file')
1426        self.RestraintEdit.Enable(wxID_AARESTRAINTADD,False)    #gets enabled if macromolecule phase
1427        self.RestraintEdit.Append(id=wxID_AARESTRAINTPLOT, kind=wx.ITEM_NORMAL,text='Plot residue restraints',
1428            help='Plot selected residue based restraints for macromolecules from macro file')
1429        self.RestraintEdit.Enable(wxID_AARESTRAINTPLOT,False)    #gets enabled if macromolecule phase
1430        self.RestraintEdit.Append(id=wxID_RESRCHANGEVAL, kind=wx.ITEM_NORMAL,text='Change value',
1431            help='Change observed value')
1432        self.RestraintEdit.Append(id=wxID_RESTCHANGEESD, kind=wx.ITEM_NORMAL,text='Change esd',
1433            help='Change esd in observed value')
1434        self.RestraintEdit.Append(id=wxID_RESTDELETE, kind=wx.ITEM_NORMAL,text='Delete restraints',
1435            help='Delete selected restraints')
1436
1437        self.RestraintMenu = wx.MenuBar()
1438        self.PrefillDataMenu(self.RestraintMenu,helpType='Restraints')
1439        self.RestraintMenu.Append(menu=self.RestraintTab, title='Select tab')
1440        self.RestraintMenu.Append(menu=self.RestraintEdit, title='Edit')
1441        self.PostfillDataMenu()
1442           
1443        # Sequential results
1444        self.SequentialMenu = wx.MenuBar()
1445        self.PrefillDataMenu(self.SequentialMenu,helpType='Sequential',helpLbl='Sequential Refinement')
1446        self.SequentialFile = wx.Menu(title='')
1447        self.SequentialMenu.Append(menu=self.SequentialFile, title='Columns')
1448        self.SequentialFile.Append(id=wxID_RENAMESEQSEL, kind=wx.ITEM_NORMAL,text='Rename selected',
1449            help='Rename selected sequential refinement columns')
1450        self.SequentialFile.Append(id=wxID_SAVESEQSEL, kind=wx.ITEM_NORMAL,text='Save selected as text',
1451            help='Save selected sequential refinement results as a text file')
1452        self.SequentialFile.Append(id=wxID_SAVESEQCSV, kind=wx.ITEM_NORMAL,text='Save all as CSV',
1453            help='Save all sequential refinement results as a CSV spreadsheet file')
1454        self.SequentialFile.Append(id=wxID_SAVESEQSELCSV, kind=wx.ITEM_NORMAL,text='Save selected as CSV',
1455            help='Save selected sequential refinement results as a CSV spreadsheet file')
1456        self.SequentialFile.Append(id=wxID_PLOTSEQSEL, kind=wx.ITEM_NORMAL,text='Plot selected',
1457            help='Plot selected sequential refinement results')
1458        self.SequentialFile.Append(id=wxID_AVESEQSEL, kind=wx.ITEM_NORMAL,text='Compute average',
1459            help='Compute average for selected parameter')           
1460        self.SequentialFile.Append(id=wxID_ORGSEQSEL, kind=wx.ITEM_NORMAL,text='Reorganize',
1461            help='Reorganize variables where variables change')
1462        self.SequentialPvars = wx.Menu(title='')
1463        self.SequentialMenu.Append(menu=self.SequentialPvars, title='Pseudo Vars')
1464        self.SequentialPvars.Append(
1465            id=wxADDSEQVAR, kind=wx.ITEM_NORMAL,text='Add Formula',
1466            help='Add a new custom pseudo-variable')
1467        self.SequentialPvars.Append(
1468            id=wxADDSEQDIST, kind=wx.ITEM_NORMAL,text='Add Distance',
1469            help='Add a new bond distance pseudo-variable')
1470        self.SequentialPvars.Append(
1471            id=wxADDSEQANGLE, kind=wx.ITEM_NORMAL,text='Add Angle',
1472            help='Add a new bond angle pseudo-variable')
1473        self.SequentialPvars.Append(
1474            id=wxDELSEQVAR, kind=wx.ITEM_NORMAL,text='Delete',
1475            help='Delete an existing pseudo-variable')
1476        self.SequentialPvars.Append(
1477            id=wxEDITSEQVAR, kind=wx.ITEM_NORMAL,text='Edit',
1478            help='Edit an existing pseudo-variable')
1479
1480        self.SequentialPfit = wx.Menu(title='')
1481        self.SequentialMenu.Append(menu=self.SequentialPfit, title='Parametric Fit')
1482        self.SequentialPfit.Append(
1483            id=wxADDPARFIT, kind=wx.ITEM_NORMAL,text='Add equation',
1484            help='Add a new equation to minimize')
1485        self.SequentialPfit.Append(
1486            id=wxCOPYPARFIT, kind=wx.ITEM_NORMAL,text='Copy equation',
1487            help='Copy an equation to minimize - edit it next')
1488        self.SequentialPfit.Append(
1489            id=wxDELPARFIT, kind=wx.ITEM_NORMAL,text='Delete equation',
1490            help='Delete an equation for parametric minimization')
1491        self.SequentialPfit.Append(
1492            id=wxEDITPARFIT, kind=wx.ITEM_NORMAL,text='Edit equation',
1493            help='Edit an existing parametric minimization equation')
1494        self.SequentialPfit.Append(
1495            id=wxDOPARFIT, kind=wx.ITEM_NORMAL,text='Fit to equation(s)',
1496            help='Perform a parametric minimization')
1497        self.PostfillDataMenu()
1498           
1499        # PWDR & SASD
1500        self.PWDRMenu = wx.MenuBar()
1501        self.PrefillDataMenu(self.PWDRMenu,helpType='PWDR Analysis',helpLbl='Powder Fit Error Analysis')
1502        self.ErrorAnal = wx.Menu(title='')
1503        self.PWDRMenu.Append(menu=self.ErrorAnal,title='Commands')
1504        self.ErrorAnal.Append(id=wxID_PWDANALYSIS,kind=wx.ITEM_NORMAL,text='Error Analysis',
1505            help='Error analysis on powder pattern')
1506        self.ErrorAnal.Append(id=wxID_PWDCOPY,kind=wx.ITEM_NORMAL,text='Copy params',
1507            help='Copy of PWDR parameters')
1508        self.ErrorAnal.Append(id=wxID_PLOTCTRLCOPY,kind=wx.ITEM_NORMAL,text='Copy plot controls',
1509            help='Copy of PWDR plot controls')
1510           
1511        self.PostfillDataMenu()
1512           
1513        # HKLF
1514        self.HKLFMenu = wx.MenuBar()
1515        self.PrefillDataMenu(self.HKLFMenu,helpType='HKLF Analysis',helpLbl='HKLF Fit Error Analysis')
1516        self.ErrorAnal = wx.Menu(title='')
1517        self.HKLFMenu.Append(menu=self.ErrorAnal,title='Commands')
1518        self.ErrorAnal.Append(id=wxID_PWDANALYSIS,kind=wx.ITEM_NORMAL,text='Error Analysis',
1519            help='Error analysis on single crystal data')
1520        self.ErrorAnal.Append(id=wxID_MERGEHKL,kind=wx.ITEM_NORMAL,text='Merge HKLs',
1521            help='Transform & merge HKLF data to new histogram')
1522        self.ErrorAnal.Append(id=wxID_PWD3DHKLPLOT,kind=wx.ITEM_NORMAL,text='Plot 3D HKLs',
1523            help='Plot HKLs from single crystal data in 3D')
1524        self.ErrorAnal.Append(id=wxID_3DALLHKLPLOT,kind=wx.ITEM_NORMAL,text='Plot all 3D HKLs',
1525            help='Plot HKLs from all single crystal data in 3D')
1526        self.ErrorAnal.Append(id=wxID_PWDCOPY,kind=wx.ITEM_NORMAL,text='Copy params',
1527            help='Copy of HKLF parameters')
1528        self.PostfillDataMenu()
1529           
1530        # PDR / Limits
1531        self.LimitMenu = wx.MenuBar()
1532        self.PrefillDataMenu(self.LimitMenu,helpType='Limits')
1533        self.LimitEdit = wx.Menu(title='')
1534        self.LimitMenu.Append(menu=self.LimitEdit, title='Edit')
1535        self.LimitEdit.Append(id=wxID_LIMITCOPY, kind=wx.ITEM_NORMAL,text='Copy',
1536            help='Copy limits to other histograms')
1537        self.LimitEdit.Append(id=wxID_ADDEXCLREGION, kind=wx.ITEM_NORMAL,text='Add exclude',
1538            help='Add excluded region - select a point on plot; drag to adjust')           
1539        self.PostfillDataMenu()
1540           
1541        # PDR / Background
1542        self.BackMenu = wx.MenuBar()
1543        self.PrefillDataMenu(self.BackMenu,helpType='Background')
1544        self.BackEdit = wx.Menu(title='')
1545        self.BackMenu.Append(menu=self.BackEdit, title='File')
1546        self.BackEdit.Append(id=wxID_BACKCOPY, kind=wx.ITEM_NORMAL,text='Copy',
1547            help='Copy background parameters to other histograms')
1548        self.BackEdit.Append(id=wxID_BACKFLAGCOPY, kind=wx.ITEM_NORMAL,text='Copy flags',
1549            help='Copy background refinement flags to other histograms')
1550        self.BackEdit.Append(id=wxID_PEAKSMOVE, kind=wx.ITEM_NORMAL,text='Move peaks',
1551            help='Move background peaks to Peak List')
1552        self.BackEdit.Append(id=wxID_MAKEBACKRDF, kind=wx.ITEM_NORMAL,text='Plot RDF',
1553            help='Plot radial distribution from differences')
1554        self.BackFixed = wx.Menu(title='') # fixed background point menu
1555        self.BackMenu.Append(menu=self.BackFixed, title='Fixed Points')
1556        self.wxID_BackPts = {}
1557        self.wxID_BackPts['Add'] = wx.NewId() # N.B. not using wxID_ global as for other menu items
1558        self.BackFixed.Append(id=self.wxID_BackPts['Add'], kind=wx.ITEM_RADIO,text='Add',
1559            help='Add fixed background points with mouse clicks')
1560        self.wxID_BackPts['Move'] = wx.NewId() 
1561        item = self.BackFixed.Append(id=self.wxID_BackPts['Move'], kind=wx.ITEM_RADIO,text='Move',
1562            help='Move selected fixed background points with mouse drags')
1563        item.Check(True)
1564        self.wxID_BackPts['Del'] = wx.NewId()
1565        self.BackFixed.Append(id=self.wxID_BackPts['Del'], kind=wx.ITEM_RADIO,text='Delete',
1566            help='Delete fixed background points with mouse clicks')
1567        self.wxID_BackPts['Clear'] = wx.NewId() 
1568        self.BackFixed.Append(id=self.wxID_BackPts['Clear'], kind=wx.ITEM_NORMAL,text='Clear',
1569            help='Clear fixed background points')
1570        self.wxID_BackPts['Fit'] = wx.NewId() 
1571        self.BackFixed.Append(id=self.wxID_BackPts['Fit'], kind=wx.ITEM_NORMAL,text='Fit background',
1572            help='Fit background function to fixed background points')
1573        self.PostfillDataMenu()
1574           
1575        # PDR / Instrument Parameters
1576        self.InstMenu = wx.MenuBar()
1577        self.PrefillDataMenu(self.InstMenu,helpType='Instrument Parameters')
1578        self.InstEdit = wx.Menu(title='')
1579        self.InstMenu.Append(menu=self.InstEdit, title='Operations')
1580        self.InstEdit.Append(help='Calibrate from indexed peaks', 
1581            id=wxID_INSTCALIB, kind=wx.ITEM_NORMAL,text='Calibrate')           
1582        self.InstEdit.Append(help='Reset instrument profile parameters to default', 
1583            id=wxID_INSTPRMRESET, kind=wx.ITEM_NORMAL,text='Reset profile')           
1584        self.InstEdit.Append(help='Load instrument profile parameters from file', 
1585            id=wxID_INSTLOAD, kind=wx.ITEM_NORMAL,text='Load profile...')           
1586        self.InstEdit.Append(help='Save all instrument profile parameters to one file', 
1587            id=wxID_INSTSAVE, kind=wx.ITEM_NORMAL,text='Save profile...')
1588        self.InstEdit.Append(help='Save instrument profile parameters to file', 
1589            id=wxID_INSTSAVEALL, kind=wx.ITEM_NORMAL,text='Save all profile...')           
1590        self.InstEdit.Append(help='Copy instrument profile parameters to other histograms', 
1591            id=wxID_INSTCOPY, kind=wx.ITEM_NORMAL,text='Copy')
1592        self.InstEdit.Append(id=wxID_INSTFLAGCOPY, kind=wx.ITEM_NORMAL,text='Copy flags',
1593            help='Copy instrument parameter refinement flags to other histograms')
1594#        self.InstEdit.Append(help='Change radiation type (Ka12 - synch)',
1595#            id=wxID_CHANGEWAVETYPE, kind=wx.ITEM_NORMAL,text='Change radiation')
1596        self.InstEdit.Append(id=wxID_INST1VAL, kind=wx.ITEM_NORMAL,text='Set one value',
1597            help='Set one instrument parameter value across multiple histograms')
1598
1599        self.PostfillDataMenu()
1600       
1601        # PDR / Sample Parameters
1602        self.SampleMenu = wx.MenuBar()
1603        self.PrefillDataMenu(self.SampleMenu,helpType='Sample Parameters')
1604        self.SampleEdit = wx.Menu(title='')
1605        self.SampleMenu.Append(menu=self.SampleEdit, title='Command')
1606        self.SetScale = self.SampleEdit.Append(id=wxID_SETSCALE, kind=wx.ITEM_NORMAL,text='Set scale',
1607            help='Set scale by matching to another histogram')
1608        self.SampleEdit.Append(id=wxID_SAMPLELOAD, kind=wx.ITEM_NORMAL,text='Load',
1609            help='Load sample parameters from file')
1610        self.SampleEdit.Append(id=wxID_SAMPLESAVE, kind=wx.ITEM_NORMAL,text='Save',
1611            help='Save sample parameters to file')
1612        self.SampleEdit.Append(id=wxID_SAMPLECOPY, kind=wx.ITEM_NORMAL,text='Copy',
1613            help='Copy refinable and most other sample parameters to other histograms')
1614        self.SampleEdit.Append(id=wxID_SAMPLECOPYSOME, kind=wx.ITEM_NORMAL,text='Copy selected...',
1615            help='Copy selected sample parameters to other histograms')
1616        self.SampleEdit.Append(id=wxID_SAMPLEFLAGCOPY, kind=wx.ITEM_NORMAL,text='Copy flags',
1617            help='Copy sample parameter refinement flags to other histograms')
1618        self.SampleEdit.Append(id=wxID_SAMPLE1VAL, kind=wx.ITEM_NORMAL,text='Set one value',
1619            help='Set one sample parameter value across multiple histograms')
1620        self.SampleEdit.Append(id=wxID_ALLSAMPLELOAD, kind=wx.ITEM_NORMAL,text='Load all',
1621            help='Load sample parmameters over multiple histograms')
1622
1623        self.PostfillDataMenu()
1624        self.SetScale.Enable(False)
1625
1626        # PDR / Peak List
1627        self.PeakMenu = wx.MenuBar()
1628        self.PrefillDataMenu(self.PeakMenu,helpType='Peak List')
1629        self.PeakEdit = wx.Menu(title='')
1630        self.PeakMenu.Append(menu=self.PeakEdit, title='Peak Fitting')
1631        self.AutoSearch = self.PeakEdit.Append(help='Automatic peak search', 
1632            id=wxID_AUTOSEARCH, kind=wx.ITEM_NORMAL,text='Auto search')
1633        self.UnDo = self.PeakEdit.Append(help='Undo last least squares refinement', 
1634            id=wxID_UNDO, kind=wx.ITEM_NORMAL,text='UnDo')
1635        self.PeakFit = self.PeakEdit.Append(id=wxID_LSQPEAKFIT, kind=wx.ITEM_NORMAL,text='Peakfit', 
1636            help='Peak fitting' )
1637        self.PFOneCycle = self.PeakEdit.Append(id=wxID_LSQONECYCLE, kind=wx.ITEM_NORMAL,text='Peakfit one cycle', 
1638            help='One cycle of Peak fitting' )
1639        self.PeakEdit.Append(id=wxID_RESETSIGGAM, kind=wx.ITEM_NORMAL, 
1640            text='Reset sig and gam',help='Reset sigma and gamma to global fit' )
1641        self.PeakCopy = self.PeakEdit.Append(help='Copy peaks to other histograms', 
1642            id=wxID_PEAKSCOPY, kind=wx.ITEM_NORMAL,text='Peak copy')
1643        self.SeqPeakFit = self.PeakEdit.Append(id=wxID_SEQPEAKFIT, kind=wx.ITEM_NORMAL,text='Seq PeakFit', 
1644            help='Sequential Peak fitting for all histograms' )
1645        self.PeakEdit.Append(id=wxID_CLEARPEAKS, kind=wx.ITEM_NORMAL,text='Clear peaks', 
1646            help='Clear the peak list' )
1647        self.PostfillDataMenu()
1648        self.UnDo.Enable(False)
1649        self.PeakFit.Enable(False)
1650        self.PFOneCycle.Enable(False)
1651        self.AutoSearch.Enable(True)
1652       
1653        # PDR / Index Peak List
1654        self.IndPeaksMenu = wx.MenuBar()
1655        self.PrefillDataMenu(self.IndPeaksMenu,helpType='Index Peak List')
1656        self.IndPeaksEdit = wx.Menu(title='')
1657        self.IndPeaksMenu.Append(menu=self.IndPeaksEdit,title='Operations')
1658        self.IndPeaksEdit.Append(help='Load/Reload index peaks from peak list',id=wxID_INDXRELOAD, 
1659            kind=wx.ITEM_NORMAL,text='Load/Reload')
1660        self.PostfillDataMenu()
1661       
1662        # PDR / Unit Cells List
1663        self.IndexMenu = wx.MenuBar()
1664        self.PrefillDataMenu(self.IndexMenu,helpType='Unit Cells List')
1665        self.IndexEdit = wx.Menu(title='')
1666        self.IndexMenu.Append(menu=self.IndexEdit, title='Cell Index/Refine')
1667        self.IndexPeaks = self.IndexEdit.Append(help='', id=wxID_INDEXPEAKS, kind=wx.ITEM_NORMAL,
1668            text='Index Cell')
1669        self.CopyCell = self.IndexEdit.Append( id=wxID_COPYCELL, kind=wx.ITEM_NORMAL,text='Copy Cell', 
1670            help='Copy selected unit cell from indexing to cell refinement fields')
1671        self.RefineCell = self.IndexEdit.Append( id=wxID_REFINECELL, kind=wx.ITEM_NORMAL, 
1672            text='Refine Cell',help='Refine unit cell parameters from indexed peaks')
1673        self.MakeNewPhase = self.IndexEdit.Append( id=wxID_MAKENEWPHASE, kind=wx.ITEM_NORMAL,
1674            text='Make new phase',help='Make new phase from selected unit cell')
1675        self.ExportCells = self.IndexEdit.Append( id=wxID_EXPORTCELLS, kind=wx.ITEM_NORMAL,
1676            text='Export cell list',help='Export cell list to csv file')
1677        self.PostfillDataMenu()
1678        self.IndexPeaks.Enable(False)
1679        self.CopyCell.Enable(False)
1680        self.RefineCell.Enable(False)
1681        self.MakeNewPhase.Enable(False)
1682       
1683        # PDR / Reflection Lists
1684        self.ReflMenu = wx.MenuBar()
1685        self.PrefillDataMenu(self.ReflMenu,helpType='Reflection List')
1686        self.ReflEdit = wx.Menu(title='')
1687        self.ReflMenu.Append(menu=self.ReflEdit, title='Reflection List')
1688        self.SelectPhase = self.ReflEdit.Append(help='Select phase for reflection list',id=wxID_SELECTPHASE, 
1689            kind=wx.ITEM_NORMAL,text='Select phase')
1690        self.ReflEdit.Append(id=wxID_PWDHKLPLOT,kind=wx.ITEM_NORMAL,text='Plot HKLs',
1691            help='Plot HKLs from powder pattern')
1692        self.ReflEdit.Append(id=wxID_PWD3DHKLPLOT,kind=wx.ITEM_NORMAL,text='Plot 3D HKLs',
1693            help='Plot HKLs from powder pattern in 3D')
1694        self.PostfillDataMenu()
1695       
1696        # SASD / Instrument Parameters
1697        self.SASDInstMenu = wx.MenuBar()
1698        self.PrefillDataMenu(self.SASDInstMenu,helpType='Instrument Parameters')
1699        self.SASDInstEdit = wx.Menu(title='')
1700        self.SASDInstMenu.Append(menu=self.SASDInstEdit, title='Operations')
1701        self.InstEdit.Append(help='Reset instrument profile parameters to default', 
1702            id=wxID_INSTPRMRESET, kind=wx.ITEM_NORMAL,text='Reset profile')
1703        self.SASDInstEdit.Append(help='Copy instrument profile parameters to other histograms', 
1704            id=wxID_INSTCOPY, kind=wx.ITEM_NORMAL,text='Copy')
1705        self.PostfillDataMenu()
1706       
1707        #SASD & REFL/ Substance editor
1708        self.SubstanceMenu = wx.MenuBar()
1709        self.PrefillDataMenu(self.SubstanceMenu,helpType='Substances')
1710        self.SubstanceEdit = wx.Menu(title='')
1711        self.SubstanceMenu.Append(menu=self.SubstanceEdit, title='Edit')
1712        self.SubstanceEdit.Append(id=wxID_LOADSUBSTANCE, kind=wx.ITEM_NORMAL,text='Load substance',
1713            help='Load substance from file')
1714        self.SubstanceEdit.Append(id=wxID_ADDSUBSTANCE, kind=wx.ITEM_NORMAL,text='Add substance',
1715            help='Add new substance to list')
1716        self.SubstanceEdit.Append(id=wxID_COPYSUBSTANCE, kind=wx.ITEM_NORMAL,text='Copy substances',
1717            help='Copy substances')
1718        self.SubstanceEdit.Append(id=wxID_DELETESUBSTANCE, kind=wx.ITEM_NORMAL,text='Delete substance',
1719            help='Delete substance from list')           
1720        self.SubstanceEdit.Append(id=wxID_ELEMENTADD, kind=wx.ITEM_NORMAL,text='Add elements',
1721            help='Add elements to substance')
1722        self.SubstanceEdit.Append(id=wxID_ELEMENTDELETE, kind=wx.ITEM_NORMAL,text='Delete elements',
1723            help='Delete elements from substance')
1724        self.PostfillDataMenu()
1725       
1726        # SASD/ Models
1727        self.ModelMenu = wx.MenuBar()
1728        self.PrefillDataMenu(self.ModelMenu,helpType='Models')
1729        self.ModelEdit = wx.Menu(title='')
1730        self.ModelMenu.Append(menu=self.ModelEdit, title='Models')
1731        self.ModelEdit.Append(id=wxID_MODELADD,kind=wx.ITEM_NORMAL,text='Add',
1732            help='Add new term to model')
1733        self.ModelEdit.Append(id=wxID_MODELFIT, kind=wx.ITEM_NORMAL,text='Fit',
1734            help='Fit model parameters to data')
1735        self.SasdUndo = self.ModelEdit.Append(id=wxID_MODELUNDO, kind=wx.ITEM_NORMAL,text='Undo',
1736            help='Undo model fit')
1737        self.SasdUndo.Enable(False)           
1738        self.ModelEdit.Append(id=wxID_MODELFITALL, kind=wx.ITEM_NORMAL,text='Sequential fit',
1739            help='Sequential fit of model parameters to all SASD data')
1740        self.ModelEdit.Append(id=wxID_MODELCOPY, kind=wx.ITEM_NORMAL,text='Copy',
1741            help='Copy model parameters to other histograms')
1742        self.ModelEdit.Append(id=wxID_MODELCOPYFLAGS, kind=wx.ITEM_NORMAL,text='Copy flags',
1743            help='Copy model refinement flags to other histograms')
1744        self.PostfillDataMenu()
1745       
1746        # IMG / Image Controls
1747        self.ImageMenu = wx.MenuBar()
1748        self.PrefillDataMenu(self.ImageMenu,helpType='Image Controls')
1749        self.ImageEdit = wx.Menu(title='')
1750        self.ImageMenu.Append(menu=self.ImageEdit, title='Operations')
1751        self.ImageEdit.Append(help='Calibrate detector by fitting to calibrant lines', 
1752            id=wxID_IMCALIBRATE, kind=wx.ITEM_NORMAL,text='Calibrate')
1753        self.ImageEdit.Append(help='Recalibrate detector by fitting to calibrant lines', 
1754            id=wxID_IMRECALIBRATE, kind=wx.ITEM_NORMAL,text='Recalibrate')
1755        self.ImageEdit.Append(help='Recalibrate all images by fitting to calibrant lines', 
1756            id=wxID_IMRECALIBALL, kind=wx.ITEM_NORMAL,text='Recalibrate all')           
1757        self.ImageEdit.Append(help='Clear calibration data points and rings',id=wxID_IMCLEARCALIB, 
1758            kind=wx.ITEM_NORMAL,text='Clear calibration')
1759        self.ImageEdit.Append(help='Integrate selected image',id=wxID_IMINTEGRATE, 
1760            kind=wx.ITEM_NORMAL,text='Integrate')
1761        self.ImageEdit.Append(help='Integrate all images selected from list',id=wxID_INTEGRATEALL,
1762            kind=wx.ITEM_NORMAL,text='Integrate all')
1763        self.ImageEdit.Append(help='Copy image controls to other images', 
1764            id=wxID_IMCOPYCONTROLS, kind=wx.ITEM_NORMAL,text='Copy Controls')
1765        self.ImageEdit.Append(help='Copy selected image controls to other images', 
1766            id=wxID_IMCOPYSELECTED, kind=wx.ITEM_NORMAL,text='Copy Selected')
1767        self.ImageEdit.Append(help='Save image controls to file', 
1768            id=wxID_IMSAVECONTROLS, kind=wx.ITEM_NORMAL,text='Save Controls')
1769        self.ImageEdit.Append(help='Save controls from selected images to file', 
1770            id=wxID_SAVESELECTEDCONTROLS, kind=wx.ITEM_NORMAL,text='Save Multiple Controls')
1771        self.ImageEdit.Append(help='Load image controls from file', 
1772            id=wxID_IMLOADCONTROLS, kind=wx.ITEM_NORMAL,text='Load Controls')
1773        self.ImageEdit.Append(help='Open Auto-integration window to integrate a series of images', 
1774            id=wxID_IMAUTOINTEG, kind=wx.ITEM_NORMAL,text='Auto Integrate')
1775        self.PostfillDataMenu()
1776           
1777        # IMG / Masks
1778        self.MaskMenu = wx.MenuBar()
1779        self.PrefillDataMenu(self.MaskMenu,helpType='Image Masks')
1780        self.MaskEdit = wx.Menu(title='')
1781        self.MaskMenu.Append(menu=self.MaskEdit, title='Operations')
1782        submenu = wx.Menu()
1783        self.MaskEdit.AppendMenu(
1784            wx.ID_ANY,'Create new', submenu,
1785            help=''
1786            )
1787        self.MaskEdit.Append(help='Copy mask to other images', 
1788            id=wxID_MASKCOPY, kind=wx.ITEM_NORMAL,text='Copy mask')
1789        self.MaskEdit.Append(help='Save mask to file', 
1790            id=wxID_MASKSAVE, kind=wx.ITEM_NORMAL,text='Save mask')
1791        self.MaskEdit.Append(help='Load mask from file', 
1792            id=wxID_MASKLOAD, kind=wx.ITEM_NORMAL,text='Load mask')
1793        self.MaskEdit.Append(help='Load mask from file; ignore threshold', 
1794            id=wxID_MASKLOADNOT, kind=wx.ITEM_NORMAL,text='Load mask w/o threshold')
1795        submenu.Append(help='Create an arc mask with mouse input', 
1796            id=wxID_NEWMASKARC, kind=wx.ITEM_NORMAL,text='Arc mask')
1797        submenu.Append(help='Create a frame mask with mouse input', 
1798            id=wxID_NEWMASKFRAME, kind=wx.ITEM_NORMAL,text='Frame mask')
1799        submenu.Append(help='Create a polygon mask with mouse input', 
1800            id=wxID_NEWMASKPOLY, kind=wx.ITEM_NORMAL,text='Polygon mask')
1801        submenu.Append(help='Create a ring mask with mouse input', 
1802            id=wxID_NEWMASKRING, kind=wx.ITEM_NORMAL,text='Ring mask')
1803        submenu.Append(help='Create a spot mask with mouse input', 
1804            id=wxID_NEWMASKSPOT, kind=wx.ITEM_NORMAL,text='Spot mask')
1805        self.PostfillDataMenu()
1806           
1807        # IMG / Stress/Strain
1808        self.StrStaMenu = wx.MenuBar()
1809        self.PrefillDataMenu(self.StrStaMenu,helpType='Stress/Strain')
1810        self.StrStaEdit = wx.Menu(title='')
1811        self.StrStaMenu.Append(menu=self.StrStaEdit, title='Operations')
1812        self.StrStaEdit.Append(help='Append d-zero for one ring', 
1813            id=wxID_APPENDDZERO, kind=wx.ITEM_NORMAL,text='Append d-zero')
1814        self.StrStaEdit.Append(help='Fit stress/strain data', 
1815            id=wxID_STRSTAFIT, kind=wx.ITEM_NORMAL,text='Fit stress/strain')
1816        self.StrStaEdit.Append(help='Update d-zero from ave d-zero',
1817            id=wxID_UPDATEDZERO, kind=wx.ITEM_NORMAL,text='Update d-zero')       
1818        self.StrStaEdit.Append(help='Fit stress/strain data for all images', 
1819            id=wxID_STRSTAALLFIT, kind=wx.ITEM_NORMAL,text='All image fit')
1820        self.StrStaEdit.Append(help='Copy stress/strain data to other images', 
1821            id=wxID_STRSTACOPY, kind=wx.ITEM_NORMAL,text='Copy stress/strain')
1822        self.StrStaEdit.Append(help='Save stress/strain data to file', 
1823            id=wxID_STRSTASAVE, kind=wx.ITEM_NORMAL,text='Save stress/strain')
1824        self.StrStaEdit.Append(help='Load stress/strain data from file', 
1825            id=wxID_STRSTALOAD, kind=wx.ITEM_NORMAL,text='Load stress/strain')
1826        self.StrStaEdit.Append(help='Load sample data from file', 
1827            id=wxID_STRSTSAMPLE, kind=wx.ITEM_NORMAL,text='Load sample data')
1828        self.PostfillDataMenu()
1829           
1830        # PDF / PDF Controls
1831        self.PDFMenu = wx.MenuBar()
1832        self.PrefillDataMenu(self.PDFMenu,helpType='PDF Controls')
1833        self.PDFEdit = wx.Menu(title='')
1834        self.PDFMenu.Append(menu=self.PDFEdit, title='PDF Controls')
1835        self.PDFEdit.Append(help='Add element to sample composition',id=wxID_PDFADDELEMENT, kind=wx.ITEM_NORMAL,
1836            text='Add element')
1837        self.PDFEdit.Append(help='Delete element from sample composition',id=wxID_PDFDELELEMENT, kind=wx.ITEM_NORMAL,
1838            text='Delete element')
1839        self.PDFEdit.Append(help='Copy PDF controls', id=wxID_PDFCOPYCONTROLS, kind=wx.ITEM_NORMAL,
1840            text='Copy controls')
1841        self.PDFEdit.Append(help='Load PDF controls from file',id=wxID_PDFLOADCONTROLS, kind=wx.ITEM_NORMAL,
1842            text='Load Controls')
1843        self.PDFEdit.Append(help='Save PDF controls to file', id=wxID_PDFSAVECONTROLS, kind=wx.ITEM_NORMAL,
1844            text='Save controls')
1845        self.PDFEdit.Append(help='Compute PDF', id=wxID_PDFCOMPUTE, kind=wx.ITEM_NORMAL,
1846            text='Compute PDF')
1847        self.PDFEdit.Append(help='Compute all PDFs', id=wxID_PDFCOMPUTEALL, kind=wx.ITEM_NORMAL,
1848            text='Compute all PDFs')
1849        self.PostfillDataMenu()
1850       
1851        # Phase / General tab
1852        self.DataGeneral = wx.MenuBar()
1853        self.PrefillDataMenu(self.DataGeneral,helpType='General', helpLbl='Phase/General')
1854        self.DataGeneral.Append(menu=wx.Menu(title=''),title='Select tab')
1855        self.GeneralCalc = wx.Menu(title='')
1856        self.DataGeneral.Append(menu=self.GeneralCalc,title='Compute')
1857        self.GeneralCalc.Append(help='Compute Fourier map',id=wxID_FOURCALC, kind=wx.ITEM_NORMAL,
1858            text='Fourier map')
1859        self.GeneralCalc.Append(help='Search Fourier map',id=wxID_FOURSEARCH, kind=wx.ITEM_NORMAL,
1860            text='Search map')
1861        self.GeneralCalc.Append(help='Run charge flipping',id=wxID_CHARGEFLIP, kind=wx.ITEM_NORMAL,
1862            text='Charge flipping')
1863        self.GeneralCalc.Append(help='Run 4D charge flipping',id=wxID_4DCHARGEFLIP, kind=wx.ITEM_NORMAL,
1864            text='4D Charge flipping')
1865        self.GeneralCalc.Enable(wxID_4DCHARGEFLIP,False)   
1866        self.GeneralCalc.Append(help='Clear map',id=wxID_FOURCLEAR, kind=wx.ITEM_NORMAL,
1867            text='Clear map')
1868        self.GeneralCalc.Append(help='Run Monte Carlo - Simulated Annealing',id=wxID_SINGLEMCSA, kind=wx.ITEM_NORMAL,
1869            text='MC/SA')
1870        self.GeneralCalc.Append(help='Run Monte Carlo - Simulated Annealing on multiprocessors',id=wxID_MULTIMCSA, kind=wx.ITEM_NORMAL,
1871            text='Multi MC/SA')            #currently not useful
1872        self.GeneralCalc.Append(help='Transform crystal structure',id=wxID_TRANSFORMSTRUCTURE, kind=wx.ITEM_NORMAL,
1873            text='Transform')
1874        self.PostfillDataMenu()
1875       
1876        # Phase / Data tab
1877        self.DataMenu = wx.MenuBar()
1878        self.PrefillDataMenu(self.DataMenu,helpType='Data', helpLbl='Phase/Data')
1879        self.DataMenu.Append(menu=wx.Menu(title=''),title='Select tab')
1880        self.DataEdit = wx.Menu(title='')
1881        self.DataMenu.Append(menu=self.DataEdit, title='Edit')
1882        self.DataEdit.Append(id=wxID_DATACOPY, kind=wx.ITEM_NORMAL,text='Copy data',
1883            help='Copy phase data to other histograms')
1884        self.DataEdit.Append(id=wxID_DATACOPYFLAGS, kind=wx.ITEM_NORMAL,text='Copy flags',
1885            help='Copy phase data flags to other histograms')
1886        self.DataEdit.Append(id=wxID_DATASELCOPY, kind=wx.ITEM_NORMAL,text='Copy selected data',
1887            help='Copy selected phase data to other histograms')
1888        self.DataEdit.Append(id=wxID_DATAUSE, kind=wx.ITEM_NORMAL,text='Select used data',
1889            help='Select all histograms to use')
1890        self.DataEdit.Append(id=wxID_PWDRADD, kind=wx.ITEM_NORMAL,text='Add powder histograms',
1891            help='Select new powder histograms to be used for this phase')
1892        self.DataEdit.Append(id=wxID_HKLFADD, kind=wx.ITEM_NORMAL,text='Add single crystal histograms',
1893            help='Select new single crystal histograms to be used for this phase')
1894        self.DataEdit.Append(id=wxID_DATADELETE, kind=wx.ITEM_NORMAL,text='Remove histograms',
1895            help='Remove histograms from use for this phase')
1896        self.PostfillDataMenu()
1897           
1898        # Phase / Atoms tab
1899        self.AtomsMenu = wx.MenuBar()
1900        self.PrefillDataMenu(self.AtomsMenu,helpType='Atoms')
1901        self.AtomsMenu.Append(menu=wx.Menu(title=''),title='Select tab')
1902        self.AtomEdit = wx.Menu(title='')
1903        self.AtomCompute = wx.Menu(title='')
1904        self.AtomsMenu.Append(menu=self.AtomEdit, title='Edit')
1905        self.AtomsMenu.Append(menu=self.AtomCompute, title='Compute')
1906        self.AtomEdit.Append(id=wxID_ATOMSEDITADD, kind=wx.ITEM_NORMAL,text='Append atom',
1907            help='Appended as an H atom')
1908        self.AtomEdit.Append(id=wxID_ATOMSVIEWADD, kind=wx.ITEM_NORMAL,text='Append view point',
1909            help='Appended as an H atom')
1910        self.AtomEdit.Append(id=wxID_ATOMSEDITINSERT, kind=wx.ITEM_NORMAL,text='Insert atom',
1911            help='Select atom row to insert before; inserted as an H atom')
1912        self.AtomEdit.Append(id=wxID_ATOMVIEWINSERT, kind=wx.ITEM_NORMAL,text='Insert view point',
1913            help='Select atom row to insert before; inserted as an H atom')
1914        self.AtomEdit.Append(id=wxID_ADDHATOM, kind=wx.ITEM_NORMAL,text='Insert H atoms',
1915            help='Insert H atoms in standard positions bonded to selected atoms')
1916        self.AtomEdit.Append(id=wxID_UPDATEHATOM, kind=wx.ITEM_NORMAL,text='Update H atoms',
1917            help='Update H atoms in standard positions')
1918        self.AtomEdit.Append(id=wxID_ATOMMOVE, kind=wx.ITEM_NORMAL,text='Move atom to view point',
1919            help='Select single atom to move')
1920        self.AtomEdit.Append(id=wxID_ATOMSEDITDELETE, kind=wx.ITEM_NORMAL,text='Delete atom',
1921            help='Select atoms to delete first')
1922        self.AtomEdit.Append(id=wxID_ATOMSREFINE, kind=wx.ITEM_NORMAL,text='Set atom refinement flags',
1923            help='Select atoms to refine first')
1924        self.AtomEdit.Append(id=wxID_ATOMSMODIFY, kind=wx.ITEM_NORMAL,text='Modify atom parameters',
1925            help='Select atoms to modify first')
1926        self.AtomEdit.Append(id=wxID_ATOMSTRANSFORM, kind=wx.ITEM_NORMAL,text='Transform atoms',
1927            help='Select atoms to transform first')
1928#        self.AtomEdit.Append(id=wxID_ATOMSROTATE, kind=wx.ITEM_NORMAL,text='Rotate atoms',
1929#            help='Select atoms to rotate first')
1930        self.AtomEdit.Append(id=wxID_MAKEMOLECULE, kind=wx.ITEM_NORMAL,text='Assemble molecule',
1931            help='Assemble molecule from scatterd atom positions')
1932        self.AtomEdit.Append(id=wxID_RELOADDRAWATOMS, kind=wx.ITEM_NORMAL,text='Reload draw atoms',
1933            help='Reload atom drawing list')
1934        submenu = wx.Menu()
1935        self.AtomEdit.AppendMenu(wx.ID_ANY, 'Reimport atoms', submenu, 
1936            help='Reimport atoms from file; sequence must match')
1937        # setup a cascade menu for the formats that have been defined
1938        self.ReImportMenuId = {}  # points to readers for each menu entry
1939        for reader in self.G2frame.ImportPhaseReaderlist:
1940            item = submenu.Append(
1941                wx.ID_ANY,help=reader.longFormatName,
1942                kind=wx.ITEM_NORMAL,text='reimport coordinates from '+reader.formatName+' file')
1943            self.ReImportMenuId[item.GetId()] = reader
1944        item = submenu.Append(
1945            wx.ID_ANY,
1946            help='Reimport coordinates, try to determine format from file',
1947            kind=wx.ITEM_NORMAL,
1948            text='guess format from file')
1949        self.ReImportMenuId[item.GetId()] = None # try all readers
1950
1951        self.AtomCompute.Append(id=wxID_ATOMSDISAGL, kind=wx.ITEM_NORMAL,text='Show Distances && Angles',
1952            help='Compute distances & angles for selected atoms')
1953        self.AtomCompute.Append(id=wxID_ATOMSPDISAGL, kind=wx.ITEM_NORMAL,text='Save Distances && Angles',
1954            help='Compute distances & angles for selected atoms')
1955        self.AtomCompute.ISOcalc = self.AtomCompute.Append(
1956            id=wxID_ISODISP, kind=wx.ITEM_NORMAL,
1957            text='Compute ISODISTORT mode values',
1958            help='Compute values of ISODISTORT modes from atom parameters')
1959        self.PostfillDataMenu()
1960       
1961        # Phase / Imcommensurate "waves" tab
1962        self.WavesData = wx.MenuBar()
1963        self.PrefillDataMenu(self.WavesData,helpType='Wave Data', helpLbl='Imcommensurate wave data')
1964        self.WavesData.Append(menu=wx.Menu(title=''),title='Select tab')
1965        self.WavesDataEdit = wx.Menu(title='')
1966        self.WavesData.Append(menu=self.WavesDataEdit, title='Edit')
1967        self.WavesDataEdit.Append(id=wxID_WAVEVARY, kind=wx.ITEM_NORMAL,text='Global wave vary',
1968            help='Global setting of wave vary flags')
1969        self.PostfillDataMenu()
1970       
1971        # Phase / Layer tab
1972        self.LayerData = wx.MenuBar()
1973        self.PrefillDataMenu(self.LayerData,helpType='Layer Data', helpLbl='Stacking fault layers')
1974        self.LayerData.Append(menu=wx.Menu(title=''),title='Select tab')
1975        self.LayerDataEdit = wx.Menu(title='')
1976        self.LayerData.Append(menu=self.LayerDataEdit, title='Operations')
1977        self.LayerDataEdit.Append(id=wxID_LOADDIFFAX, kind=wx.ITEM_NORMAL,text='Load from DIFFaX file',
1978            help='Load layer info from DIFFaX file')
1979        self.LayerDataEdit.Append(id=wxID_COPYPHASE, kind=wx.ITEM_NORMAL,text='Copy phase cell',
1980            help='Copy phase cell from another project')
1981        self.LayerDataEdit.Append(id=wxID_LAYERSIMULATE, kind=wx.ITEM_NORMAL,text='Simulate pattern',
1982            help='Simulate diffraction pattern from layer stacking')
1983        self.LayerDataEdit.Append(id=wxID_SEQUENCESIMULATE, kind=wx.ITEM_NORMAL,text='Sequence simulations',
1984            help='Sequence simulation changing one parameter')
1985        self.PostfillDataMenu()
1986                 
1987        # Phase / Draw Options tab
1988        self.DataDrawOptions = wx.MenuBar()
1989        self.PrefillDataMenu(self.DataDrawOptions,helpType='Draw Options', helpLbl='Phase/Draw Options')
1990        self.DataDrawOptions.Append(menu=wx.Menu(title=''),title='Select tab')
1991        self.PostfillDataMenu()
1992       
1993        # Phase / Draw Atoms tab
1994        self.DrawAtomsMenu = wx.MenuBar()
1995        self.PrefillDataMenu(self.DrawAtomsMenu,helpType='Draw Atoms')
1996        self.DrawAtomsMenu.Append(menu=wx.Menu(title=''),title='Select tab')
1997        self.DrawAtomEdit = wx.Menu(title='')
1998        self.DrawAtomCompute = wx.Menu(title='')
1999        self.DrawAtomRestraint = wx.Menu(title='')
2000        self.DrawAtomRigidBody = wx.Menu(title='')
2001        self.DrawAtomsMenu.Append(menu=self.DrawAtomEdit, title='Edit')
2002        self.DrawAtomsMenu.Append(menu=self.DrawAtomCompute,title='Compute')
2003        self.DrawAtomsMenu.Append(menu=self.DrawAtomRestraint, title='Restraints')
2004        self.DrawAtomsMenu.Append(menu=self.DrawAtomRigidBody, title='Rigid body')
2005        self.DrawAtomEdit.Append(id=wxID_DRAWATOMSTYLE, kind=wx.ITEM_NORMAL,text='Atom style',
2006            help='Select atoms first')
2007        self.DrawAtomEdit.Append(id=wxID_DRAWATOMLABEL, kind=wx.ITEM_NORMAL,text='Atom label',
2008            help='Select atoms first')
2009        self.DrawAtomEdit.Append(id=wxID_DRAWATOMCOLOR, kind=wx.ITEM_NORMAL,text='Atom color',
2010            help='Select atoms first')
2011        self.DrawAtomEdit.Append(id=wxID_DRAWATOMRESETCOLOR, kind=wx.ITEM_NORMAL,text='Reset atom colors',
2012            help='Resets all atom colors to defaults')
2013        self.DrawAtomEdit.Append(id=wxID_DRAWVIEWPOINT, kind=wx.ITEM_NORMAL,text='View point',
2014            help='View point is 1st atom selected')
2015        self.DrawAtomEdit.Append(id=wxID_DRAWADDEQUIV, kind=wx.ITEM_NORMAL,text='Add atoms',
2016            help='Add symmetry & cell equivalents to drawing set from selected atoms')
2017        self.DrawAtomEdit.Append(id=wxID_DRAWTRANSFORM, kind=wx.ITEM_NORMAL,text='Transform draw atoms',
2018            help='Transform selected atoms by symmetry & cell translations')
2019        self.DrawAtomEdit.Append(id=wxID_DRAWFILLCOORD, kind=wx.ITEM_NORMAL,text='Fill CN-sphere',
2020            help='Fill coordination sphere for selected atoms')           
2021        self.DrawAtomEdit.Append(id=wxID_DRAWFILLCELL, kind=wx.ITEM_NORMAL,text='Fill unit cell',
2022            help='Fill unit cell with selected atoms')
2023        self.DrawAtomEdit.Append(id=wxID_DRAWDELETE, kind=wx.ITEM_NORMAL,text='Delete atoms',
2024            help='Delete atoms from drawing set')
2025        self.DrawAtomCompute.Append(id=wxID_DRAWDISTVP, kind=wx.ITEM_NORMAL,text='View pt. dist.',
2026            help='Compute distance of selected atoms from view point')   
2027        self.DrawAtomCompute.Append(id=wxID_DRAWDISAGLTOR, kind=wx.ITEM_NORMAL,text='Dist. Ang. Tors.',
2028            help='Compute distance, angle or torsion for 2-4 selected atoms')   
2029        self.DrawAtomCompute.Append(id=wxID_DRAWPLANE, kind=wx.ITEM_NORMAL,text='Best plane',
2030            help='Compute best plane for 4+ selected atoms')   
2031        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRBOND, kind=wx.ITEM_NORMAL,text='Add bond restraint',
2032            help='Add bond restraint for selected atoms (2)')
2033        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRANGLE, kind=wx.ITEM_NORMAL,text='Add angle restraint',
2034            help='Add angle restraint for selected atoms (3: one end 1st)')
2035        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRPLANE, kind=wx.ITEM_NORMAL,text='Add plane restraint',
2036            help='Add plane restraint for selected atoms (4+)')
2037        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRCHIRAL, kind=wx.ITEM_NORMAL,text='Add chiral restraint',
2038            help='Add chiral restraint for selected atoms (4: center atom 1st)')
2039        self.DrawAtomRigidBody.Append(id=wxID_DRAWDEFINERB, kind=wx.ITEM_NORMAL,text='Define rigid body',
2040            help='Define rigid body with selected atoms')
2041        self.PostfillDataMenu()
2042
2043        # Phase / MCSA tab
2044        self.MCSAMenu = wx.MenuBar()
2045        self.PrefillDataMenu(self.MCSAMenu,helpType='MC/SA')
2046        self.MCSAMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2047        self.MCSAEdit = wx.Menu(title='')
2048        self.MCSAMenu.Append(menu=self.MCSAEdit, title='MC/SA')
2049        self.MCSAEdit.Append(id=wxID_ADDMCSAATOM, kind=wx.ITEM_NORMAL,text='Add atom', 
2050            help='Add single atom to MC/SA model')
2051        self.MCSAEdit.Append(id=wxID_ADDMCSARB, kind=wx.ITEM_NORMAL,text='Add rigid body', 
2052            help='Add rigid body to MC/SA model' )
2053        self.MCSAEdit.Append(id=wxID_CLEARMCSARB, kind=wx.ITEM_NORMAL,text='Clear rigid bodies', 
2054            help='Clear all atoms & rigid bodies from MC/SA model' )
2055        self.MCSAEdit.Append(id=wxID_MOVEMCSA, kind=wx.ITEM_NORMAL,text='Move MC/SA solution', 
2056            help='Move MC/SA solution to atom list' )
2057        self.MCSAEdit.Append(id=wxID_MCSACLEARRESULTS, kind=wx.ITEM_NORMAL,text='Clear results', 
2058            help='Clear table of MC/SA results' )
2059        self.PostfillDataMenu()
2060           
2061        # Phase / Texture tab
2062        self.TextureMenu = wx.MenuBar()
2063        self.PrefillDataMenu(self.TextureMenu,helpType='Texture')
2064        self.TextureMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2065        self.TextureEdit = wx.Menu(title='')
2066        self.TextureMenu.Append(menu=self.TextureEdit, title='Texture')
2067        self.TextureEdit.Append(id=wxID_REFINETEXTURE, kind=wx.ITEM_NORMAL,text='Refine texture', 
2068            help='Refine the texture coefficients from sequential results')
2069#        self.TextureEdit.Append(id=wxID_CLEARTEXTURE, kind=wx.ITEM_NORMAL,text='Clear texture',
2070#            help='Clear the texture coefficients' )
2071        self.PostfillDataMenu()
2072           
2073        # Phase / Pawley tab
2074        self.PawleyMenu = wx.MenuBar()
2075        self.PrefillDataMenu(self.PawleyMenu,helpType='Pawley')
2076        self.PawleyMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2077        self.PawleyEdit = wx.Menu(title='')
2078        self.PawleyMenu.Append(menu=self.PawleyEdit,title='Operations')
2079        self.PawleyEdit.Append(id=wxID_PAWLEYLOAD, kind=wx.ITEM_NORMAL,text='Pawley create',
2080            help='Initialize Pawley reflection list')
2081        self.PawleyEdit.Append(id=wxID_PAWLEYESTIMATE, kind=wx.ITEM_NORMAL,text='Pawley estimate',
2082            help='Estimate initial Pawley intensities')
2083        self.PawleyEdit.Append(id=wxID_PAWLEYUPDATE, kind=wx.ITEM_NORMAL,text='Pawley update',
2084            help='Update negative Pawley intensities with -0.5*Fobs and turn off refinemnt')
2085        self.PostfillDataMenu()
2086           
2087        # Phase / Map peaks tab
2088        self.MapPeaksMenu = wx.MenuBar()
2089        self.PrefillDataMenu(self.MapPeaksMenu,helpType='Map peaks')
2090        self.MapPeaksMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2091        self.MapPeaksEdit = wx.Menu(title='')
2092        self.MapPeaksMenu.Append(menu=self.MapPeaksEdit, title='Map peaks')
2093        self.MapPeaksEdit.Append(id=wxID_PEAKSMOVE, kind=wx.ITEM_NORMAL,text='Move peaks', 
2094            help='Move selected peaks to atom list')
2095        self.MapPeaksEdit.Append(id=wxID_PEAKSVIEWPT, kind=wx.ITEM_NORMAL,text='View point',
2096            help='View point is 1st peak selected')
2097        self.MapPeaksEdit.Append(id=wxID_PEAKSDISTVP, kind=wx.ITEM_NORMAL,text='View pt. dist.',
2098            help='Compute distance of selected peaks from view point')   
2099        self.MapPeaksEdit.Append(id=wxID_SHOWBONDS, kind=wx.ITEM_NORMAL,text='Hide bonds',
2100            help='Hide or show bonds between peak positions')   
2101        self.MapPeaksEdit.Append(id=wxID_PEAKSDA, kind=wx.ITEM_NORMAL,text='Calc dist/ang', 
2102            help='Calculate distance or angle for selection')
2103        self.MapPeaksEdit.Append(id=wxID_FINDEQVPEAKS, kind=wx.ITEM_NORMAL,text='Equivalent peaks', 
2104            help='Find equivalent peaks')
2105        self.MapPeaksEdit.Append(id=wxID_PEAKSUNIQUE, kind=wx.ITEM_NORMAL,text='Unique peaks', 
2106            help='Select unique set')
2107        self.MapPeaksEdit.Append(id=wxID_PEAKSDELETE, kind=wx.ITEM_NORMAL,text='Delete peaks', 
2108            help='Delete selected peaks')
2109        self.MapPeaksEdit.Append(id=wxID_PEAKSCLEAR, kind=wx.ITEM_NORMAL,text='Clear peaks', 
2110            help='Clear the map peak list')
2111        self.PostfillDataMenu()
2112
2113        # Phase / Rigid bodies tab
2114        self.RigidBodiesMenu = wx.MenuBar()
2115        self.PrefillDataMenu(self.RigidBodiesMenu,helpType='Rigid bodies')
2116        self.RigidBodiesMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2117        self.RigidBodiesEdit = wx.Menu(title='')
2118        self.RigidBodiesMenu.Append(menu=self.RigidBodiesEdit, title='Edit')
2119        self.RigidBodiesEdit.Append(id=wxID_ASSIGNATMS2RB, kind=wx.ITEM_NORMAL,text='Assign atoms to rigid body',
2120            help='Select & position rigid body in structure of existing atoms')
2121        self.RigidBodiesEdit.Append(id=wxID_AUTOFINDRESRB, kind=wx.ITEM_NORMAL,text='Auto find residues',
2122            help='Auto find of residue RBs in macromolecule')
2123        self.RigidBodiesEdit.Append(id=wxID_COPYRBPARMS, kind=wx.ITEM_NORMAL,text='Copy rigid body parms',
2124            help='Copy rigid body location & TLS parameters')
2125        self.RigidBodiesEdit.Append(id=wxID_GLOBALTHERM, kind=wx.ITEM_NORMAL,text='Global thermal motion',
2126            help='Global setting of residue thermal motion models')
2127        self.RigidBodiesEdit.Append(id=wxID_GLOBALRESREFINE, kind=wx.ITEM_NORMAL,text='Global residue refine',
2128            help='Global setting of residue RB refinement flags')
2129        self.RigidBodiesEdit.Append(id=wxID_RBREMOVEALL, kind=wx.ITEM_NORMAL,text='Remove all rigid bodies',
2130            help='Remove all rigid body assignment for atoms')
2131        self.PostfillDataMenu()
2132    # end of GSAS-II menu definitions
2133       
2134    def _init_ctrls(self, parent,name=None,size=None,pos=None):
2135        wx.Frame.__init__(
2136            self,parent=parent,
2137            #style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX | wx.FRAME_FLOAT_ON_PARENT ,
2138            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX,
2139            size=size,pos=pos,title='GSAS-II data display')
2140        self._init_menus()
2141        if name:
2142            self.SetLabel(name)
2143        self.Show()
2144       
2145    def __init__(self,parent,frame,data=None,name=None, size=None,pos=None):
2146        self.G2frame = frame
2147        self._init_ctrls(parent,name,size,pos)
2148        self.data = data
2149        clientSize = wx.ClientDisplayRect()
2150        Size = self.GetSize()
2151        xPos = clientSize[2]-Size[0]
2152        self.SetPosition(wx.Point(xPos,clientSize[1]+250))
2153        self.AtomGrid = []
2154        self.selectedRow = 0
2155       
2156    def setSizePosLeft(self,Width):
2157        clientSize = wx.ClientDisplayRect()
2158        Width[1] = min(Width[1],clientSize[2]-300)
2159        Width[0] = max(Width[0],300)
2160        self.SetSize(Width)
2161#        self.SetPosition(wx.Point(clientSize[2]-Width[0],clientSize[1]+250))
2162       
2163    def Clear(self):
2164        self.ClearBackground()
2165        self.DestroyChildren()
2166                   
2167
2168################################################################################
2169#####  Notebook Tree Item editor
2170################################################################################                 
2171def UpdateNotebook(G2frame,data):
2172    '''Called when the data tree notebook entry is selected. Allows for
2173    editing of the text in that tree entry
2174    '''
2175    def OnNoteBook(event):
2176        data = G2frame.dataDisplay.GetValue().split('\n')
2177        G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,G2frame.root,'Notebook'),data)
2178        if 'nt' not in os.name:
2179            G2frame.dataDisplay.AppendText('\n')
2180                   
2181    if G2frame.dataDisplay:
2182        G2frame.dataDisplay.Destroy()
2183    G2frame.dataFrame.SetLabel('Notebook')
2184    G2frame.dataDisplay = wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
2185        style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER | wx.TE_DONTWRAP)
2186    G2frame.dataDisplay.Bind(wx.EVT_TEXT_ENTER,OnNoteBook)
2187    G2frame.dataDisplay.Bind(wx.EVT_KILL_FOCUS,OnNoteBook)
2188    for line in data:
2189        G2frame.dataDisplay.AppendText(line+"\n")
2190    G2frame.dataDisplay.AppendText('Notebook entry @ '+time.ctime()+"\n")
2191    G2frame.dataFrame.setSizePosLeft([400,250])
2192           
2193################################################################################
2194#####  Controls Tree Item editor
2195################################################################################           
2196def UpdateControls(G2frame,data):
2197    '''Edit overall GSAS-II controls in main Controls data tree entry
2198    '''
2199    #patch
2200    if 'deriv type' not in data:
2201        data = {}
2202        data['deriv type'] = 'analytic Hessian'
2203        data['min dM/M'] = 0.0001
2204        data['shift factor'] = 1.
2205        data['max cyc'] = 3       
2206        data['F**2'] = False
2207    if 'shift factor' not in data:
2208        data['shift factor'] = 1.
2209    if 'max cyc' not in data:
2210        data['max cyc'] = 3
2211    if 'F**2' not in data:
2212        data['F**2'] = False
2213    if 'Author' not in data:
2214        data['Author'] = 'no name'
2215    if 'FreePrm1' not in data:
2216        data['FreePrm1'] = 'Sample humidity (%)'
2217    if 'FreePrm2' not in data:
2218        data['FreePrm2'] = 'Sample voltage (V)'
2219    if 'FreePrm3' not in data:
2220        data['FreePrm3'] = 'Applied load (MN)'
2221    if 'Copy2Next' not in data:
2222        data['Copy2Next'] = False
2223    if 'Reverse Seq' not in data:
2224        data['Reverse Seq'] = False
2225    if 'UsrReject' not in data:
2226        data['UsrReject'] = {'minF/sig':0,'MinExt':0.01,'MaxDF/F':20.,'MaxD':500.,'MinD':0.05}
2227    if 'HatomFix' not in data:
2228        data['HatomFix'] = False
2229   
2230    #end patch
2231
2232    def SeqSizer():
2233       
2234        def OnSelectData(event):
2235            choices = GetPatternTreeDataNames(G2frame,['PWDR','HKLF',])
2236            sel = []
2237            try:
2238                if 'Seq Data' in data:
2239                    for item in data['Seq Data']:
2240                        sel.append(choices.index(item))
2241                    sel = [choices.index(item) for item in data['Seq Data']]
2242            except ValueError:  #data changed somehow - start fresh
2243                sel = []
2244            dlg = G2G.G2MultiChoiceDialog(G2frame.dataFrame, 'Sequential refinement',
2245                'Select dataset to include',choices)
2246            dlg.SetSelections(sel)
2247            names = []
2248            if dlg.ShowModal() == wx.ID_OK:
2249                for sel in dlg.GetSelections():
2250                    names.append(choices[sel])
2251                data['Seq Data'] = names               
2252                G2frame.EnableSeqRefineMenu()
2253            dlg.Destroy()
2254            wx.CallAfter(UpdateControls,G2frame,data)
2255           
2256        def OnReverse(event):
2257            data['Reverse Seq'] = reverseSel.GetValue()
2258           
2259        def OnCopySel(event):
2260            data['Copy2Next'] = copySel.GetValue() 
2261                   
2262        seqSizer = wx.BoxSizer(wx.VERTICAL)
2263        dataSizer = wx.BoxSizer(wx.HORIZONTAL)
2264        dataSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Sequential Refinement: '),0,WACV)
2265        selSeqData = wx.Button(G2frame.dataDisplay,-1,label=' Select data')
2266        selSeqData.Bind(wx.EVT_BUTTON,OnSelectData)
2267        dataSizer.Add(selSeqData,0,WACV)
2268        SeqData = data.get('Seq Data',[])
2269        if not SeqData:
2270            lbl = ' (no data selected)'
2271        else:
2272            lbl = ' ('+str(len(SeqData))+' dataset(s) selected)'
2273
2274        dataSizer.Add(wx.StaticText(G2frame.dataDisplay,label=lbl),0,WACV)
2275        seqSizer.Add(dataSizer,0)
2276        if SeqData:
2277            selSizer = wx.BoxSizer(wx.HORIZONTAL)
2278            reverseSel = wx.CheckBox(G2frame.dataDisplay,-1,label=' Reverse order?')
2279            reverseSel.Bind(wx.EVT_CHECKBOX,OnReverse)
2280            reverseSel.SetValue(data['Reverse Seq'])
2281            selSizer.Add(reverseSel,0,WACV)
2282            copySel =  wx.CheckBox(G2frame.dataDisplay,-1,label=' Copy results to next histogram?')
2283            copySel.Bind(wx.EVT_CHECKBOX,OnCopySel)
2284            copySel.SetValue(data['Copy2Next'])
2285            selSizer.Add(copySel,0,WACV)
2286            seqSizer.Add(selSizer,0)
2287        return seqSizer
2288       
2289    def LSSizer():       
2290       
2291        def OnDerivType(event):
2292            data['deriv type'] = derivSel.GetValue()
2293            derivSel.SetValue(data['deriv type'])
2294            wx.CallAfter(UpdateControls,G2frame,data)
2295           
2296        def OnConvergence(event):
2297            try:
2298                value = max(1.e-9,min(1.0,float(Cnvrg.GetValue())))
2299            except ValueError:
2300                value = 0.0001
2301            data['min dM/M'] = value
2302            Cnvrg.SetValue('%.2g'%(value))
2303           
2304        def OnMaxCycles(event):
2305            data['max cyc'] = int(maxCyc.GetValue())
2306            maxCyc.SetValue(str(data['max cyc']))
2307                       
2308        def OnFactor(event):
2309            try:
2310                value = min(max(float(Factr.GetValue()),0.00001),100.)
2311            except ValueError:
2312                value = 1.0
2313            data['shift factor'] = value
2314            Factr.SetValue('%.5f'%(value))
2315           
2316        def OnFsqRef(event):
2317            data['F**2'] = fsqRef.GetValue()
2318           
2319        def OnHatomFix(event):
2320            data['HatomFix'] = Hfix.GetValue()
2321       
2322        def OnUsrRej(event):
2323            Obj = event.GetEventObject()
2324            item,limits = Indx[Obj]
2325            try:
2326                value = min(max(float(Obj.GetValue()),limits[0]),limits[1])
2327            except ValueError:
2328                value = data['UsrReject'][item]
2329            data['UsrReject'][item] = value
2330            Obj.SetValue('%.2f'%(value))
2331
2332        LSSizer = wx.FlexGridSizer(cols=4,vgap=5,hgap=5)
2333        LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Refinement derivatives: '),0,WACV)
2334        Choice=['analytic Jacobian','numeric','analytic Hessian']
2335        derivSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['deriv type'],choices=Choice,
2336            style=wx.CB_READONLY|wx.CB_DROPDOWN)
2337        derivSel.SetValue(data['deriv type'])
2338        derivSel.Bind(wx.EVT_COMBOBOX, OnDerivType)
2339           
2340        LSSizer.Add(derivSel,0,WACV)
2341        LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Min delta-M/M: '),0,WACV)
2342        Cnvrg = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2g'%(data['min dM/M']),style=wx.TE_PROCESS_ENTER)
2343        Cnvrg.Bind(wx.EVT_TEXT_ENTER,OnConvergence)
2344        Cnvrg.Bind(wx.EVT_KILL_FOCUS,OnConvergence)
2345        LSSizer.Add(Cnvrg,0,WACV)
2346        Indx = {}
2347        if 'Hessian' in data['deriv type']:
2348            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Max cycles: '),0,WACV)
2349            Choice = ['0','1','2','3','5','10','15','20']
2350            maxCyc = wx.ComboBox(parent=G2frame.dataDisplay,value=str(data['max cyc']),choices=Choice,
2351                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2352            maxCyc.SetValue(str(data['max cyc']))
2353            maxCyc.Bind(wx.EVT_COMBOBOX, OnMaxCycles)
2354            LSSizer.Add(maxCyc,0,WACV)
2355        else:
2356            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Initial shift factor: '),0,WACV)
2357            Factr = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.5f'%(data['shift factor']),style=wx.TE_PROCESS_ENTER)
2358            Factr.Bind(wx.EVT_TEXT_ENTER,OnFactor)
2359            Factr.Bind(wx.EVT_KILL_FOCUS,OnFactor)
2360            LSSizer.Add(Factr,0,WACV)
2361        if G2frame.Sngl:
2362            userReject = data['UsrReject']
2363            usrRej = {'minF/sig':[' Min obs/sig (0-5): ',[0,5], ],'MinExt':[' Min extinct. (0-.9): ',[0,.9],],
2364                'MaxDF/F':[' Max delt-F/sig (3-1000): ',[3.,1000.],],'MaxD':[' Max d-spacing (3-500): ',[3,500],],
2365                'MinD':[' Min d-spacing (0.1-2.0): ',[0.1,2.0],]}
2366
2367            fsqRef = wx.CheckBox(G2frame.dataDisplay,-1,label='Refine HKLF as F^2? ')
2368            fsqRef.SetValue(data['F**2'])
2369            fsqRef.Bind(wx.EVT_CHECKBOX,OnFsqRef)
2370            LSSizer.Add(fsqRef,0,WACV)
2371            LSSizer.Add((1,0),)
2372            for item in usrRej:
2373                LSSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=usrRej[item][0]),0,WACV)
2374                usrrej = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2f'%(userReject[item]),style=wx.TE_PROCESS_ENTER)
2375                Indx[usrrej] = [item,usrRej[item][1]]
2376                usrrej.Bind(wx.EVT_TEXT_ENTER,OnUsrRej)
2377                usrrej.Bind(wx.EVT_KILL_FOCUS,OnUsrRej)
2378                LSSizer.Add(usrrej,0,WACV)
2379#        Hfix = wx.CheckBox(G2frame.dataDisplay,-1,label='Regularize H atoms? ')
2380#        Hfix.SetValue(data['HatomFix'])
2381#        Hfix.Bind(wx.EVT_CHECKBOX,OnHatomFix)
2382#        LSSizer.Add(Hfix,0,WACV)   #for now
2383        return LSSizer
2384       
2385    def AuthSizer():
2386
2387        def OnAuthor(event):
2388            data['Author'] = auth.GetValue()
2389
2390        Author = data['Author']
2391        authSizer = wx.BoxSizer(wx.HORIZONTAL)
2392        authSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' CIF Author (last, first):'),0,WACV)
2393        auth = wx.TextCtrl(G2frame.dataDisplay,-1,value=Author,style=wx.TE_PROCESS_ENTER)
2394        auth.Bind(wx.EVT_TEXT_ENTER,OnAuthor)
2395        auth.Bind(wx.EVT_KILL_FOCUS,OnAuthor)
2396        authSizer.Add(auth,0,WACV)
2397        return authSizer
2398       
2399       
2400    if G2frame.dataDisplay:
2401        G2frame.dataDisplay.Destroy()
2402    if not G2frame.dataFrame.GetStatusBar():
2403        Status = G2frame.dataFrame.CreateStatusBar()
2404        Status.SetStatusText('')
2405    G2frame.dataFrame.SetLabel('Controls')
2406    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
2407    SetDataMenuBar(G2frame,G2frame.dataFrame.ControlsMenu)
2408    mainSizer = wx.BoxSizer(wx.VERTICAL)
2409    mainSizer.Add((5,5),0)
2410    mainSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Refinement Controls:'),0,WACV)   
2411    mainSizer.Add(LSSizer())
2412    mainSizer.Add((5,5),0)
2413    mainSizer.Add(SeqSizer())
2414    mainSizer.Add((5,5),0)
2415    mainSizer.Add(AuthSizer())
2416    mainSizer.Add((5,5),0)
2417       
2418    mainSizer.Layout()   
2419    G2frame.dataDisplay.SetSizer(mainSizer)
2420    G2frame.dataDisplay.SetSize(mainSizer.Fit(G2frame.dataFrame))
2421    G2frame.dataFrame.setSizePosLeft(mainSizer.Fit(G2frame.dataFrame))
2422     
2423################################################################################
2424#####  Comments
2425################################################################################           
2426       
2427def UpdateComments(G2frame,data):                   
2428
2429    if G2frame.dataDisplay:
2430        G2frame.dataDisplay.Destroy()
2431    G2frame.dataFrame.SetLabel('Comments')
2432    G2frame.dataDisplay = wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
2433        style=wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_DONTWRAP)
2434    for line in data:
2435        if '\n' not in line:
2436            G2frame.dataDisplay.AppendText(line+'\n')
2437        else:
2438            G2frame.dataDisplay.AppendText(line)
2439    G2frame.dataFrame.setSizePosLeft([400,250])
2440           
2441################################################################################
2442#####  Display of Sequential Results
2443################################################################################           
2444       
2445def UpdateSeqResults(G2frame,data,prevSize=None):
2446    """
2447    Called when the Sequential Results data tree entry is selected
2448    to show results from a sequential refinement.
2449   
2450    :param wx.Frame G2frame: main GSAS-II data tree windows
2451
2452    :param dict data: a dictionary containing the following items: 
2453
2454            * 'histNames' - list of histogram names in order as processed by Sequential Refinement
2455            * 'varyList' - list of variables - identical over all refinements in sequence
2456              note that this is the original list of variables, prior to processing
2457              constraints.
2458            * 'variableLabels' -- a dict of labels to be applied to each parameter
2459              (this is created as an empty dict if not present in data).
2460            * keyed by histName - dictionaries for all data sets processed, which contains:
2461
2462              * 'variables'- result[0] from leastsq call
2463              * 'varyList' - list of variables passed to leastsq call (not same as above)
2464              * 'sig' - esds for variables
2465              * 'covMatrix' - covariance matrix from individual refinement
2466              * 'title' - histogram name; same as dict item name
2467              * 'newAtomDict' - new atom parameters after shifts applied
2468              * 'newCellDict' - refined cell parameters after shifts to A0-A5 from Dij terms applied'
2469    """
2470
2471    def GetSampleParms():
2472        '''Make a dictionary of the sample parameters are not the same over the
2473        refinement series.
2474        '''
2475        if 'IMG' in histNames[0]:
2476            sampleParmDict = {'Sample load':[],}
2477        else:
2478            sampleParmDict = {'Temperature':[],'Pressure':[],'Time':[],
2479                'FreePrm1':[],'FreePrm2':[],'FreePrm3':[],'Omega':[],
2480                'Chi':[],'Phi':[],'Azimuth':[],}
2481        Controls = G2frame.PatternTree.GetItemPyData(
2482            GetPatternTreeItemId(G2frame,G2frame.root, 'Controls'))
2483        sampleParm = {}
2484        for name in histNames:
2485            if 'IMG' in name:
2486                for item in sampleParmDict:
2487                    sampleParmDict[item].append(data[name]['parmDict'].get(item,0))
2488            else:
2489                Id = GetPatternTreeItemId(G2frame,G2frame.root,name)
2490                sampleData = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,Id,'Sample Parameters'))
2491                for item in sampleParmDict:
2492                    sampleParmDict[item].append(sampleData.get(item,0))
2493        for item in sampleParmDict:
2494            frstValue = sampleParmDict[item][0]
2495            if np.any(np.array(sampleParmDict[item])-frstValue):
2496                if item.startswith('FreePrm'):
2497                    sampleParm[Controls[item]] = sampleParmDict[item]
2498                else:
2499                    sampleParm[item] = sampleParmDict[item]
2500        return sampleParm
2501
2502    def GetColumnInfo(col):
2503        '''returns column label, lists of values and errors (or None) for each column in the table
2504        for plotting. The column label is reformatted from Unicode to MatPlotLib encoding
2505        '''
2506        colName = G2frame.SeqTable.GetColLabelValue(col)
2507        plotName = variableLabels.get(colName,colName)
2508        plotName = plotSpCharFix(plotName)
2509        return plotName,colList[col],colSigs[col]
2510           
2511    def PlotSelect(event):
2512        'Plots a row (covariance) or column on double-click'
2513        cols = G2frame.dataDisplay.GetSelectedCols()
2514        rows = G2frame.dataDisplay.GetSelectedRows()
2515        if cols:
2516            G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
2517        elif rows:
2518            name = histNames[rows[0]]       #only does 1st one selected
2519            G2plt.PlotCovariance(G2frame,data[name])
2520        else:
2521            G2frame.ErrorDialog(
2522                'Select row or columns',
2523                'Nothing selected in table. Click on column or row label(s) to plot. N.B. Grid selection can be a bit funky.'
2524                )
2525           
2526    def OnPlotSelSeq(event):
2527        'plot the selected columns or row from menu command'
2528        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
2529        rows = G2frame.dataDisplay.GetSelectedRows()
2530        if cols:
2531            G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
2532        elif rows:
2533            name = histNames[rows[0]]       #only does 1st one selected
2534            G2plt.PlotCovariance(G2frame,data[name])
2535        else:
2536            G2frame.ErrorDialog(
2537                'Select columns',
2538                'No columns or rows selected in table. Click on row or column labels to select fields for plotting.'
2539                )
2540               
2541    def OnAveSelSeq(event):
2542        'average the selected columns from menu command'
2543        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
2544        if cols:
2545            for col in cols:
2546                ave = np.mean(GetColumnInfo(col)[1])
2547                sig = np.std(GetColumnInfo(col)[1])
2548                print ' Average for '+G2frame.SeqTable.GetColLabelValue(col)+': '+'%.6g'%(ave)+' +/- '+'%.6g'%(sig)
2549        else:
2550            G2frame.ErrorDialog(
2551                'Select columns',
2552                'No columns selected in table. Click on column labels to select fields for averaging.'
2553                )
2554               
2555    def OnRenameSelSeq(event):
2556        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
2557        colNames = [G2frame.SeqTable.GetColLabelValue(c) for c in cols]
2558        newNames = colNames[:]
2559        for i,name in enumerate(colNames):
2560            if name in variableLabels:
2561                newNames[i] = variableLabels[name]
2562        if not cols:
2563            G2frame.ErrorDialog('Select columns',
2564                'No columns selected in table. Click on column labels to select fields for rename.')
2565            return
2566        dlg = G2G.MultiStringDialog(G2frame.dataDisplay,'Set column names',colNames,newNames)
2567        if dlg.Show():
2568            newNames = dlg.GetValues()           
2569            variableLabels.update(dict(zip(colNames,newNames)))
2570        data['variableLabels'] = variableLabels
2571        dlg.Destroy()
2572        UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
2573        G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
2574           
2575    def OnReOrgSelSeq(event):
2576        'Reorder the columns'
2577        G2G.GetItemOrder(G2frame,VaryListChanges,vallookup,posdict)   
2578        UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
2579
2580    def OnSaveSelSeqCSV(event):
2581        'export the selected columns to a .csv file from menu command'
2582        OnSaveSelSeq(event,csv=True)
2583       
2584    def OnSaveSeqCSV(event):
2585        'export all columns to a .csv file from menu command'
2586        OnSaveSelSeq(event,csv=True,allcols=True)
2587       
2588    def OnSaveSelSeq(event,csv=False,allcols=False):
2589        'export the selected columns to a .txt or .csv file from menu command'
2590        def WriteCSV():
2591            def WriteList(headerItems):
2592                line = ''
2593                for lbl in headerItems:
2594                    if line: line += ','
2595                    line += '"'+lbl+'"'
2596                return line
2597            head = ['name']
2598            for col in cols:
2599                item = G2frame.SeqTable.GetColLabelValue(col)
2600                # get rid of labels that have Unicode characters
2601                if not all([ord(c) < 128 and ord(c) != 0 for c in item]): item = '?'
2602                if col in havesig:
2603                    head += [item,'esd-'+item]
2604                else:
2605                    head += [item]
2606            SeqFile.write(WriteList(head)+'\n')
2607            for row,name in enumerate(saveNames):
2608                line = '"'+saveNames[row]+'"'
2609                for col in cols:
2610                    if col in havesig:
2611                        line += ','+str(saveData[col][row])+','+str(saveSigs[col][row])
2612                    else:
2613                        line += ','+str(saveData[col][row])
2614                SeqFile.write(line+'\n')
2615        def WriteSeq():
2616            lenName = len(saveNames[0])
2617            line = %s  '%('name'.center(lenName))
2618            for col in cols:
2619                item = G2frame.SeqTable.GetColLabelValue(col)
2620                if col in havesig:
2621                    line += ' %12s %12s '%(item.center(12),'esd'.center(12))
2622                else:
2623                    line += ' %12s '%(item.center(12))
2624            SeqFile.write(line+'\n')
2625            for row,name in enumerate(saveNames):
2626                line = " '%s' "%(saveNames[row])
2627                for col in cols:
2628                    if col in havesig:
2629                        line += ' %12.6f %12.6f '%(saveData[col][row],saveSigs[col][row])
2630                    else:
2631                        line += ' %12.6f '%saveData[col][row]
2632                SeqFile.write(line+'\n')
2633
2634        # start of OnSaveSelSeq code
2635        if allcols:
2636            cols = range(G2frame.SeqTable.GetNumberCols())
2637        else:
2638            cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
2639        nrows = G2frame.SeqTable.GetNumberRows()
2640        if not cols:
2641            G2frame.ErrorDialog('Select columns',
2642                             'No columns selected in table. Click on column labels to select fields for output.')
2643            return
2644        saveNames = [G2frame.SeqTable.GetRowLabelValue(r) for r in range(nrows)]
2645        saveData = {}
2646        saveSigs = {}
2647        havesig = []
2648        for col in cols:
2649            name,vals,sigs = GetColumnInfo(col)
2650            saveData[col] = vals
2651            if sigs:
2652                havesig.append(col)
2653                saveSigs[col] = sigs
2654        if csv:
2655            wild = 'CSV output file (*.csv)|*.csv'
2656        else:
2657            wild = 'Text output file (*.txt)|*.txt'
2658        pth = G2G.GetExportPath(G2frame)
2659        dlg = wx.FileDialog(
2660            G2frame,
2661            'Choose text output file for your selection', pth, '', 
2662            wild,wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
2663        try:
2664            if dlg.ShowModal() == wx.ID_OK:
2665                SeqTextFile = dlg.GetPath()
2666                SeqTextFile = G2IO.FileDlgFixExt(dlg,SeqTextFile) 
2667                SeqFile = open(SeqTextFile,'w')
2668                if csv:
2669                    WriteCSV()
2670                else:
2671                    WriteSeq()
2672                SeqFile.close()
2673        finally:
2674            dlg.Destroy()
2675               
2676    def striphist(var,insChar=''):
2677        'strip a histogram number from a var name'
2678        sv = var.split(':')
2679        if len(sv) <= 1: return var
2680        if sv[1]:
2681            sv[1] = insChar
2682        return ':'.join(sv)
2683       
2684    def plotSpCharFix(lbl):
2685        'Change selected unicode characters to their matplotlib equivalent'
2686        for u,p in [
2687            (u'\u03B1',r'$\alpha$'),
2688            (u'\u03B2',r'$\beta$'),
2689            (u'\u03B3',r'$\gamma$'),
2690            (u'\u0394\u03C7',r'$\Delta\chi$'),
2691            ]:
2692            lbl = lbl.replace(u,p)
2693        return lbl
2694   
2695    def SelectXaxis():
2696        'returns a selected column number (or None) as the X-axis selection'
2697        ncols = G2frame.SeqTable.GetNumberCols()
2698        colNames = [G2frame.SeqTable.GetColLabelValue(r) for r in range(ncols)]
2699        dlg = G2G.G2SingleChoiceDialog(
2700            G2frame.dataDisplay,
2701            'Select x-axis parameter for plot or Cancel for sequence number',
2702            'Select X-axis',
2703            colNames)
2704        try:
2705            if dlg.ShowModal() == wx.ID_OK:
2706                col = dlg.GetSelection()
2707            else:
2708                col = None
2709        finally:
2710            dlg.Destroy()
2711        return col
2712   
2713    def EnablePseudoVarMenus():
2714        'Enables or disables the PseudoVar menu items that require existing defs'
2715        if Controls['SeqPseudoVars']:
2716            val = True
2717        else:
2718            val = False
2719        G2frame.dataFrame.SequentialPvars.Enable(wxDELSEQVAR,val)
2720        G2frame.dataFrame.SequentialPvars.Enable(wxEDITSEQVAR,val)
2721
2722    def DelPseudoVar(event):
2723        'Ask the user to select a pseudo var expression to delete'
2724        choices = Controls['SeqPseudoVars'].keys()
2725        selected = G2G.ItemSelector(
2726            choices,G2frame.dataFrame,
2727            multiple=True,
2728            title='Select expressions to remove',
2729            header='Delete expression')
2730        if selected is None: return
2731        for item in selected:
2732            del Controls['SeqPseudoVars'][choices[item]]
2733        if selected:
2734            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
2735
2736    def EditPseudoVar(event):
2737        'Edit an existing pseudo var expression'
2738        choices = Controls['SeqPseudoVars'].keys()
2739        if len(choices) == 1:
2740            selected = 0
2741        else:
2742            selected = G2G.ItemSelector(
2743                choices,G2frame.dataFrame,
2744                multiple=False,
2745                title='Select an expression to edit',
2746                header='Edit expression')
2747        if selected is not None:
2748            dlg = G2exG.ExpressionDialog(
2749                G2frame.dataDisplay,PSvarDict,
2750                Controls['SeqPseudoVars'][choices[selected]],
2751                header="Edit the PseudoVar expression",
2752                VarLabel="PseudoVar #"+str(selected+1),
2753                fit=False)
2754            newobj = dlg.Show(True)
2755            if newobj:
2756                calcobj = G2obj.ExpressionCalcObj(newobj)
2757                del Controls['SeqPseudoVars'][choices[selected]]
2758                Controls['SeqPseudoVars'][calcobj.eObj.expression] = newobj
2759                UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
2760       
2761    def AddNewPseudoVar(event):
2762        'Create a new pseudo var expression'
2763        dlg = G2exG.ExpressionDialog(
2764            G2frame.dataDisplay,PSvarDict,
2765            header='Enter an expression for a PseudoVar here',
2766            VarLabel = "New PseudoVar",
2767            fit=False)
2768        obj = dlg.Show(True)
2769        dlg.Destroy()
2770        if obj:
2771            calcobj = G2obj.ExpressionCalcObj(obj)
2772            Controls['SeqPseudoVars'][calcobj.eObj.expression] = obj
2773            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
2774           
2775    def AddNewDistPseudoVar(event):
2776        obj = None
2777        dlg = G2exG.BondDialog(
2778            G2frame.dataDisplay,Phases,PSvarDict,
2779            header='Select a Bond here',
2780            VarLabel = "New Bond")
2781        if dlg.ShowModal() == wx.ID_OK:
2782            pName,Oatom,Tatom = dlg.GetSelection()
2783            if Tatom:
2784                Phase = Phases[pName]
2785                General = Phase['General']
2786                cx,ct = General['AtomPtrs'][:2]
2787                pId = Phase['pId']
2788                SGData = General['SGData']
2789                sB = Tatom.find('(')+1
2790                symNo = 0
2791                if sB:
2792                    sF = Tatom.find(')')
2793                    symNo = int(Tatom[sB:sF])
2794                cellNo = [0,0,0]
2795                cB = Tatom.find('[')
2796                if cB>0:
2797                    cF = Tatom.find(']')+1
2798                    cellNo = eval(Tatom[cB:cF])
2799                Atoms = Phase['Atoms']
2800                aNames = [atom[ct-1] for atom in Atoms]
2801                oId = aNames.index(Oatom)
2802                tId = aNames.index(Tatom.split(' +')[0])
2803                # create an expression object
2804                obj = G2obj.ExpressionObj()
2805                obj.expression = 'Dist(%s,\n%s)'%(Oatom,Tatom.split(' d=')[0].replace(' ',''))
2806                obj.distance_dict = {'pId':pId,'SGData':SGData,'symNo':symNo,'cellNo':cellNo}
2807                obj.distance_atoms = [oId,tId]
2808        else: 
2809            dlg.Destroy()
2810            return
2811        dlg.Destroy()
2812        if obj:
2813            Controls['SeqPseudoVars'][obj.expression] = obj
2814            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
2815
2816    def AddNewAnglePseudoVar(event):
2817        obj = None
2818        dlg = G2exG.AngleDialog(
2819            G2frame.dataDisplay,Phases,PSvarDict,
2820            header='Enter an Angle here',
2821            VarLabel = "New Angle")
2822        if dlg.ShowModal() == wx.ID_OK:
2823            pName,Oatom,Tatoms = dlg.GetSelection()
2824            if Tatoms:
2825                Phase = Phases[pName]
2826                General = Phase['General']
2827                cx,ct = General['AtomPtrs'][:2]
2828                pId = Phase['pId']
2829                SGData = General['SGData']
2830                Atoms = Phase['Atoms']
2831                aNames = [atom[ct-1] for atom in Atoms]
2832                tIds = []
2833                symNos = []
2834                cellNos = []
2835                oId = aNames.index(Oatom)
2836                Tatoms = Tatoms.split(';')
2837                for Tatom in Tatoms:
2838                    sB = Tatom.find('(')+1
2839                    symNo = 0
2840                    if sB:
2841                        sF = Tatom.find(')')
2842                        symNo = int(Tatom[sB:sF])
2843                    symNos.append(symNo)
2844                    cellNo = [0,0,0]
2845                    cB = Tatom.find('[')
2846                    if cB>0:
2847                        cF = Tatom.find(']')+1
2848                        cellNo = eval(Tatom[cB:cF])
2849                    cellNos.append(cellNo)
2850                    tIds.append(aNames.index(Tatom.split('+')[0]))
2851                # create an expression object
2852                obj = G2obj.ExpressionObj()
2853                obj.expression = 'Angle(%s,%s,\n%s)'%(Tatoms[0],Oatom,Tatoms[1])
2854                obj.angle_dict = {'pId':pId,'SGData':SGData,'symNo':symNos,'cellNo':cellNos}
2855                obj.angle_atoms = [oId,tIds]
2856        else: 
2857            dlg.Destroy()
2858            return
2859        dlg.Destroy()
2860        if obj:
2861            Controls['SeqPseudoVars'][obj.expression] = obj
2862            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
2863           
2864    def UpdateParmDict(parmDict):
2865        '''generate the atom positions and the direct & reciprocal cell values,
2866        because they might be needed to evaluate the pseudovar
2867        '''
2868        Ddict = dict(zip(['D11','D22','D33','D12','D13','D23'],
2869                         ['A'+str(i) for i in range(6)])
2870                     )
2871        delList = []
2872        phaselist = []
2873        for item in parmDict: 
2874            if ':' not in item: continue
2875            key = item.split(':')
2876            if len(key) < 3: continue
2877            # remove the dA[xyz] terms, they would only bring confusion
2878            if key[2].startswith('dA'):
2879                delList.append(item)
2880            # compute and update the corrected reciprocal cell terms using the Dij values
2881            elif key[2] in Ddict:
2882                if key[0] not in phaselist: phaselist.append(key[0])
2883                akey = key[0]+'::'+Ddict[key[2]]
2884                parmDict[akey] -= parmDict[item]
2885                delList.append(item)
2886        for item in delList:
2887            del parmDict[item]               
2888        for i in phaselist:
2889            pId = int(i)
2890            # apply cell symmetry
2891            A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata[pId],parmDict,zeroDict[pId])
2892            # convert to direct cell & add the unique terms to the dictionary
2893            for i,val in enumerate(G2lat.A2cell(A)):
2894                if i in uniqCellIndx[pId]:
2895                    lbl = str(pId)+'::'+cellUlbl[i]
2896                    parmDict[lbl] = val
2897            lbl = str(pId)+'::'+'vol'
2898            parmDict[lbl] = G2lat.calc_V(A)
2899        return parmDict
2900
2901    def EvalPSvarDeriv(calcobj,parmDict,sampleDict,var,ESD):
2902        '''Evaluate an expression derivative with respect to a
2903        GSAS-II variable name.
2904
2905        Note this likely could be faster if the loop over calcobjs were done
2906        inside after the Dict was created.
2907        '''
2908        step = ESD/10
2909        Ddict = dict(zip(['D11','D22','D33','D12','D13','D23'],
2910                         ['A'+str(i) for i in range(6)])
2911                     )
2912        results = []
2913        phaselist = []
2914        VparmDict = sampleDict.copy()
2915        for incr in step,-step:
2916            VparmDict.update(parmDict.copy())           
2917            # as saved, the parmDict has updated 'A[xyz]' values, but 'dA[xyz]'
2918            # values are not zeroed: fix that!
2919            VparmDict.update({item:0.0 for item in parmDict if 'dA' in item})
2920            VparmDict[var] += incr
2921            G2mv.Dict2Map(VparmDict,[]) # apply constraints
2922            # generate the atom positions and the direct & reciprocal cell values now, because they might
2923            # needed to evaluate the pseudovar
2924            for item in VparmDict:
2925                if item in sampleDict:
2926                    continue 
2927                if ':' not in item: continue
2928                key = item.split(':')
2929                if len(key) < 3: continue
2930                # apply any new shifts to atom positions
2931                if key[2].startswith('dA'):
2932                    VparmDict[''.join(item.split('d'))] += VparmDict[item]
2933                    VparmDict[item] = 0.0
2934                # compute and update the corrected reciprocal cell terms using the Dij values
2935                if key[2] in Ddict:
2936                    if key[0] not in phaselist: phaselist.append(key[0])
2937                    akey = key[0]+'::'+Ddict[key[2]]
2938                    VparmDict[akey] -= VparmDict[item]
2939            for i in phaselist:
2940                pId = int(i)
2941                # apply cell symmetry
2942                A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata[pId],VparmDict,zeroDict[pId])
2943                # convert to direct cell & add the unique terms to the dictionary
2944                for i,val in enumerate(G2lat.A2cell(A)):
2945                    if i in uniqCellIndx[pId]:
2946                        lbl = str(pId)+'::'+cellUlbl[i]
2947                        VparmDict[lbl] = val
2948                lbl = str(pId)+'::'+'vol'
2949                VparmDict[lbl] = G2lat.calc_V(A)
2950            # dict should be fully updated, use it & calculate
2951            calcobj.SetupCalc(VparmDict)
2952            results.append(calcobj.EvalExpression())
2953        return (results[0] - results[1]) / (2.*step)
2954       
2955    def EnableParFitEqMenus():
2956        'Enables or disables the Parametric Fit menu items that require existing defs'
2957        if Controls['SeqParFitEqList']:
2958            val = True
2959        else:
2960            val = False
2961        G2frame.dataFrame.SequentialPfit.Enable(wxDELPARFIT,val)
2962        G2frame.dataFrame.SequentialPfit.Enable(wxEDITPARFIT,val)
2963        G2frame.dataFrame.SequentialPfit.Enable(wxDOPARFIT,val)
2964
2965    def ParEqEval(Values,calcObjList,varyList):
2966        '''Evaluate the parametric expression(s)
2967        :param list Values: a list of values for each variable parameter
2968        :param list calcObjList: a list of :class:`GSASIIobj.ExpressionCalcObj`
2969          expression objects to evaluate
2970        :param list varyList: a list of variable names for each value in Values
2971        '''
2972        result = []
2973        for calcobj in calcObjList:
2974            calcobj.UpdateVars(varyList,Values)
2975            result.append((calcobj.depVal-calcobj.EvalExpression())/calcobj.depSig)
2976        return result
2977
2978    def DoParEqFit(event,eqObj=None):
2979        'Parametric fit minimizer'
2980        varyValueDict = {} # dict of variables and their initial values
2981        calcObjList = [] # expression objects, ready to go for each data point
2982        if eqObj is not None:
2983            eqObjList = [eqObj,]
2984        else:
2985            eqObjList = Controls['SeqParFitEqList']
2986        UseFlags = G2frame.SeqTable.GetColValues(0)         
2987        for obj in eqObjList:
2988            expr = obj.expression
2989            # assemble refined vars for this equation
2990            varyValueDict.update({var:val for var,val in obj.GetVariedVarVal()})
2991            # lookup dependent var position
2992            depVar = obj.GetDepVar()
2993            if depVar in colLabels:
2994                indx = colLabels.index(depVar)
2995            else:
2996                raise Exception('Dependent variable '+depVar+' not found')
2997            # assemble a list of the independent variables
2998            indepVars = obj.GetIndependentVars()
2999            # loop over each datapoint
3000            for j,row in enumerate(zip(*colList)):
3001                if not UseFlags[j]: continue
3002                # assemble equations to fit
3003                calcobj = G2obj.ExpressionCalcObj(obj)
3004                # prepare a dict of needed independent vars for this expression
3005                indepVarDict = {var:row[i] for i,var in enumerate(colLabels) if var in indepVars}
3006                calcobj.SetupCalc(indepVarDict)               
3007                # values and sigs for current value of dependent var
3008                calcobj.depVal = row[indx]
3009                calcobj.depSig = colSigs[indx][j]
3010                calcObjList.append(calcobj)
3011        # varied parameters
3012        varyList = varyValueDict.keys()
3013        values = varyValues = [varyValueDict[key] for key in varyList]
3014        if not varyList:
3015            print 'no variables to refine!'
3016            return
3017        try:
3018            result = so.leastsq(ParEqEval,varyValues,full_output=True,   #ftol=Ftol,
3019                                args=(calcObjList,varyList)
3020                                )
3021            values = result[0]
3022            covar = result[1]
3023            if covar is None:
3024                raise Exception
3025            esdDict = {}
3026            for i,avar in enumerate(varyList):
3027                esdDict[avar] = np.sqrt(covar[i,i])
3028        except:
3029            print('====> Fit failed')
3030            return
3031        print('==== Fit Results ====')
3032        for obj in eqObjList:
3033            obj.UpdateVariedVars(varyList,values)
3034            ind = '      '
3035            print('  '+obj.GetDepVar()+' = '+obj.expression)
3036            for var in obj.assgnVars:
3037                print(ind+var+' = '+obj.assgnVars[var])
3038            for var in obj.freeVars:
3039                avar = "::"+obj.freeVars[var][0]
3040                val = obj.freeVars[var][1]
3041                if obj.freeVars[var][2]:
3042                    print(ind+var+' = '+avar + " = " + G2mth.ValEsd(val,esdDict[avar]))
3043                else:
3044                    print(ind+var+' = '+avar + " =" + G2mth.ValEsd(val,0))
3045        # create a plot for each parametric variable
3046        for fitnum,obj in enumerate(eqObjList):
3047            calcobj = G2obj.ExpressionCalcObj(obj)
3048            # lookup dependent var position
3049            indx = colLabels.index(obj.GetDepVar())
3050            # assemble a list of the independent variables
3051            indepVars = obj.GetIndependentVars()           
3052            # loop over each datapoint
3053            fitvals = []
3054            for j,row in enumerate(zip(*colList)):
3055                calcobj.SetupCalc(
3056                    {var:row[i] for i,var in enumerate(colLabels) if var in indepVars}
3057                    )
3058                fitvals.append(calcobj.EvalExpression())
3059            G2plt.PlotSelectedSequence(
3060                G2frame,[indx],GetColumnInfo,SelectXaxis,
3061                fitnum,fitvals)
3062
3063    def SingleParEqFit(eqObj):
3064        DoParEqFit(None,eqObj)
3065
3066    def DelParFitEq(event):
3067        'Ask the user to select function to delete'
3068        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
3069        selected = G2G.ItemSelector(
3070            txtlst,G2frame.dataFrame,
3071            multiple=True,
3072            title='Select a parametric equation(s) to remove',
3073            header='Delete equation')
3074        if selected is None: return
3075        Controls['SeqParFitEqList'] = [obj for i,obj in enumerate(Controls['SeqParFitEqList']) if i not in selected]
3076        EnableParFitEqMenus()
3077        if Controls['SeqParFitEqList']: DoParEqFit(event)
3078       
3079    def EditParFitEq(event):
3080        'Edit an existing parametric equation'
3081        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
3082        if len(txtlst) == 1:
3083            selected = 0
3084        else:
3085            selected = G2G.ItemSelector(
3086                txtlst,G2frame.dataFrame,
3087                multiple=False,
3088                title='Select a parametric equation to edit',
3089                header='Edit equation')
3090        if selected is not None:
3091            dlg = G2exG.ExpressionDialog(
3092                G2frame.dataDisplay,indepVarDict,
3093                Controls['SeqParFitEqList'][selected],
3094                depVarDict=depVarDict,
3095                header="Edit the formula for this minimization function",
3096                ExtraButton=['Fit',SingleParEqFit])
3097            newobj = dlg.Show(True)
3098            if newobj:
3099                calcobj = G2obj.ExpressionCalcObj(newobj)
3100                Controls['SeqParFitEqList'][selected] = newobj
3101                EnableParFitEqMenus()
3102            if Controls['SeqParFitEqList']: DoParEqFit(event)
3103
3104    def AddNewParFitEq(event):
3105        'Create a new parametric equation to be fit to sequential results'
3106
3107        # compile the variable names used in previous freevars to avoid accidental name collisions
3108        usedvarlist = []
3109        for obj in Controls['SeqParFitEqList']:
3110            for var in obj.freeVars:
3111                if obj.freeVars[var][0] not in usedvarlist: usedvarlist.append(obj.freeVars[var][0])
3112
3113        dlg = G2exG.ExpressionDialog(
3114            G2frame.dataDisplay,indepVarDict,
3115            depVarDict=depVarDict,
3116            header='Define an equation to minimize in the parametric fit',
3117            ExtraButton=['Fit',SingleParEqFit],
3118            usedVars=usedvarlist)
3119        obj = dlg.Show(True)
3120        dlg.Destroy()
3121        if obj:
3122            Controls['SeqParFitEqList'].append(obj)
3123            EnableParFitEqMenus()
3124            if Controls['SeqParFitEqList']: DoParEqFit(event)
3125               
3126    def CopyParFitEq(event):
3127        'Copy an existing parametric equation to be fit to sequential results'
3128        # compile the variable names used in previous freevars to avoid accidental name collisions
3129        usedvarlist = []
3130        for obj in Controls['SeqParFitEqList']:
3131            for var in obj.freeVars:
3132                if obj.freeVars[var][0] not in usedvarlist: usedvarlist.append(obj.freeVars[var][0])
3133        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
3134        if len(txtlst) == 1:
3135            selected = 0
3136        else:
3137            selected = G2G.ItemSelector(
3138                txtlst,G2frame.dataFrame,
3139                multiple=False,
3140                title='Select a parametric equation to copy',
3141                header='Copy equation')
3142        if selected is not None:
3143            newEqn = copy.deepcopy(Controls['SeqParFitEqList'][selected])
3144            for var in newEqn.freeVars:
3145                newEqn.freeVars[var][0] = G2obj.MakeUniqueLabel(newEqn.freeVars[var][0],usedvarlist)
3146            dlg = G2exG.ExpressionDialog(
3147                G2frame.dataDisplay,indepVarDict,
3148                newEqn,
3149                depVarDict=depVarDict,
3150                header="Edit the formula for this minimization function",
3151                ExtraButton=['Fit',SingleParEqFit])
3152            newobj = dlg.Show(True)
3153            if newobj:
3154                calcobj = G2obj.ExpressionCalcObj(newobj)
3155                Controls['SeqParFitEqList'].append(newobj)
3156                EnableParFitEqMenus()
3157            if Controls['SeqParFitEqList']: DoParEqFit(event)
3158                                           
3159    def GridSetToolTip(row,col):
3160        '''Routine to show standard uncertainties for each element in table
3161        as a tooltip
3162        '''
3163        if colSigs[col]:
3164            return u'\u03c3 = '+str(colSigs[col][row])
3165        return ''
3166       
3167    def GridColLblToolTip(col):
3168        '''Define a tooltip for a column. This will be the user-entered value
3169        (from data['variableLabels']) or the default name
3170        '''
3171        if col < 0 or col > len(colLabels):
3172            print 'Illegal column #',col
3173            return
3174        var = colLabels[col]
3175        return variableLabels.get(var,G2obj.fmtVarDescr(var))
3176       
3177    def SetLabelString(event):
3178        '''Define or edit the label for a column in the table, to be used
3179        as a tooltip and for plotting
3180        '''
3181        col = event.GetCol()
3182        if col < 0 or col > len(colLabels):
3183            return
3184        var = colLabels[col]
3185        lbl = variableLabels.get(var,G2obj.fmtVarDescr(var))
3186        dlg = G2G.SingleStringDialog(G2frame.dataFrame,'Set variable label',
3187                                 'Set a new name for variable '+var,lbl,size=(400,-1))
3188        if dlg.Show():
3189            variableLabels[var] = dlg.GetValue()
3190        dlg.Destroy()
3191       
3192    #def GridRowLblToolTip(row): return 'Row ='+str(row)
3193   
3194    # lookup table for unique cell parameters by symmetry
3195    cellGUIlist = [
3196        [['m3','m3m'],(0,)],
3197        [['3R','3mR'],(0,3)],
3198        [['3','3m1','31m','6/m','6/mmm','4/m','4/mmm'],(0,2)],
3199        [['mmm'],(0,1,2)],
3200        [['2/m'+'a'],(0,1,2,3)],
3201        [['2/m'+'b'],(0,1,2,4)],
3202        [['2/m'+'c'],(0,1,2,5)],
3203        [['-1'],(0,1,2,3,4,5)],
3204        ]
3205    # cell labels
3206    cellUlbl = ('a','b','c',u'\u03B1',u'\u03B2',u'\u03B3') # unicode a,b,c,alpha,beta,gamma
3207
3208    #======================================================================
3209    # start processing sequential results here (UpdateSeqResults)
3210    #======================================================================
3211    if not data:
3212        print 'No sequential refinement results'
3213        return
3214    variableLabels = data.get('variableLabels',{})
3215    data['variableLabels'] = variableLabels
3216    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
3217    Controls = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.root,'Controls'))
3218    # create a place to store Pseudo Vars & Parametric Fit functions, if not present
3219    if 'SeqPseudoVars' not in Controls: Controls['SeqPseudoVars'] = {}
3220    if 'SeqParFitEqList' not in Controls: Controls['SeqParFitEqList'] = []
3221    histNames = data['histNames']
3222    if G2frame.dataDisplay:
3223        G2frame.dataDisplay.Destroy()
3224    if not G2frame.dataFrame.GetStatusBar():
3225        Status = G2frame.dataFrame.CreateStatusBar()
3226        Status.SetStatusText("Select column to export; Double click on column to plot data; on row for Covariance")
3227    sampleParms = GetSampleParms()
3228
3229    # make dict of varied atom coords keyed by absolute position
3230    newAtomDict = data[histNames[0]].get('newAtomDict',{}) # dict with atom positions; relative & absolute
3231    # Possible error: the next might need to be data[histNames[0]]['varyList']
3232    # error will arise if there constraints on coordinates?
3233    atomLookup = {newAtomDict[item][0]:item for item in newAtomDict if item in data['varyList']}
3234   
3235    # make dict of varied cell parameters equivalents
3236    ESDlookup = {} # provides the Dij term for each Ak term (where terms are refined)
3237    Dlookup = {} # provides the Ak term for each Dij term (where terms are refined)
3238    # N.B. These Dij vars are missing a histogram #
3239    newCellDict = data[histNames[0]].get('newCellDict',{})
3240    for item in newCellDict:
3241        if item in data['varyList']:
3242            ESDlookup[newCellDict[item][0]] = item
3243            Dlookup[item] = newCellDict[item][0]
3244    # add coordinate equivalents to lookup table
3245    for parm in atomLookup:
3246        Dlookup[atomLookup[parm]] = parm
3247        ESDlookup[parm] = atomLookup[parm]
3248
3249    # get unit cell & symmetry for all phases & initial stuff for later use
3250    RecpCellTerms = {}
3251    SGdata = {}
3252    uniqCellIndx = {}
3253    initialCell = {}
3254    RcellLbls = {}
3255    zeroDict = {}
3256    Rcelldict = {}
3257    for phase in Phases:
3258        phasedict = Phases[phase]
3259        pId = phasedict['pId']
3260        pfx = str(pId)+'::' # prefix for A values from phase
3261        RcellLbls[pId] = [pfx+'A'+str(i) for i in range(6)]
3262        RecpCellTerms[pId] = G2lat.cell2A(phasedict['General']['Cell'][1:7])
3263        zeroDict[pId] = dict(zip(RcellLbls[pId],6*[0.,]))
3264        SGdata[pId] = phasedict['General']['SGData']
3265        Rcelldict.update({lbl:val for lbl,val in zip(RcellLbls[pId],RecpCellTerms[pId])})
3266        laue = SGdata[pId]['SGLaue']
3267        if laue == '2/m':
3268            laue += SGdata[pId]['SGUniq']
3269        for symlist,celllist in cellGUIlist:
3270            if laue in symlist:
3271                uniqCellIndx[pId] = celllist
3272                break
3273        else: # should not happen
3274            uniqCellIndx[pId] = range(6)
3275        for i in uniqCellIndx[pId]:
3276            initialCell[str(pId)+'::A'+str(i)] =  RecpCellTerms[pId][i]
3277
3278    SetDataMenuBar(G2frame,G2frame.dataFrame.SequentialMenu)
3279    G2frame.dataFrame.SetLabel('Sequential refinement results')
3280    if not G2frame.dataFrame.GetStatusBar():
3281        Status = G2frame.dataFrame.CreateStatusBar()
3282        Status.SetStatusText('')
3283    G2frame.dataFrame.Bind(wx.EVT_MENU, OnRenameSelSeq, id=wxID_RENAMESEQSEL)
3284    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSelSeq, id=wxID_SAVESEQSEL)
3285    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSelSeqCSV, id=wxID_SAVESEQSELCSV)
3286    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSeqCSV, id=wxID_SAVESEQCSV)
3287    G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlotSelSeq, id=wxID_PLOTSEQSEL)
3288    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAveSelSeq, id=wxID_AVESEQSEL)
3289    G2frame.dataFrame.Bind(wx.EVT_MENU, OnReOrgSelSeq, id=wxID_ORGSEQSEL)
3290    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewPseudoVar, id=wxADDSEQVAR)
3291    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewDistPseudoVar, id=wxADDSEQDIST)
3292    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewAnglePseudoVar, id=wxADDSEQANGLE)
3293    G2frame.dataFrame.Bind(wx.EVT_MENU, DelPseudoVar, id=wxDELSEQVAR)
3294    G2frame.dataFrame.Bind(wx.EVT_MENU, EditPseudoVar, id=wxEDITSEQVAR)
3295    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewParFitEq, id=wxADDPARFIT)
3296    G2frame.dataFrame.Bind(wx.EVT_MENU, CopyParFitEq, id=wxCOPYPARFIT)
3297    G2frame.dataFrame.Bind(wx.EVT_MENU, DelParFitEq, id=wxDELPARFIT)
3298    G2frame.dataFrame.Bind(wx.EVT_MENU, EditParFitEq, id=wxEDITPARFIT)
3299    G2frame.dataFrame.Bind(wx.EVT_MENU, DoParEqFit, id=wxDOPARFIT)
3300    EnablePseudoVarMenus()
3301    EnableParFitEqMenus()
3302
3303    # scan for locations where the variables change
3304    VaryListChanges = [] # histograms where there is a change
3305    combinedVaryList = []
3306    firstValueDict = {}
3307    vallookup = {}
3308    posdict = {}
3309    prevVaryList = []
3310    for i,name in enumerate(histNames):
3311        for var,val,sig in zip(data[name]['varyList'],data[name]['variables'],data[name]['sig']):
3312            svar = striphist(var,'*') # wild-carded
3313            if svar not in combinedVaryList:
3314                # add variables to list as they appear
3315                combinedVaryList.append(svar)
3316                firstValueDict[svar] = (val,sig)
3317        if prevVaryList != data[name]['varyList']: # this refinement has a different refinement list from previous
3318            prevVaryList = data[name]['varyList']
3319            vallookup[name] = dict(zip(data[name]['varyList'],data[name]['variables']))
3320            posdict[name] = {}
3321            for var in data[name]['varyList']:
3322                svar = striphist(var,'*')
3323                posdict[name][combinedVaryList.index(svar)] = svar
3324            VaryListChanges.append(name)
3325    if len(VaryListChanges) > 1:
3326        G2frame.dataFrame.SequentialFile.Enable(wxID_ORGSEQSEL,True)
3327    else:
3328        G2frame.dataFrame.SequentialFile.Enable(wxID_ORGSEQSEL,False)
3329    #-----------------------------------------------------------------------------------
3330    # build up the data table by columns -----------------------------------------------
3331    nRows = len(histNames)
3332    colList = [nRows*[True]]
3333    colSigs = [None]
3334    colLabels = ['Use']
3335    Types = [wg.GRID_VALUE_BOOL]
3336    # start with Rwp values
3337    if 'IMG ' not in histNames[0][:4]:
3338        colList += [[data[name]['Rvals']['Rwp'] for name in histNames]]
3339        colSigs += [None]
3340        colLabels += ['Rwp']
3341        Types += [wg.GRID_VALUE_FLOAT+':10,3',]
3342    # add % change in Chi^2 in last cycle
3343    if histNames[0][:4] not in ['SASD','IMG '] and Controls.get('ShowCell'):
3344        colList += [[100.*data[name]['Rvals'].get('DelChi2',-1) for name in histNames]]
3345        colSigs += [None]
3346        colLabels += [u'\u0394\u03C7\u00B2 (%)']
3347        Types += [wg.GRID_VALUE_FLOAT,]
3348    deltaChiCol = len(colLabels)-1
3349    # add changing sample parameters to table
3350    for key in sampleParms:
3351        colList += [sampleParms[key]]
3352        colSigs += [None]
3353        colLabels += [key]
3354        Types += [wg.GRID_VALUE_FLOAT,]
3355    sampleDict = {}
3356    for i,name in enumerate(histNames):
3357        sampleDict[name] = dict(zip(sampleParms.keys(),[sampleParms[key][i] for key in sampleParms.keys()])) 
3358    # add unique cell parameters TODO: review this where the cell symmetry changes (when possible)
3359    if Controls.get('ShowCell',False):
3360        for pId in sorted(RecpCellTerms):
3361            pfx = str(pId)+'::' # prefix for A values from phase
3362            cells = []
3363            cellESDs = []
3364            colLabels += [pfx+cellUlbl[i] for i in uniqCellIndx[pId]]
3365            colLabels += [pfx+'Vol']
3366            Types += (1+len(uniqCellIndx[pId]))*[wg.GRID_VALUE_FLOAT,]
3367            for name in histNames:
3368                covData = {
3369                    'varyList': [Dlookup.get(striphist(v),v) for v in data[name]['varyList']],
3370                    'covMatrix': data[name]['covMatrix']
3371                    }
3372                A = RecpCellTerms[pId][:] # make copy of starting A values
3373                # update with refined values
3374                for i in range(6):
3375                    var = str(pId)+'::A'+str(i)
3376                    if var in ESDlookup:
3377                        val = data[name]['newCellDict'][ESDlookup[var]][1] # get refined value
3378                        A[i] = val # override with updated value
3379                # apply symmetry
3380                Albls = [pfx+'A'+str(i) for i in range(6)]
3381                cellDict = dict(zip(Albls,A))
3382                A,zeros = G2stIO.cellFill(pfx,SGdata[pId],cellDict,zeroDict[pId])
3383                # convert to direct cell & add only unique values to table
3384                c = G2lat.A2cell(A)
3385                vol = G2lat.calc_V(A)
3386                cE = G2stIO.getCellEsd(pfx,SGdata[pId],A,covData)
3387                cells += [[c[i] for i in uniqCellIndx[pId]]+[vol]]
3388                cellESDs += [[cE[i] for i in uniqCellIndx[pId]]+[cE[-1]]]
3389            colList += zip(*cells)
3390            colSigs += zip(*cellESDs)
3391    # sort out the variables in their selected order
3392    varcols = 0
3393    for d in posdict.itervalues():
3394        varcols = max(varcols,max(d.keys())+1)
3395    # get labels for each column
3396    for i in range(varcols):
3397        lbl = ''
3398        for h in VaryListChanges:
3399            if posdict[h].get(i):
3400                if posdict[h].get(i) in lbl: continue
3401                if lbl != "": lbl += '/'
3402                lbl += posdict[h].get(i)
3403        colLabels.append(lbl)
3404    Types += varcols*[wg.GRID_VALUE_FLOAT]
3405    vals = []
3406    esds = []
3407    varsellist = None        # will be a list of variable names in the order they are selected to appear
3408    # tabulate values for each hist, leaving None for blank columns
3409    for name in histNames:
3410        if name in posdict:
3411            varsellist = [posdict[name].get(i) for i in range(varcols)]
3412            # translate variable names to how they will be used in the headings
3413            vs = [striphist(v,'*') for v in data[name]['varyList']]
3414            # determine the index for each column (or None) in the data[]['variables'] and ['sig'] lists
3415            sellist = [vs.index(v) if v is not None else None for v in varsellist]
3416            #sellist = [i if striphist(v,'*') in varsellist else None for i,v in enumerate(data[name]['varyList'])]
3417        if not varsellist: raise Exception()
3418        vals.append([data[name]['variables'][s] if s is not None else None for s in sellist])
3419        esds.append([data[name]['sig'][s] if s is not None else None for s in sellist])
3420        #GSASIIpath.IPyBreak()
3421    colList += zip(*vals)
3422    colSigs += zip(*esds)
3423               
3424    # tabulate constrained variables, removing histogram numbers if needed
3425    # from parameter label
3426    depValDict = {}
3427    depSigDict = {}
3428    for name in histNames:
3429        for var in data[name].get('depParmDict',{}):
3430            val,sig = data[name]['depParmDict'][var]
3431            svar = striphist(var,'*')
3432            if svar not in depValDict:
3433               depValDict[svar] = [val]
3434               depSigDict[svar] = [sig]
3435            else:
3436               depValDict[svar].append(val)
3437               depSigDict[svar].append(sig)
3438    # add the dependent constrained variables to the table
3439    for var in sorted(depValDict):
3440        if len(depValDict[var]) != len(histNames): continue
3441        colLabels.append(var)
3442        Types += [wg.GRID_VALUE_FLOAT,]
3443        colSigs += [depSigDict[var]]
3444        colList += [depValDict[var]]
3445
3446    # add atom parameters to table
3447    colLabels += atomLookup.keys()
3448    Types += len(atomLookup)*[wg.GRID_VALUE_FLOAT]
3449    for parm in sorted(atomLookup):
3450        colList += [[data[name]['newAtomDict'][atomLookup[parm]][1] for name in histNames]]
3451        if atomLookup[parm] in data[histNames[0]]['varyList']:
3452            col = data[histNames[0]]['varyList'].index(atomLookup[parm])
3453            colSigs += [[data[name]['sig'][col] for name in histNames]]
3454        else:
3455            colSigs += [None] # should not happen
3456    # evaluate Pseudovars, their ESDs and add them to grid
3457    for expr in Controls['SeqPseudoVars']:
3458        obj = Controls['SeqPseudoVars'][expr]
3459        calcobj = G2obj.ExpressionCalcObj(obj)
3460        valList = []
3461        esdList = []
3462        for seqnum,name in enumerate(histNames):
3463            sigs = data[name]['sig']
3464            G2mv.InitVars()
3465            parmDict = data[name].get('parmDict')
3466            badVary = data[name].get('badVary',[])
3467            constraintInfo = data[name].get('constraintInfo',[[],[],{},[],seqnum])
3468            groups,parmlist,constrDict,fixedList,ihst = constraintInfo
3469            varyList = data[name]['varyList']
3470            parmDict = data[name]['parmDict']
3471            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmDict,SeqHist=ihst)
3472            if 'Dist' in expr:
3473                derivs = G2mth.CalcDistDeriv(obj.distance_dict,obj.distance_atoms, parmDict)
3474                pId = obj.distance_dict['pId']
3475                aId,bId = obj.distance_atoms
3476                varyNames = ['%d::dA%s:%d'%(pId,ip,aId) for ip in ['x','y','z']]
3477                varyNames += ['%d::dA%s:%d'%(pId,ip,bId) for ip in ['x','y','z']]
3478                VCoV = G2mth.getVCov(varyNames,varyList,data[name]['covMatrix'])
3479                esdList.append(np.sqrt(np.inner(derivs,np.inner(VCoV,derivs.T)) ))
3480#                GSASIIpath.IPyBreak()
3481            elif 'Angle' in expr:
3482                derivs = G2mth.CalcAngleDeriv(obj.angle_dict,obj.angle_atoms, parmDict)
3483                pId = obj.angle_dict['pId']
3484                aId,bId = obj.angle_atoms
3485                varyNames = ['%d::dA%s:%d'%(pId,ip,aId) for ip in ['x','y','z']]
3486                varyNames += ['%d::dA%s:%d'%(pId,ip,bId[0]) for ip in ['x','y','z']]
3487                varyNames += ['%d::dA%s:%d'%(pId,ip,bId[1]) for ip in ['x','y','z']]
3488                VCoV = G2mth.getVCov(varyNames,varyList,data[name]['covMatrix'])
3489                esdList.append(np.sqrt(np.inner(derivs,np.inner(VCoV,derivs.T)) ))
3490            else:
3491                derivs = np.array(
3492                    [EvalPSvarDeriv(calcobj,parmDict.copy(),sampleDict[name],var,ESD)
3493                     for var,ESD in zip(varyList,sigs)])
3494                esdList.append(np.sqrt(
3495                    np.inner(derivs,np.inner(data[name]['covMatrix'],derivs.T)) ))
3496            PSvarDict = parmDict.copy()
3497            PSvarDict.update(sampleDict[name])
3498            UpdateParmDict(PSvarDict)
3499            calcobj.UpdateDict(PSvarDict)
3500            valList.append(calcobj.EvalExpression())
3501#            if calcobj.su is not None: esdList[-1] = calcobj.su
3502        if not esdList:
3503            esdList = None
3504        colList += [valList]
3505        colSigs += [esdList]
3506        colLabels += [expr]
3507        Types += [wg.GRID_VALUE_FLOAT,]
3508    #---- table build done -------------------------------------------------------------
3509
3510    # Make dict needed for creating & editing pseudovars (PSvarDict).
3511    name = histNames[0]
3512    parmDict = data[name].get('parmDict',{})
3513    PSvarDict = parmDict.copy()
3514    PSvarDict.update(sampleParms)
3515    UpdateParmDict(PSvarDict)
3516    # Also dicts of dependent (depVarDict) & independent vars (indepVarDict)
3517    # for Parametric fitting from the data table
3518    parmDict = dict(zip(colLabels,zip(*colList)[0])) # scratch dict w/all values in table
3519    parmDict.update(
3520        {var:val for var,val in data[name].get('newCellDict',{}).values()} #  add varied reciprocal cell terms
3521    )
3522    name = histNames[0]
3523
3524    #******************************************************************************
3525    # create a set of values for example evaluation of pseudovars and
3526    # this does not work for refinements that have differing numbers of variables.
3527    #raise Exception
3528    indepVarDict = {}     #  values in table w/o ESDs
3529    depVarDict = {}
3530    for i,var in enumerate(colLabels):
3531        if var == 'Use': continue
3532        if colList[i][0] is None:
3533            val,sig = firstValueDict.get(var,[None,None])
3534        elif colSigs[i]:
3535            val,sig = colList[i][0],colSigs[i][0]
3536        else:
3537            val,sig = colList[i][0],None
3538        if val is None:
3539            continue
3540        elif sig is None:
3541            indepVarDict[var] = val
3542        elif striphist(var) not in Dlookup:
3543            depVarDict[var] = val
3544    # add recip cell coeff. values
3545    depVarDict.update({var:val for var,val in data[name].get('newCellDict',{}).values()})
3546
3547    G2frame.dataDisplay = G2G.GSGrid(parent=G2frame.dataFrame)
3548    G2frame.SeqTable = G2G.Table(
3549        [list(c) for c in zip(*colList)],     # convert from columns to rows
3550        colLabels=colLabels,rowLabels=histNames,types=Types)
3551    G2frame.dataDisplay.SetTable(G2frame.SeqTable, True)
3552    #G2frame.dataDisplay.EnableEditing(False)
3553    # make all but first column read-only
3554    for c in range(1,len(colLabels)):
3555        for r in range(nRows):
3556            G2frame.dataDisplay.SetCellReadOnly(r,c)
3557    G2frame.dataDisplay.Bind(wg.EVT_GRID_LABEL_LEFT_DCLICK, PlotSelect)
3558    G2frame.dataDisplay.Bind(wg.EVT_GRID_LABEL_RIGHT_CLICK, SetLabelString)
3559    G2frame.dataDisplay.SetRowLabelSize(8*len(histNames[0]))       #pretty arbitrary 8
3560    G2frame.dataDisplay.SetMargins(0,0)
3561    G2frame.dataDisplay.AutoSizeColumns(True)
3562    if prevSize:
3563        G2frame.dataDisplay.SetSize(prevSize)
3564    else:
3565        G2frame.dataFrame.setSizePosLeft([700,350])
3566    # highlight unconverged shifts
3567    if histNames[0][:4] not in ['SASD','IMG ']:
3568        for row,name in enumerate(histNames):
3569            deltaChi = G2frame.SeqTable.GetValue(row,deltaChiCol)
3570            if deltaChi > 10.:
3571                G2frame.dataDisplay.SetCellStyle(row,deltaChiCol,color=wx.Colour(255,0,0))
3572            elif deltaChi > 1.0:
3573                G2frame.dataDisplay.SetCellStyle(row,deltaChiCol,color=wx.Colour(255,255,0))
3574    G2frame.dataDisplay.InstallGridToolTip(GridSetToolTip,GridColLblToolTip)
3575    G2frame.dataDisplay.SendSizeEvent() # resize needed on mac
3576    G2frame.dataDisplay.Refresh() # shows colored text on mac
3577   
3578################################################################################
3579#####  Main PWDR panel
3580################################################################################           
3581       
3582def UpdatePWHKPlot(G2frame,kind,item):
3583    '''Called when the histogram main tree entry is called. Displays the
3584    histogram weight factor, refinement statistics for the histogram
3585    and the range of data for a simulation.
3586
3587    Also invokes a plot of the histogram.
3588    '''
3589    def onEditSimRange(event):
3590        'Edit simulation range'
3591        inp = [
3592            min(data[1][0]),
3593            max(data[1][0]),
3594            None
3595            ]
3596        inp[2] = (inp[1] - inp[0])/(len(data[1][0])-1.)
3597        names = ('start angle', 'end angle', 'step size')
3598        dictlst = [inp] * len(inp)
3599        elemlst = range(len(inp))
3600        dlg = G2G.ScrolledMultiEditor(
3601            G2frame,[inp] * len(inp), range(len(inp)), names,
3602            header='Edit simulation range',
3603            minvals=(0.001,0.001,0.0001),
3604            maxvals=(180.,180.,.1),
3605            )
3606        dlg.CenterOnParent()
3607        val = dlg.ShowModal()
3608        dlg.Destroy()
3609        if val != wx.ID_OK: return
3610        if inp[0] > inp[1]:
3611            end,start,step = inp
3612        else:               
3613            start,end,step = inp
3614        step = abs(step)
3615        N = int((end-start)/step)+1
3616        newdata = np.linspace(start,end,N,True)
3617        if len(newdata) < 2: return # too small a range - reject
3618        data[1] = [newdata,np.zeros_like(newdata),np.ones_like(newdata),
3619            np.zeros_like(newdata),np.zeros_like(newdata),np.zeros_like(newdata)]
3620        Tmin = newdata[0]
3621        Tmax = newdata[-1]
3622        G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,item,'Limits'),
3623            [(Tmin,Tmax),[Tmin,Tmax]])
3624        UpdatePWHKPlot(G2frame,kind,item) # redisplay data screen
3625
3626    def OnPlot3DHKL(event):
3627        refList = data[1]['RefList']
3628        FoMax = np.max(refList.T[8+Super])
3629        Hmin = np.array([int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))])
3630        Hmax = np.array([int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))])
3631        Vpoint = np.array([int(np.mean(refList.T[0])),int(np.mean(refList.T[1])),int(np.mean(refList.T[2]))])
3632        controls = {'Type' : 'Fosq','Iscale' : False,'HKLmax' : Hmax,'HKLmin' : Hmin,'Zone':False,'viewKey':'L',
3633            'FoMax' : FoMax,'Scale' : 1.0,'Drawing':{'viewPoint':[Vpoint,[]],'default':Vpoint[:],
3634            'backColor':[0,0,0],'depthFog':False,'Zclip':10.0,'cameraPos':10.,'Zstep':0.05,'viewUp':[0,1,0],
3635            'Scale':1.0,'oldxy':[],'viewDir':[0,0,1]},'Super':Super,'SuperVec':SuperVec}
3636        G2plt.Plot3DSngl(G2frame,newPlot=True,Data=controls,hklRef=refList,Title=phaseName)
3637       
3638    def OnPlotAll3DHKL(event):
3639        choices = GetPatternTreeDataNames(G2frame,['HKLF',])
3640        dlg = G2G.G2MultiChoiceDialog(G2frame, 'Select reflection sets to plot',
3641            'Use data',choices)
3642        try:
3643            if dlg.ShowModal() == wx.ID_OK:
3644                refNames = [choices[i] for i in dlg.GetSelections()]
3645            else:
3646                return
3647        finally:
3648            dlg.Destroy()
3649        refList = np.zeros(0)
3650        for name in refNames:
3651            Id = GetPatternTreeItemId(G2frame,G2frame.root, name)
3652            reflData = G2frame.PatternTree.GetItemPyData(Id)[1]
3653            if len(refList):
3654                refList = np.concatenate((refList,reflData['RefList']))
3655            else:
3656                refList = reflData['RefList']
3657           
3658        FoMax = np.max(refList.T[8+Super])
3659        Hmin = np.array([int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))])
3660        Hmax = np.array([int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))])
3661        Vpoint = [int(np.mean(refList.T[0])),int(np.mean(refList.T[1])),int(np.mean(refList.T[2]))]
3662        controls = {'Type' : 'Fosq','Iscale' : False,'HKLmax' : Hmax,'HKLmin' : Hmin,'Zone':False,'viewKey':'L',
3663            'FoMax' : FoMax,'Scale' : 1.0,'Drawing':{'viewPoint':[Vpoint,[]],'default':Vpoint[:],
3664            'backColor':[0,0,0],'depthFog':False,'Zclip':10.0,'cameraPos':10.,'Zstep':0.05,'viewUp':[0,1,0],
3665            'Scale':1.0,'oldxy':[],'viewDir':[1,0,0]},'Super':Super,'SuperVec':SuperVec}
3666        G2plt.Plot3DSngl(G2frame,newPlot=True,Data=controls,hklRef=refList,Title=phaseName)
3667                 
3668    def OnMergeHKL(event):
3669        Name = G2frame.PatternTree.GetItemText(G2frame.PatternId)
3670        Inst = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,
3671            G2frame.PatternId,'Instrument Parameters'))
3672        CId = GetPatternTreeItemId(G2frame,G2frame.PatternId,'Comments')
3673        if CId:
3674            Comments = G2frame.PatternTree.GetItemPyData(CId)
3675        else:
3676            Comments = []
3677        refList = np.copy(data[1]['RefList'])
3678        Comments.append(' Merging %d reflections from %s'%(len(refList),Name))
3679        dlg = MergeDialog(G2frame,data)
3680        try:
3681            if dlg.ShowModal() == wx.ID_OK:
3682                Trans,Cent,Laue = dlg.GetSelection()
3683            else:
3684                return
3685        finally:
3686            dlg.Destroy()
3687        Super = data[1]['Super']
3688        refList,badRefs = G2lat.transposeHKLF(Trans,Super,refList)
3689        if len(badRefs):    #do I want to list badRefs?
3690            G2frame.ErrorDialog('Failed transformation','Matrix yields fractional hkl indices')
3691            return
3692        Comments.append(" Transformation M*H = H' applied; M=")
3693        Comments.append(str(Trans))
3694        SG = 'P '+Laue
3695        SGData = G2spc.SpcGroup(SG)[1]
3696        refList = G2lat.LaueUnique(Laue,refList)
3697        dlg = wx.ProgressDialog('Build HKL dictonary','',len(refList)+1, 
3698            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE)
3699        HKLdict = {}
3700        for ih,hkl in enumerate(refList):
3701            if str(hkl[:3+Super]) not in HKLdict:
3702                HKLdict[str(hkl[:3+Super])] = [hkl[:3+Super],[hkl[3+Super:],]]
3703            else:
3704                HKLdict[str(hkl[:3+Super])][1].append(hkl[3+Super:])
3705            dlg.Update(ih)
3706        dlg.Destroy()
3707        mergeRef = []
3708        dlg = wx.ProgressDialog('Processing merge','',len(HKLdict)+1, 
3709            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE)
3710        sumDf = 0.
3711        sumFo = 0.
3712        for ih,hkl in enumerate(HKLdict):
3713            HKL = HKLdict[hkl]
3714            newHKL = list(HKL[0])+list(HKL[1][0])
3715            if len(HKL[1]) > 1:
3716                fos = np.array(HKL[1])
3717                wFo = 1/fos[:,3]**2
3718                Fo = np.average(fos[:,2],weights=wFo)
3719                std = np.std(fos[:,2])
3720                sig = np.sqrt(np.mean(fos[:,3])**2+std**2)
3721                sumFo += np.sum(fos[:,2])
3722                sumDf += np.sum(np.abs(fos[:,2]-Fo))
3723                dlg.Update(ih)
3724                newHKL[5+Super] = Fo
3725                newHKL[6+Super] = sig
3726                newHKL[8+Super] = Fo
3727            if newHKL[5+Super] > 0.:
3728                mergeRef.append(list(newHKL)) 
3729        dlg.Destroy()
3730        if Super:
3731            mergeRef = G2mth.sortArray(G2mth.sortArray(G2mth.sortArray(G2mth.sortArray(mergeRef,3),2),1),0)
3732        else:
3733            mergeRef = G2mth.sortArray(G2mth.sortArray(G2mth.sortArray(mergeRef,2),1),0)
3734        mergeRef = np.array(mergeRef)
3735        if sumFo:
3736            mtext = ' merge R = %6.2f%s for %d reflections in %s'%(100.*sumDf/sumFo,'%',mergeRef.shape[0],Laue)
3737            print mtext
3738            Comments.append(mtext)
3739        else:
3740            print 'nothing to merge for %s reflections'%(mergeRef.shape[0])
3741        HKLFlist = []
3742        newName = Name+' '+Laue
3743        if G2frame.PatternTree.GetCount():
3744            item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
3745            while item:
3746                name = G2frame.PatternTree.GetItemText(item)
3747                if name.startswith('HKLF ') and name not in HKLFlist:
3748                    HKLFlist.append(name)
3749                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
3750        newName = G2obj.MakeUniqueLabel(newName,HKLFlist)
3751        newData = copy.deepcopy(data)
3752        newData[0]['ranId'] = ran.randint(0,sys.maxint)
3753        newData[1]['RefList'] = mergeRef
3754        Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=newName)
3755        G2frame.PatternTree.SetItemPyData(
3756            G2frame.PatternTree.AppendItem(Id,text='Comments'),Comments)
3757        G2frame.PatternTree.SetItemPyData(Id,newData)
3758        G2frame.PatternTree.SetItemPyData(
3759            G2frame.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
3760        G2frame.PatternTree.SetItemPyData(
3761            G2frame.PatternTree.AppendItem(Id,text='Reflection List'),{})  #dummy entry for GUI use
3762                   
3763    def OnErrorAnalysis(event):
3764        G2plt.PlotDeltSig(G2frame,kind)
3765       
3766    def OnWtFactor(event):
3767        try:
3768            val = float(wtval.GetValue())
3769        except ValueError:
3770            val = data[0]['wtFactor']
3771        data[0]['wtFactor'] = val
3772        wtval.SetValue('%.3f'%(val))
3773       
3774    def OnCompression(event):
3775        data[0] = int(comp.GetValue())
3776       
3777    def onCopyPlotCtrls(event):
3778        '''Respond to menu item to copy multiple sections from a histogram.
3779        Need this here to pass on the G2frame object.
3780        '''
3781        G2pdG.CopyPlotCtrls(G2frame)
3782
3783    def onCopySelectedItems(event):
3784        '''Respond to menu item to copy multiple sections from a histogram.
3785        Need this here to pass on the G2frame object.
3786        '''
3787        G2pdG.CopySelectedHistItems(G2frame)
3788           
3789    data = G2frame.PatternTree.GetItemPyData(item)
3790#patches
3791    if 'wtFactor' not in data[0]:
3792        data[0] = {'wtFactor':1.0}
3793#    if kind == 'PWDR' and 'Compression' not in data[0]:
3794#        data[0]['Compression'] = 1
3795    #if isinstance(data[1],list) and kind == 'HKLF':
3796    if 'list' in str(type(data[1])) and kind == 'HKLF':
3797        RefData = {'RefList':[],'FF':[]}
3798        for ref in data[1]:
3799            RefData['RefList'].append(ref[:11]+[ref[13],])
3800            RefData['FF'].append(ref[14])
3801        data[1] = RefData
3802        G2frame.PatternTree.SetItemPyData(item,data)
3803#end patches
3804    if G2frame.dataDisplay:
3805        G2frame.dataDisplay.Destroy()
3806    if kind in ['PWDR','SASD']:
3807        SetDataMenuBar(G2frame,G2frame.dataFrame.PWDRMenu)
3808        G2frame.dataFrame.Bind(wx.EVT_MENU, OnErrorAnalysis, id=wxID_PWDANALYSIS)
3809        G2frame.dataFrame.Bind(wx.EVT_MENU, onCopySelectedItems, id=wxID_PWDCOPY)
3810        G2frame.dataFrame.Bind(wx.EVT_MENU, onCopyPlotCtrls, id=wxID_PLOTCTRLCOPY)
3811    elif kind in ['HKLF',]:
3812        SetDataMenuBar(G2frame,G2frame.dataFrame.HKLFMenu)
3813        G2frame.dataFrame.Bind(wx.EVT_MENU, OnErrorAnalysis, id=wxID_PWDANALYSIS)
3814        G2frame.dataFrame.Bind(wx.EVT_MENU, OnMergeHKL, id=wxID_MERGEHKL)
3815        G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlot3DHKL, id=wxID_PWD3DHKLPLOT)
3816        G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlotAll3DHKL, id=wxID_3DALLHKLPLOT)
3817#        G2frame.dataFrame.Bind(wx.EVT_MENU, onCopySelectedItems, id=wxID_PWDCOPY)
3818    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
3819   
3820    mainSizer = wx.BoxSizer(wx.VERTICAL)
3821    mainSizer.Add((5,5),)
3822    wtSizer = wx.BoxSizer(wx.HORIZONTAL)
3823    wtSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Weight factor: '),0,WACV)
3824    wtval = wx.TextCtrl(G2frame.dataDisplay,-1,'%.3f'%(data[0]['wtFactor']),style=wx.TE_PROCESS_ENTER)
3825    wtval.Bind(wx.EVT_TEXT_ENTER,OnWtFactor)
3826    wtval.Bind(wx.EVT_KILL_FOCUS,OnWtFactor)
3827    wtSizer.Add(wtval,0,WACV)
3828#    if kind == 'PWDR':         #possible future compression feature; NB above patch as well
3829#        wtSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Compression factor: '),0,WACV)
3830#        choice = ['1','2','3','4','5','6']
3831#        comp = wx.ComboBox(parent=G2frame.dataDisplay,choices=choice,
3832#            style=wx.CB_READONLY|wx.CB_DROPDOWN)
3833#        comp.SetValue(str(data[0]['Compression']))
3834#        comp.Bind(wx.EVT_COMBOBOX, OnCompression)
3835#        wtSizer.Add(comp,0,WACV)
3836    mainSizer.Add(wtSizer)
3837    if data[0].get('Dummy'):
3838        simSizer = wx.BoxSizer(wx.HORIZONTAL)
3839        Tmin = min(data[1][0])
3840        Tmax = max(data[1][0])
3841        num = len(data[1][0])
3842        step = (Tmax - Tmin)/(num-1)
3843        t = u'2\u03b8' # 2theta
3844        lbl =  u'Simulation range: {:.2f} to {:.2f} {:s}\nwith {:.4f} steps ({:d} points)'
3845        lbl += u'\n(Edit range resets observed intensities).'
3846        lbl = lbl.format(Tmin,Tmax,t,step,num)
3847        simSizer.Add(wx.StaticText(G2frame.dataDisplay,wx.ID_ANY,lbl),
3848                    0,WACV)
3849        but = wx.Button(G2frame.dataDisplay,wx.ID_ANY,"Edit range")
3850        but.Bind(wx.EVT_BUTTON,onEditSimRange)
3851        simSizer.Add(but,0,WACV)
3852        mainSizer.Add(simSizer)
3853    if 'Nobs' in data[0]:
3854        mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,
3855            ' Data residual wR: %.3f%% on %d observations'%(data[0]['wR'],data[0]['Nobs'])))
3856        for value in data[0]:
3857            if 'Nref' in value:
3858                pfx = value.split('Nref')[0]
3859                name = data[0].get(pfx.split(':')[0]+'::Name','?')
3860                if 'SS' in value:
3861                    mainSizer.Add((5,5),)
3862                    mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' For incommensurate phase '+name+':'))
3863                    for m,(Rf2,Rf,Nobs) in enumerate(zip(data[0][pfx+'Rf^2'],data[0][pfx+'Rf'],data[0][value])):
3864                        mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,
3865                            u' m = +/- %d: RF\u00b2: %.3f%%, RF: %.3f%% on %d reflections  '% \
3866                            (m,Rf2,Rf,Nobs)))
3867                else:
3868                    mainSizer.Add((5,5),)
3869                    mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' For phase '+name+':'))
3870                    mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,
3871                        u' Unweighted phase residuals RF\u00b2: %.3f%%, RF: %.3f%% on %d reflections  '% \
3872                        (data[0][pfx+'Rf^2'],data[0][pfx+'Rf'],data[0][value])))
3873                   
3874    mainSizer.Add((5,5),)
3875    mainSizer.Layout()   
3876    G2frame.dataDisplay.SetSizer(mainSizer)
3877    Size = mainSizer.Fit(G2frame.dataFrame)
3878    Size[1] += 10
3879    G2frame.dataFrame.setSizePosLeft(Size)
3880    G2frame.PatternTree.SetItemPyData(item,data)
3881    if kind in ['PWDR','SASD']:
3882        NewPlot = True
3883        if 'xylim' in dir(G2frame):
3884            NewPlot = False
3885        G2plt.PlotPatterns(G2frame,plotType=kind,newPlot=NewPlot)
3886    elif kind == 'HKLF':
3887        Name = G2frame.PatternTree.GetItemText(item)
3888        phaseName = G2pdG.IsHistogramInAnyPhase(G2frame,Name)
3889        if phaseName:
3890            pId = GetPatternTreeItemId(G2frame,G2frame.root,'Phases')
3891            phaseId =  GetPatternTreeItemId(G2frame,pId,phaseName)
3892            General = G2frame.PatternTree.GetItemPyData(phaseId)['General']
3893            Super = General.get('Super',0)
3894            SuperVec = General.get('SuperVec',[])
3895        else:
3896            Super = 0
3897            SuperVec = []       
3898        refList = data[1]['RefList']
3899#        GSASIIpath.IPyBreak()
3900        FoMax = np.max(refList.T[5+data[1].get('Super',0)])
3901        page = G2frame.G2plotNB.nb.GetSelection()
3902        tab = ''
3903        if page >= 0:
3904            tab = G2frame.G2plotNB.nb.GetPageText(page)
3905        if '3D' in tab:
3906            Hmin = np.array([int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))])
3907            Hmax = np.array([int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))])
3908            Vpoint = [int(np.mean(refList.T[0])),int(np.mean(refList.T[1])),int(np.mean(refList.T[2]))]
3909            Page = G2frame.G2plotNB.nb.GetPage(page)
3910            controls = Page.controls
3911            G2plt.Plot3DSngl(G2frame,newPlot=False,Data=controls,hklRef=refList,Title=phaseName)
3912        else:
3913            controls = {'Type' : 'Fo','ifFc' : True,     
3914                'HKLmax' : [int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))],
3915                'HKLmin' : [int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))],
3916                'FoMax' : FoMax,'Zone' : '001','Layer' : 0,'Scale' : 1.0,'Super':Super,'SuperVec':SuperVec}
3917            G2plt.PlotSngl(G2frame,newPlot=True,Data=controls,hklRef=refList)
3918                 
3919################################################################################
3920#####  Pattern tree routines
3921################################################################################           
3922       
3923def GetPatternTreeDataNames(G2frame,dataTypes):
3924    '''Finds all items in tree that match a 4 character prefix
3925   
3926    :param wx.Frame G2frame: Data tree frame object
3927    :param list dataTypes: Contains one or more data tree item types to be matched
3928      such as ['IMG '] or ['PWDR','HKLF']
3929    :returns: a list of tree item names for the matching items 
3930    '''
3931    names = []
3932    item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)       
3933    while item:
3934        name = G2frame.PatternTree.GetItemText(item)
3935        if name[:4] in dataTypes:
3936            names.append(name)
3937        item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
3938    return names
3939                         
3940def GetPatternTreeItemId(G2frame, parentId, itemText):
3941    '''Find the tree item that matches the text in itemText starting with parentId
3942
3943    :param wx.Frame G2frame: Data tree frame object
3944    :param wx.TreeItemId parentId: tree item to start search with
3945    :param str itemText: text for tree item
3946    '''
3947    item, cookie = G2frame.PatternTree.GetFirstChild(parentId)
3948    while item:
3949        if G2frame.PatternTree.GetItemText(item) == itemText:
3950            return item
3951        item, cookie = G2frame.PatternTree.GetNextChild(parentId, cookie)
3952    return 0               
3953
3954def MovePatternTreeToGrid(G2frame,item):
3955    '''Called from GSASII.OnPatternTreeSelChanged when a item is selected on the tree
3956    '''
3957    pickName = G2frame.PatternTree.GetItemText(item)
3958    if G2frame.PickIdText == pickName:
3959        return
3960   
3961    oldPage = None # will be set later if already on a Phase item
3962    if G2frame.dataFrame:
3963        SetDataMenuBar(G2frame)
3964        if G2frame.dataFrame.GetLabel() == 'Comments':
3965            try:
3966                data = [G2frame.dataDisplay.GetValue()]
3967                G2frame.dataDisplay.Clear() 
3968                Id = GetPatternTreeItemId(G2frame,G2frame.root, 'Comments')
3969                if Id: G2frame.PatternTree.SetItemPyData(Id,data)
3970            except:     #clumsy but avoids dead window problem when opening another project
3971                pass
3972        elif G2frame.dataFrame.GetLabel() == 'Notebook':
3973            try:
3974                data = [G2frame.dataDisplay.GetValue()]
3975                G2frame.dataDisplay.Clear() 
3976                Id = GetPatternTreeItemId(G2frame,G2frame.root, 'Notebook')
3977                if Id: G2frame.PatternTree.SetItemPyData(Id,data)
3978            except:     #clumsy but avoids dead window problem when opening another project
3979                pass
3980        elif 'Phase Data for' in G2frame.dataFrame.GetLabel():
3981            if G2frame.dataDisplay: 
3982                oldPage = G2frame.dataDisplay.GetSelection()
3983        G2frame.dataFrame.Clear()
3984        G2frame.dataFrame.SetLabel('')
3985    else:
3986        #create the frame for the data item window
3987        G2frame.dataFrame = DataFrame(parent=G2frame.mainPanel,frame=G2frame)
3988        G2frame.dataFrame.PhaseUserSize = None
3989       
3990    G2frame.dataFrame.Raise()           
3991    G2frame.PickId = item
3992    G2frame.PickIdText = None
3993    parentID = G2frame.root
3994    #for i in G2frame.ExportPattern: i.Enable(False)
3995    defWid = [250,150]
3996    if item != G2frame.root:
3997        parentID = G2frame.PatternTree.GetItemParent(item)
3998    if G2frame.PatternTree.GetItemParent(item) == G2frame.root:
3999        G2frame.PatternId = item
4000        if G2frame.PatternTree.GetItemText(item) == 'Notebook':
4001            SetDataMenuBar(G2frame,G2frame.dataFrame.DataNotebookMenu)
4002            G2frame.PatternId = 0
4003            #for i in G2frame.ExportPattern: i.Enable(False)
4004            data = G2frame.PatternTree.GetItemPyData(item)
4005            UpdateNotebook(G2frame,data)
4006        elif G2frame.PatternTree.GetItemText(item) == 'Controls':
4007            G2frame.PatternId = 0
4008            #for i in G2frame.ExportPattern: i.Enable(False)
4009            data = G2frame.PatternTree.GetItemPyData(item)
4010            if not data:           #fill in defaults
4011                data = copy.copy(G2obj.DefaultControls)    #least squares controls
4012                G2frame.PatternTree.SetItemPyData(item,data)                             
4013            for i in G2frame.Refine: i.Enable(True)
4014            G2frame.EnableSeqRefineMenu()
4015            UpdateControls(G2frame,data)
4016        elif G2frame.PatternTree.GetItemText(item) == 'Sequential results':
4017            data = G2frame.PatternTree.GetItemPyData(item)
4018            UpdateSeqResults(G2frame,data)
4019        elif G2frame.PatternTree.GetItemText(item) == 'Covariance':
4020            data = G2frame.PatternTree.GetItemPyData(item)
4021            G2frame.dataFrame.setSizePosLeft(defWid)
4022            text = ''
4023            if 'Rvals' in data:
4024                Nvars = len(data['varyList'])
4025                Rvals = data['Rvals']
4026                text = '\nFinal residuals: \nwR = %.3f%% \nchi**2 = %.1f \nGOF = %.2f'%(Rvals['Rwp'],Rvals['chisq'],Rvals['GOF'])
4027                text += '\nNobs = %d \nNvals = %d'%(Rvals['Nobs'],Nvars)
4028                if 'lamMax' in Rvals:
4029                    text += '\nlog10 MaxLambda = %.1f'%(np.log10(Rvals['lamMax']))
4030            wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
4031                value='See plot window for covariance display'+text,style=wx.TE_MULTILINE)
4032            G2plt.PlotCovariance(G2frame,data)
4033        elif G2frame.PatternTree.GetItemText(item) == 'Constraints':
4034            data = G2frame.PatternTree.GetItemPyData(item)
4035            G2cnstG.UpdateConstraints(G2frame,data)
4036        elif G2frame.PatternTree.GetItemText(item) == 'Rigid bodies':
4037            data = G2frame.PatternTree.GetItemPyData(item)
4038            G2cnstG.UpdateRigidBodies(G2frame,data)
4039        elif G2frame.PatternTree.GetItemText(item) == 'Restraints':
4040            data = G2frame.PatternTree.GetItemPyData(item)
4041            Phases = G2frame.GetPhaseData()
4042            phase = ''
4043            phaseName = ''
4044            if Phases:
4045                phaseName = Phases.keys()[0]
4046            G2frame.dataFrame.setSizePosLeft(defWid)
4047            G2restG.UpdateRestraints(G2frame,data,Phases,phaseName)
4048        elif 'IMG' in G2frame.PatternTree.GetItemText(item):
4049            G2frame.Image = item
4050            G2frame.dataFrame.SetTitle('Image Data')
4051            data = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(
4052                G2frame,item,'Image Controls'))
4053            G2imG.UpdateImageData(G2frame,data)
4054            G2plt.PlotImage(G2frame,newPlot=True)
4055        elif 'PKS' in G2frame.PatternTree.GetItemText(item):
4056            G2plt.PlotPowderLines(G2frame)
4057        elif 'PWDR' in G2frame.PatternTree.GetItemText(item):
4058            #for i in G2frame.ExportPattern: i.Enable(True)
4059            if G2frame.EnablePlot:
4060                UpdatePWHKPlot(G2frame,'PWDR',item)
4061        elif 'SASD' in G2frame.PatternTree.GetItemText(item):
4062            #for i in G2frame.ExportPattern: i.Enable(True)
4063            if G2frame.EnablePlot:
4064                UpdatePWHKPlot(G2frame,'SASD',item)
4065        elif 'HKLF' in G2frame.PatternTree.GetItemText(item):
4066            G2frame.Sngl = True
4067            UpdatePWHKPlot(G2frame,'HKLF',item)
4068        elif 'PDF' in G2frame.PatternTree.GetItemText(item):
4069            G2frame.PatternId = item
4070            for i in G2frame.ExportPDF: i.Enable(True)
4071            G2plt.PlotISFG(G2frame,type='S(Q)')
4072        elif G2frame.PatternTree.GetItemText(item) == 'Phases':
4073            G2frame.dataFrame.setSizePosLeft(defWid)
4074            wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
4075                value='Select one phase to see its parameters')           
4076    elif 'I(Q)' in G2frame.PatternTree.GetItemText(item):
4077        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4078        data = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.PatternId,'PDF Controls'))
4079        G2pdG.UpdatePDFGrid(G2frame,data)
4080        G2plt.PlotISFG(G2frame,type='I(Q)',newPlot=True)
4081    elif 'S(Q)' in G2frame.PatternTree.GetItemText(item):
4082        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4083        data = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.PatternId,'PDF Controls'))
4084        G2pdG.UpdatePDFGrid(G2frame,data)
4085        G2plt.PlotISFG(G2frame,type='S(Q)',newPlot=True)
4086    elif 'F(Q)' in G2frame.PatternTree.GetItemText(item):
4087        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4088        data = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.PatternId,'PDF Controls'))
4089        G2pdG.UpdatePDFGrid(G2frame,data)
4090        G2plt.PlotISFG(G2frame,type='F(Q)',newPlot=True)
4091    elif 'G(R)' in G2frame.PatternTree.GetItemText(item):
4092        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4093        data = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.PatternId,'PDF Controls'))
4094        G2pdG.UpdatePDFGrid(G2frame,data)
4095        G2plt.PlotISFG(G2frame,type='G(R)',newPlot=True)           
4096    elif G2frame.PatternTree.GetItemText(parentID) == 'Phases':
4097        data = G2frame.PatternTree.GetItemPyData(item)
4098        G2phG.UpdatePhaseData(G2frame,item,data,oldPage)
4099    elif G2frame.PatternTree.GetItemText(item) == 'Comments':
4100        SetDataMenuBar(G2frame,G2frame.dataFrame.DataCommentsMenu)
4101        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4102        data = G2frame.PatternTree.GetItemPyData(item)
4103        UpdateComments(G2frame,data)
4104    elif G2frame.PatternTree.GetItemText(item) == 'Image Controls':
4105        G2frame.dataFrame.SetTitle('Image Controls')
4106        G2frame.Image = G2frame.PatternTree.GetItemParent(item)
4107        masks = G2frame.PatternTree.GetItemPyData(
4108            GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks'))
4109        data = G2frame.PatternTree.GetItemPyData(item)
4110        G2frame.ImageZ = G2imG.GetImageZ(G2frame,data)
4111        G2imG.UpdateImageControls(G2frame,data,masks)
4112        G2plt.PlotImage(G2frame,newPlot=True)
4113    elif G2frame.PatternTree.GetItemText(item) == 'Masks':
4114        G2frame.dataFrame.SetTitle('Masks')
4115        G2frame.Image = G2frame.PatternTree.GetItemParent(item)
4116        masks = G2frame.PatternTree.GetItemPyData(item)
4117        data = G2frame.PatternTree.GetItemPyData(
4118            GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
4119        G2frame.ImageZ = G2imG.GetImageZ(G2frame,data)
4120        G2imG.UpdateMasks(G2frame,masks)
4121        G2plt.PlotImage(G2frame,newPlot=True)
4122    elif G2frame.PatternTree.GetItemText(item) == 'Stress/Strain':
4123        G2frame.dataFrame.SetTitle('Stress/Strain')
4124        G2frame.Image = G2frame.PatternTree.GetItemParent(item)
4125        data = G2frame.PatternTree.GetItemPyData(
4126            GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
4127        G2frame.ImageZ = G2imG.GetImageZ(G2frame,data)
4128        strsta = G2frame.PatternTree.GetItemPyData(item)
4129        G2plt.PlotImage(G2frame,newPlot=True)
4130        G2plt.PlotStrain(G2frame,strsta,newPlot=True)
4131        G2imG.UpdateStressStrain(G2frame,strsta)
4132    elif G2frame.PatternTree.GetItemText(item) == 'PDF Controls':
4133        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4134        for i in G2frame.ExportPDF: i.Enable(True)
4135        data = G2frame.PatternTree.GetItemPyData(item)
4136        G2pdG.UpdatePDFGrid(G2frame,data)
4137        G2plt.PlotISFG(G2frame,type='I(Q)')
4138        G2plt.PlotISFG(G2frame,type='S(Q)')
4139        G2plt.PlotISFG(G2frame,type='F(Q)')
4140        G2plt.PlotISFG(G2frame,type='G(R)')
4141    elif G2frame.PatternTree.GetItemText(item) == 'Peak List':
4142        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4143        for i in G2frame.ExportPeakList: i.Enable(True)
4144        data = G2frame.PatternTree.GetItemPyData(item)
4145#patch
4146        if 'list' in str(type(data)):
4147            data = {'peaks':data,'sigDict':{}}
4148            G2frame.PatternTree.SetItemPyData(item,data)
4149#end patch
4150        G2pdG.UpdatePeakGrid(G2frame,data)
4151        G2plt.PlotPatterns(G2frame)
4152    elif G2frame.PatternTree.GetItemText(item) == 'Background':
4153        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4154        data = G2frame.PatternTree.GetItemPyData(item)
4155        G2pdG.UpdateBackground(G2frame,data)
4156        G2plt.PlotPatterns(G2frame)
4157    elif G2frame.PatternTree.GetItemText(item) == 'Limits':
4158        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4159        datatype = G2frame.PatternTree.GetItemText(G2frame.PatternId)[:4]
4160        data = G2frame.PatternTree.GetItemPyData(item)
4161        G2pdG.UpdateLimitsGrid(G2frame,data,datatype)
4162        G2plt.PlotPatterns(G2frame,plotType=datatype)
4163    elif G2frame.PatternTree.GetItemText(item) == 'Instrument Parameters':
4164        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4165        data = G2frame.PatternTree.GetItemPyData(item)[0]
4166        G2pdG.UpdateInstrumentGrid(G2frame,data)
4167        if 'P' in data['Type'][0]:          #powder data only
4168            G2plt.PlotPeakWidths(G2frame)
4169    elif G2frame.PatternTree.GetItemText(item) == 'Models':
4170        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4171        data = G2frame.PatternTree.GetItemPyData(item)
4172        G2pdG.UpdateModelsGrid(G2frame,data)
4173        G2plt.PlotPatterns(G2frame,plotType='SASD')
4174        if len(data['Size']['Distribution']):
4175            G2plt.PlotSASDSizeDist(G2frame)
4176    elif G2frame.PatternTree.GetItemText(item) == 'Substances':
4177        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4178        data = G2frame.PatternTree.GetItemPyData(item)
4179        G2pdG.UpdateSubstanceGrid(G2frame,data)
4180    elif G2frame.PatternTree.GetItemText(item) == 'Sample Parameters':
4181        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4182        data = G2frame.PatternTree.GetItemPyData(item)
4183        datatype = G2frame.PatternTree.GetItemPyData(G2frame.PatternId)[2][:4]
4184
4185        if 'Temperature' not in data:           #temp fix for old gpx files
4186            data = {'Scale':[1.0,True],'Type':'Debye-Scherrer','Absorption':[0.0,False],'DisplaceX':[0.0,False],
4187                'DisplaceY':[0.0,False],'Diffuse':[],'Temperature':300.,'Pressure':1.0,
4188                    'FreePrm1':0.,'FreePrm2':0.,'FreePrm3':0.,
4189                    'Gonio. radius':200.0}
4190            G2frame.PatternTree.SetItemPyData(item,data)
4191   
4192        G2pdG.UpdateSampleGrid(G2frame,data)
4193        G2plt.PlotPatterns(G2frame,plotType=datatype)
4194    elif G2frame.PatternTree.GetItemText(item) == 'Index Peak List':
4195        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4196        for i in G2frame.ExportPeakList: i.Enable(True)
4197        data = G2frame.PatternTree.GetItemPyData(item)
4198#patch
4199        if len(data) != 2:
4200            data = [data,[]]
4201            G2frame.PatternTree.SetItemPyData(item,data)
4202#end patch
4203        G2pdG.UpdateIndexPeaksGrid(G2frame,data)
4204        if 'PKS' in G2frame.PatternTree.GetItemText(G2frame.PatternId):
4205            G2plt.PlotPowderLines(G2frame)
4206        else:
4207            G2plt.PlotPatterns(G2frame)
4208    elif G2frame.PatternTree.GetItemText(item) == 'Unit Cells List':
4209        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4210        data = G2frame.PatternTree.GetItemPyData(item)
4211        if not data:
4212            data.append([0,0.0,4,25.0,0,'P1',1,1,1,90,90,90]) #zero error flag, zero value, max Nc/No, start volume
4213            data.append([0,0,0,0,0,0,0,0,0,0,0,0,0,0])      #Bravais lattice flags
4214            data.append([])                                 #empty cell list
4215            data.append([])                                 #empty dmin
4216            data.append({})                                 #empty superlattice stuff
4217            G2frame.PatternTree.SetItemPyData(item,data)                             
4218#patch
4219        if len(data) < 5:
4220            data.append({'Use':False,'ModVec':[0,0,0.1],'maxH':1,