source: trunk/GSASIIgrid.py @ 2697

Last change on this file since 2697 was 2697, checked in by vondreele, 5 years ago

put colList for seqTable in G2frame it has persistence when plot is revised
put SeqPseudoVars? & SeqParFitEqList? in data (SeqResult?) so are local to each type of sequential result. Remove from Controls except for seqRefine (this may change).
Use parmDict instead of indepVarDict in AddNewParFitEqList? - allows more choice of parameters for functions
clear both SeqPseudoVars? & SeqParFitEqList? for a new sequential fit

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