source: trunk/GSASIIgrid.py @ 2856

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

fix bug in drawatom add via "sphere" enclosure

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