source: branch/2frame/GSASIIgrid.py @ 2888

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