source: trunk/GSASIIgrid.py @ 2809

Last change on this file since 2809 was 2809, checked in by vondreele, 6 years ago

add new transform option for moving atoms from setting #1 --> #2

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