source: trunk/GSASIIgrid.py @ 2846

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

implement SVD inversion (Moore Penrose invert routine) in Hessian LSQ
clean up commented out code replaced by ValidatedTextCtrl?
set default dM/M to 0.001 (from 0.0001) - more reasonable convergence criterion

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