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

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

Constraints now displayed
fix TopLevel? references
fix display issues for Sample Parameters
fix Resttraints display issues

  • 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 16:17:10 +0000 (Sat, 01 Jul 2017) $
5# $Author: vondreele $
6# $Revision: 2890 $
7# $URL: branch/2frame/GSASIIgrid.py $
8# $Id: GSASIIgrid.py 2890 2017-07-01 16:17: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: 2890 $")
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.GetTopLevelParent().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.GetTopLevelParent().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.GetTopLevelParent().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        self.SetAutoLayout(True)
2603        self.SetupScrolling()
2604       
2605#TODO - remove         
2606#    def _init_ctrls(self, parent,name=None,size=None,pos=None):
2607#        wx.Frame.__init__(
2608#            self,parent=parent,
2609#            #style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX | wx.FRAME_FLOAT_ON_PARENT ,
2610#            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX,
2611#            size=size,pos=pos,title='GSAS-II data display')
2612#        self._init_menus()
2613#        if name:
2614#            self.SetLabel(name)
2615#        self.Show()
2616#
2617#    def __init__(self,parent,frame,data=None,name=None, size=None,pos=None):
2618#        self.G2frame = frame
2619#        self._init_ctrls(parent,name,size,pos)
2620#        self.data = data
2621#        clientSize = wx.ClientDisplayRect()
2622#        Size = self.GetSize()
2623#        xPos = clientSize[2]-Size[0]
2624#        self.SetPosition(wx.Point(xPos,clientSize[1]+250))
2625#        self.AtomGrid = []
2626#        self.selectedRow = 0
2627#        self.lastSize = Size       
2628#        self.manualPhaseSize = None
2629#        self.userReSize = False
2630#        wx.Frame.Bind(self,wx.EVT_SIZE,self.OnReSize)
2631#           
2632#    def OnReSize(self,event):
2633#        '''Keep track of size changes for Phase windows
2634#        '''
2635#        id = self.G2frame.PatternTree.GetSelection()
2636#        try:
2637#            parent = self.G2frame.PatternTree.GetItemParent(id)
2638#        except:         #avoid bad tree item on start via gpx file selection
2639#            parent = 0
2640#        if self.userReSize and parent and self.G2frame.PatternTree.GetItemText(parent) == "Phases":
2641#            newSize = event.EventObject.GetSize()
2642#            if newSize[1] < 200: return             #avois spurious small window after Refine
2643#            if self.lastSize == newSize:
2644##                if GSASIIpath.GetConfigValue('debug'):
2645##                    print 'no save size=',self.lastSize
2646#                return
2647#            self.manualPhaseSize = newSize
2648#            self.lastSize = event.EventObject.GetSize()
2649##            if GSASIIpath.GetConfigValue('debug'):
2650##                print 'Saving Phase size=',self.manualPhaseSize
2651#                #HowDidIgetHere()
2652#        event.Skip()
2653#
2654#    def SendSizeEvent(self):
2655#        '''Prevent SendSizeEvent from overriding the saved size
2656#        '''
2657#        self.userReSize = False
2658#        wx.Frame.SendSizeEvent(self)
2659#        self.userReSize = True
2660#               
2661#    def Clear(self):
2662#        self.ClearBackground()
2663#        self.DestroyChildren()
2664                   
2665################################################################################
2666#####  Notebook Tree Item editor
2667################################################################################                 
2668def UpdateNotebook(G2frame,data):
2669    '''Called when the data tree notebook entry is selected. Allows for
2670    editing of the text in that tree entry
2671    '''
2672    def OnNoteBook(event):
2673        event.Skip()
2674        data = text.GetValue().split('\n')
2675        G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,G2frame.root,'Notebook'),data)
2676        if 'nt' not in os.name:
2677            text.AppendText('\n')
2678                   
2679    G2frame.SetLabel(G2frame.GetLabel().split('||')[0]+' || '+'Notebook')
2680    G2frame.dataWindow.ClearData()
2681    G2frame.dataWindow.SetupScrolling()
2682    mainSizer = wx.BoxSizer()
2683    text = wx.TextCtrl(parent=G2frame.dataWindow,size=G2frame.dataWindow.GetSize(),
2684        style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER | wx.TE_DONTWRAP)
2685    text.Bind(wx.EVT_TEXT_ENTER,OnNoteBook)
2686    text.Bind(wx.EVT_KILL_FOCUS,OnNoteBook)
2687    for line in data:
2688        text.AppendText(line+"\n")
2689    text.AppendText('Notebook entry @ '+time.ctime()+"\n")
2690    mainSizer.Add(text,1,wx.EXPAND)
2691    G2frame.dataWindow.SetSizer(mainSizer)
2692    G2frame.SetDataSize()
2693           
2694################################################################################
2695#####  Comments
2696################################################################################           
2697       
2698def UpdateComments(G2frame,data):                   
2699
2700    G2frame.SetLabel(G2frame.GetLabel().split('||')[0]+' || '+'Comments')
2701    G2frame.dataWindow.ClearData()
2702#    G2frame.dataWindow.SetupScrolling()
2703    text = wx.TextCtrl(parent=G2frame.dataWindow,size=G2frame.dataWindow.GetSize(),
2704        style=wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_DONTWRAP)
2705    for line in data:
2706        if '\n' not in line:
2707            text.AppendText(line+'\n')
2708        else:
2709            text.AppendText(line)
2710    mainSizer = wx.BoxSizer()
2711    mainSizer.Add(text,1,wx.EXPAND)
2712    G2frame.dataWindow.SetSizer(mainSizer)
2713    G2frame.SetDataSize()
2714           
2715################################################################################
2716#####  Controls Tree Item editor
2717################################################################################           
2718def UpdateControls(G2frame,data):
2719    '''Edit overall GSAS-II controls in main Controls data tree entry
2720    '''
2721    #patch
2722    if 'deriv type' not in data:
2723        data = {}
2724        data['deriv type'] = 'analytic Hessian'
2725        data['min dM/M'] = 0.001
2726        data['shift factor'] = 1.
2727        data['max cyc'] = 3       
2728        data['F**2'] = False
2729    if 'SVDtol' not in data:
2730        data['SVDtol'] = 1.e-6
2731    if 'shift factor' not in data:
2732        data['shift factor'] = 1.
2733    if 'max cyc' not in data:
2734        data['max cyc'] = 3
2735    if 'F**2' not in data:
2736        data['F**2'] = False
2737    if 'Author' not in data:
2738        data['Author'] = 'no name'
2739    if 'FreePrm1' not in data:
2740        data['FreePrm1'] = 'Sample humidity (%)'
2741    if 'FreePrm2' not in data:
2742        data['FreePrm2'] = 'Sample voltage (V)'
2743    if 'FreePrm3' not in data:
2744        data['FreePrm3'] = 'Applied load (MN)'
2745    if 'Copy2Next' not in data:
2746        data['Copy2Next'] = False
2747    if 'Reverse Seq' not in data:
2748        data['Reverse Seq'] = False
2749    if 'UsrReject' not in data:
2750        data['UsrReject'] = {'minF/sig':0,'MinExt':0.01,'MaxDF/F':20.,'MaxD':500.,'MinD':0.05}
2751    if 'HatomFix' not in data:
2752        data['HatomFix'] = False
2753    if 'Marquardt' not in data:
2754        data['Marquardt'] = -3
2755   
2756    #end patch
2757
2758    def SeqSizer():
2759       
2760        def OnSelectData(event):
2761            choices = GetPatternTreeDataNames(G2frame,['PWDR','HKLF',])
2762            sel = []
2763            try:
2764                if 'Seq Data' in data:
2765                    for item in data['Seq Data']:
2766                        sel.append(choices.index(item))
2767                    sel = [choices.index(item) for item in data['Seq Data']]
2768            except ValueError:  #data changed somehow - start fresh
2769                sel = []
2770            dlg = G2G.G2MultiChoiceDialog(G2frame.dataFrame, 'Sequential refinement',
2771                'Select dataset to include',choices)
2772            dlg.SetSelections(sel)
2773            names = []
2774            if dlg.ShowModal() == wx.ID_OK:
2775                for sel in dlg.GetSelections():
2776                    names.append(choices[sel])
2777                data['Seq Data'] = names               
2778                G2frame.EnableSeqRefineMenu()
2779            dlg.Destroy()
2780            wx.CallAfter(UpdateControls,G2frame,data)
2781           
2782        def OnReverse(event):
2783            data['Reverse Seq'] = reverseSel.GetValue()
2784           
2785        def OnCopySel(event):
2786            data['Copy2Next'] = copySel.GetValue() 
2787                   
2788        seqSizer = wx.BoxSizer(wx.VERTICAL)
2789        dataSizer = wx.BoxSizer(wx.HORIZONTAL)
2790        dataSizer.Add(wx.StaticText(G2frame.dataWindow,label=' Sequential Refinement: '),0,WACV)
2791        selSeqData = wx.Button(G2frame.dataWindow,-1,label=' Select data')
2792        selSeqData.Bind(wx.EVT_BUTTON,OnSelectData)
2793        dataSizer.Add(selSeqData,0,WACV)
2794        SeqData = data.get('Seq Data',[])
2795        if not SeqData:
2796            lbl = ' (no data selected)'
2797        else:
2798            lbl = ' ('+str(len(SeqData))+' dataset(s) selected)'
2799
2800        dataSizer.Add(wx.StaticText(G2frame.dataWindow,label=lbl),0,WACV)
2801        seqSizer.Add(dataSizer,0)
2802        if SeqData:
2803            selSizer = wx.BoxSizer(wx.HORIZONTAL)
2804            reverseSel = wx.CheckBox(G2frame.dataWindow,-1,label=' Reverse order?')
2805            reverseSel.Bind(wx.EVT_CHECKBOX,OnReverse)
2806            reverseSel.SetValue(data['Reverse Seq'])
2807            selSizer.Add(reverseSel,0,WACV)
2808            copySel =  wx.CheckBox(G2frame.dataWindow,-1,label=' Copy results to next histogram?')
2809            copySel.Bind(wx.EVT_CHECKBOX,OnCopySel)
2810            copySel.SetValue(data['Copy2Next'])
2811            selSizer.Add(copySel,0,WACV)
2812            seqSizer.Add(selSizer,0)
2813        return seqSizer
2814       
2815    def LSSizer():       
2816       
2817        def OnDerivType(event):
2818            data['deriv type'] = derivSel.GetValue()
2819            derivSel.SetValue(data['deriv type'])
2820            wx.CallAfter(UpdateControls,G2frame,data)
2821           
2822        def OnMaxCycles(event):
2823            data['max cyc'] = int(maxCyc.GetValue())
2824            maxCyc.SetValue(str(data['max cyc']))
2825           
2826        def OnMarqLam(event):
2827            data['Marquardt'] = int(marqLam.GetValue())
2828            marqLam.SetValue(str(data['Marquardt']))
2829                       
2830        def OnFactor(event):
2831            event.Skip()
2832            try:
2833                value = min(max(float(Factr.GetValue()),0.00001),100.)
2834            except ValueError:
2835                value = 1.0
2836            data['shift factor'] = value
2837            Factr.SetValue('%.5f'%(value))
2838           
2839        def OnFsqRef(event):
2840            data['F**2'] = fsqRef.GetValue()
2841           
2842        LSSizer = wx.FlexGridSizer(cols=4,vgap=5,hgap=5)
2843        LSSizer.Add(wx.StaticText(G2frame.dataWindow,label=' Refinement derivatives: '),0,WACV)
2844        Choice=['analytic Jacobian','numeric','analytic Hessian','Hessian SVD']   #TODO +'SVD refine' - what flags will it need?
2845        derivSel = wx.ComboBox(parent=G2frame.dataWindow,value=data['deriv type'],choices=Choice,
2846            style=wx.CB_READONLY|wx.CB_DROPDOWN)
2847        derivSel.SetValue(data['deriv type'])
2848        derivSel.Bind(wx.EVT_COMBOBOX, OnDerivType)
2849           
2850        LSSizer.Add(derivSel,0,WACV)
2851        LSSizer.Add(wx.StaticText(G2frame.dataWindow,label=' Min delta-M/M: '),0,WACV)
2852        LSSizer.Add(G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'min dM/M',nDig=(10,2,'g'),min=1.e-9,max=1.),0,WACV)
2853        if 'Hessian' in data['deriv type']:
2854            LSSizer.Add(wx.StaticText(G2frame.dataWindow,label=' Max cycles: '),0,WACV)
2855            Choice = ['0','1','2','3','5','10','15','20']
2856            maxCyc = wx.ComboBox(parent=G2frame.dataWindow,value=str(data['max cyc']),choices=Choice,
2857                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2858            maxCyc.Bind(wx.EVT_COMBOBOX, OnMaxCycles)
2859            LSSizer.Add(maxCyc,0,WACV)
2860            if 'SVD' not in data['deriv type']:
2861                LSSizer.Add(wx.StaticText(G2frame.dataWindow,label=' Initial lambda = 10**'),0,WACV)
2862                MarqChoice = ['-3','-2','-1','0','1','2','3','4']
2863                marqLam = wx.ComboBox(parent=G2frame.dataWindow,value=str(data['Marquardt']),choices=MarqChoice,
2864                    style=wx.CB_READONLY|wx.CB_DROPDOWN)
2865                marqLam.Bind(wx.EVT_COMBOBOX,OnMarqLam)
2866                LSSizer.Add(marqLam,0,WACV)
2867            LSSizer.Add(wx.StaticText(G2frame.dataWindow,label=' SVD zero tolerance:'),0,WACV)
2868            LSSizer.Add(G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'SVDtol',nDig=(10,1,'g'),min=1.e-9,max=.01),0,WACV)
2869        else:       #TODO what for SVD refine?
2870            LSSizer.Add(wx.StaticText(G2frame.dataWindow,label=' Initial shift factor: '),0,WACV)
2871            Factr = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'shift factor',nDig=(10,5),min=1.e-5,max=100.)
2872            LSSizer.Add(Factr,0,WACV)
2873        if G2frame.Sngl:
2874            userReject = data['UsrReject']
2875            usrRej = {'minF/sig':[' Min obs/sig (0-5): ',[0,5], ],'MinExt':[' Min extinct. (0-.9): ',[0,.9],],
2876                'MaxDF/F':[' Max delt-F/sig (3-1000): ',[3.,1000.],],'MaxD':[' Max d-spacing (3-500): ',[3,500],],
2877                'MinD':[' Min d-spacing (0.1-2.0): ',[0.1,2.0],]}
2878
2879            fsqRef = wx.CheckBox(G2frame.dataWindow,-1,label='Refine HKLF as F^2? ')
2880            fsqRef.SetValue(data['F**2'])
2881            fsqRef.Bind(wx.EVT_CHECKBOX,OnFsqRef)
2882            LSSizer.Add(fsqRef,0,WACV)
2883            LSSizer.Add((1,0),)
2884            for item in usrRej:
2885                LSSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=usrRej[item][0]),0,WACV)
2886                usrrej = G2G.ValidatedTxtCtrl(G2frame.dataWindow,userReject,item,nDig=(10,2),
2887                    min=usrRej[item][1][0],max=usrRej[item][1][1])
2888                LSSizer.Add(usrrej,0,WACV)
2889        return LSSizer
2890       
2891    def AuthSizer():
2892
2893        def OnAuthor(event):
2894            event.Skip()
2895            data['Author'] = auth.GetValue()
2896
2897        Author = data['Author']
2898        authSizer = wx.BoxSizer(wx.HORIZONTAL)
2899        authSizer.Add(wx.StaticText(G2frame.dataWindow,label=' CIF Author (last, first):'),0,WACV)
2900        auth = wx.TextCtrl(G2frame.dataWindow,-1,value=Author,style=wx.TE_PROCESS_ENTER)
2901        auth.Bind(wx.EVT_TEXT_ENTER,OnAuthor)
2902        auth.Bind(wx.EVT_KILL_FOCUS,OnAuthor)
2903        authSizer.Add(auth,0,WACV)
2904        return authSizer
2905       
2906    if 'SVD' in data['deriv type']:
2907        G2frame.GetStatusBar().SetStatusText('Hessian SVD not recommended for initial refinements; use analytic Hessian or Jacobian')
2908    else:
2909        G2frame.GetStatusBar().SetStatusText('')
2910    G2frame.dataWindow.ClearData()
2911    G2frame.dataWindow.SetupScrolling()
2912    G2frame.SetLabel(G2frame.GetLabel().split('||')[0]+' || '+'Controls')
2913    SetDataMenuBar(G2frame,G2frame.dataWindow.ControlsMenu)
2914    mainSizer = wx.BoxSizer(wx.VERTICAL)
2915    mainSizer.Add((5,5),0)
2916    mainSizer.Add(wx.StaticText(G2frame.dataWindow,label=' Refinement Controls:'),0,WACV)   
2917    mainSizer.Add(LSSizer())
2918    mainSizer.Add((5,5),0)
2919    mainSizer.Add(SeqSizer())
2920    mainSizer.Add((5,5),0)
2921    mainSizer.Add(AuthSizer())
2922    mainSizer.Add((5,5),0)
2923       
2924    mainSizer.Layout()
2925    mainSizer.FitInside(G2frame.dataWindow)   
2926    G2frame.dataWindow.SetSizer(mainSizer)
2927    G2frame.dataWindow.SetAutoLayout(1)
2928    G2frame.SetDataSize()
2929     
2930################################################################################
2931#####  Display of Sequential Results
2932################################################################################           
2933       
2934def UpdateSeqResults(G2frame,data,prevSize=None):
2935    """
2936    Called when the Sequential Results data tree entry is selected
2937    to show results from a sequential refinement.
2938   
2939    :param wx.Frame G2frame: main GSAS-II data tree windows
2940
2941    :param dict data: a dictionary containing the following items: 
2942
2943            * 'histNames' - list of histogram names in order as processed by Sequential Refinement
2944            * 'varyList' - list of variables - identical over all refinements in sequence
2945              note that this is the original list of variables, prior to processing
2946              constraints.
2947            * 'variableLabels' -- a dict of labels to be applied to each parameter
2948              (this is created as an empty dict if not present in data).
2949            * keyed by histName - dictionaries for all data sets processed, which contains:
2950
2951              * 'variables'- result[0] from leastsq call
2952              * 'varyList' - list of variables passed to leastsq call (not same as above)
2953              * 'sig' - esds for variables
2954              * 'covMatrix' - covariance matrix from individual refinement
2955              * 'title' - histogram name; same as dict item name
2956              * 'newAtomDict' - new atom parameters after shifts applied
2957              * 'newCellDict' - refined cell parameters after shifts to A0-A5 from Dij terms applied'
2958    """
2959    def GetSampleParms():
2960        '''Make a dictionary of the sample parameters that are not the same over the
2961        refinement series. Controls here is local
2962        '''
2963        if 'IMG' in histNames[0]:
2964            sampleParmDict = {'Sample load':[],}
2965        else:
2966            sampleParmDict = {'Temperature':[],'Pressure':[],'Time':[],
2967                'FreePrm1':[],'FreePrm2':[],'FreePrm3':[],'Omega':[],
2968                'Chi':[],'Phi':[],'Azimuth':[],}
2969        Controls = G2frame.PatternTree.GetItemPyData(
2970            GetPatternTreeItemId(G2frame,G2frame.root, 'Controls'))
2971        sampleParm = {}
2972        for name in histNames:
2973            if 'IMG' in name:
2974                if name not in data:
2975                    continue
2976                for item in sampleParmDict:
2977                    sampleParmDict[item].append(data[name]['parmDict'].get(item,0))
2978            else:
2979                if 'PDF' in name:
2980                    name = 'PWDR' + name[4:]
2981                Id = GetPatternTreeItemId(G2frame,G2frame.root,name)
2982                if Id:
2983                    sampleData = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,Id,'Sample Parameters'))
2984                    for item in sampleParmDict:
2985                        sampleParmDict[item].append(sampleData.get(item,0))
2986        for item in sampleParmDict:
2987            if sampleParmDict[item]:
2988                frstValue = sampleParmDict[item][0]
2989                if np.any(np.array(sampleParmDict[item])-frstValue):
2990                    if item.startswith('FreePrm'):
2991                        sampleParm[Controls[item]] = sampleParmDict[item]
2992                    else:
2993                        sampleParm[item] = sampleParmDict[item]
2994        return sampleParm
2995
2996    def GetColumnInfo(col):
2997        '''returns column label, lists of values and errors (or None) for each column in the table
2998        for plotting. The column label is reformatted from Unicode to MatPlotLib encoding
2999        '''
3000        colName = G2frame.SeqTable.GetColLabelValue(col)
3001        plotName = variableLabels.get(colName,colName)
3002        plotName = plotSpCharFix(plotName)
3003        return plotName,G2frame.colList[col],G2frame.colSigs[col]
3004           
3005    def PlotSelect(event):
3006        'Plots a row (covariance) or column on double-click'
3007        cols = G2frame.dataDisplay.GetSelectedCols()
3008        rows = G2frame.dataDisplay.GetSelectedRows()
3009        if cols:
3010            G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
3011        elif rows:
3012            name = histNames[rows[0]]       #only does 1st one selected
3013            G2plt.PlotCovariance(G2frame,data[name])
3014        else:
3015            G2frame.ErrorDialog(
3016                'Select row or columns',
3017                'Nothing selected in table. Click on column or row label(s) to plot. N.B. Grid selection can be a bit funky.'
3018                )
3019           
3020    def OnPlotSelSeq(event):
3021        'plot the selected columns or row from menu command'
3022        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
3023        rows = G2frame.dataDisplay.GetSelectedRows()
3024        if cols:
3025            G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
3026        elif rows:
3027            name = histNames[rows[0]]       #only does 1st one selected
3028            G2plt.PlotCovariance(G2frame,data[name])
3029        else:
3030            G2frame.ErrorDialog(
3031                'Select columns',
3032                'No columns or rows selected in table. Click on row or column labels to select fields for plotting.'
3033                )
3034               
3035    def OnAveSelSeq(event):
3036        'average the selected columns from menu command'
3037        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
3038        useCol = -np.array(G2frame.SeqTable.GetColValues(0),dtype=bool)
3039        if cols:
3040            for col in cols:
3041                items = GetColumnInfo(col)[1]
3042                noneMask = np.array([item is None for item in items])
3043                info = ma.array(items,mask=useCol+noneMask)
3044                ave = ma.mean(ma.compressed(info))
3045                sig = ma.std(ma.compressed(info))
3046                print ' Average for '+G2frame.SeqTable.GetColLabelValue(col)+': '+'%.6g'%(ave)+' +/- '+'%.6g'%(sig)
3047        else:
3048            G2frame.ErrorDialog(
3049                'Select columns',
3050                'No columns selected in table. Click on column labels to select fields for averaging.'
3051                )
3052               
3053    def OnRenameSelSeq(event):
3054        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
3055        colNames = [G2frame.SeqTable.GetColLabelValue(c) for c in cols]
3056        newNames = colNames[:]
3057        for i,name in enumerate(colNames):
3058            if name in variableLabels:
3059                newNames[i] = variableLabels[name]
3060        if not cols:
3061            G2frame.ErrorDialog('Select columns',
3062                'No columns selected in table. Click on column labels to select fields for rename.')
3063            return
3064        dlg = G2G.MultiStringDialog(G2frame.dataDisplay,'Set column names',colNames,newNames)
3065        if dlg.Show():
3066            newNames = dlg.GetValues()           
3067            variableLabels.update(dict(zip(colNames,newNames)))
3068        data['variableLabels'] = variableLabels
3069        dlg.Destroy()
3070        UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3071        G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
3072           
3073    def OnReOrgSelSeq(event):
3074        'Reorder the columns -- probably not fully implemented'
3075        G2G.GetItemOrder(G2frame,VaryListChanges,vallookup,posdict)   
3076        UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3077
3078    def OnSaveSelSeqCSV(event):
3079        'export the selected columns to a .csv file from menu command'
3080        OnSaveSelSeq(event,csv=True)
3081       
3082    def OnSaveSeqCSV(event):
3083        'export all columns to a .csv file from menu command'
3084        OnSaveSelSeq(event,csv=True,allcols=True)
3085       
3086    def OnSaveSelSeq(event,csv=False,allcols=False):
3087        'export the selected columns to a .txt or .csv file from menu command'
3088        def WriteCSV():
3089            def WriteList(headerItems):
3090                line = ''
3091                for lbl in headerItems:
3092                    if line: line += ','
3093                    line += '"'+lbl+'"'
3094                return line
3095            head = ['name']
3096            for col in cols:
3097                item = G2frame.SeqTable.GetColLabelValue(col)
3098                # get rid of labels that have Unicode characters
3099                if not all([ord(c) < 128 and ord(c) != 0 for c in item]): item = '?'
3100                if col in havesig:
3101                    head += [item,'esd-'+item]
3102                else:
3103                    head += [item]
3104            SeqFile.write(WriteList(head)+'\n')
3105            for row,name in enumerate(saveNames):
3106                line = '"'+saveNames[row]+'"'
3107                for col in cols:
3108                    if col in havesig:
3109                        line += ','+str(saveData[col][row])+','+str(saveSigs[col][row])
3110                    else:
3111                        line += ','+str(saveData[col][row])
3112                SeqFile.write(line+'\n')
3113        def WriteSeq():
3114            lenName = len(saveNames[0])
3115            line = %s  '%('name'.center(lenName))
3116            for col in cols:
3117                item = G2frame.SeqTable.GetColLabelValue(col)
3118                if col in havesig:
3119                    line += ' %12s %12s '%(item.center(12),'esd'.center(12))
3120                else:
3121                    line += ' %12s '%(item.center(12))
3122            SeqFile.write(line+'\n')
3123            for row,name in enumerate(saveNames):
3124                line = " '%s' "%(saveNames[row])
3125                for col in cols:
3126                    if col in havesig:
3127                        line += ' %12.6f %12.6f '%(saveData[col][row],saveSigs[col][row])
3128                    else:
3129                        line += ' %12.6f '%saveData[col][row]
3130                SeqFile.write(line+'\n')
3131
3132        # start of OnSaveSelSeq code
3133        if allcols:
3134            cols = range(G2frame.SeqTable.GetNumberCols())
3135        else:
3136            cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
3137        nrows = G2frame.SeqTable.GetNumberRows()
3138        if not cols:
3139            G2frame.ErrorDialog('Select columns',
3140                             'No columns selected in table. Click on column labels to select fields for output.')
3141            return
3142        saveNames = [G2frame.SeqTable.GetRowLabelValue(r) for r in range(nrows)]
3143        saveData = {}
3144        saveSigs = {}
3145        havesig = []
3146        for col in cols:
3147            name,vals,sigs = GetColumnInfo(col)
3148            saveData[col] = vals
3149            if sigs:
3150                havesig.append(col)
3151                saveSigs[col] = sigs
3152        if csv:
3153            wild = 'CSV output file (*.csv)|*.csv'
3154        else:
3155            wild = 'Text output file (*.txt)|*.txt'
3156        pth = G2G.GetExportPath(G2frame)
3157        dlg = wx.FileDialog(
3158            G2frame,
3159            'Choose text output file for your selection', pth, '', 
3160            wild,wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3161        try:
3162            if dlg.ShowModal() == wx.ID_OK:
3163                SeqTextFile = dlg.GetPath()
3164                SeqTextFile = G2IO.FileDlgFixExt(dlg,SeqTextFile) 
3165                SeqFile = open(SeqTextFile,'w')
3166                if csv:
3167                    WriteCSV()
3168                else:
3169                    WriteSeq()
3170                SeqFile.close()
3171        finally:
3172            dlg.Destroy()
3173               
3174    def striphist(var,insChar=''):
3175        'strip a histogram number from a var name'
3176        sv = var.split(':')
3177        if len(sv) <= 1: return var
3178        if sv[1]:
3179            sv[1] = insChar
3180        return ':'.join(sv)
3181       
3182    def plotSpCharFix(lbl):
3183        'Change selected unicode characters to their matplotlib equivalent'
3184        for u,p in [
3185            (u'\u03B1',r'$\alpha$'),
3186            (u'\u03B2',r'$\beta$'),
3187            (u'\u03B3',r'$\gamma$'),
3188            (u'\u0394\u03C7',r'$\Delta\chi$'),
3189            ]:
3190            lbl = lbl.replace(u,p)
3191        return lbl
3192   
3193    def SelectXaxis():
3194        'returns a selected column number (or None) as the X-axis selection'
3195        ncols = G2frame.SeqTable.GetNumberCols()
3196        colNames = [G2frame.SeqTable.GetColLabelValue(r) for r in range(ncols)]
3197        dlg = G2G.G2SingleChoiceDialog(
3198            G2frame.dataDisplay,
3199            'Select x-axis parameter for plot or Cancel for sequence number',
3200            'Select X-axis',
3201            colNames)
3202        try:
3203            if dlg.ShowModal() == wx.ID_OK:
3204                col = dlg.GetSelection()
3205            else:
3206                col = None
3207        finally:
3208            dlg.Destroy()
3209        return col
3210   
3211    def EnablePseudoVarMenus():
3212        'Enables or disables the PseudoVar menu items that require existing defs'
3213        if data['SeqPseudoVars']:
3214            val = True
3215        else:
3216            val = False
3217        G2frame.dataWindow.SequentialPvars.Enable(wxDELSEQVAR,val)
3218        G2frame.dataWindow.SequentialPvars.Enable(wxEDITSEQVAR,val)
3219
3220    def DelPseudoVar(event):
3221        'Ask the user to select a pseudo var expression to delete'
3222        choices = data['SeqPseudoVars'].keys()
3223        selected = G2G.ItemSelector(
3224            choices,G2frame.dataFrame,
3225            multiple=True,
3226            title='Select expressions to remove',
3227            header='Delete expression')
3228        if selected is None: return
3229        for item in selected:
3230            del data['SeqPseudoVars'][choices[item]]
3231        if selected:
3232            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3233
3234    def EditPseudoVar(event):
3235        'Edit an existing pseudo var expression'
3236        choices = data['SeqPseudoVars'].keys()
3237        if len(choices) == 1:
3238            selected = 0
3239        else:
3240            selected = G2G.ItemSelector(
3241                choices,G2frame.dataFrame,
3242                multiple=False,
3243                title='Select an expression to edit',
3244                header='Edit expression')
3245        if selected is not None:
3246            dlg = G2exG.ExpressionDialog(
3247                G2frame.dataDisplay,PSvarDict,
3248                data['SeqPseudoVars'][choices[selected]],
3249                header="Edit the PseudoVar expression",
3250                VarLabel="PseudoVar #"+str(selected+1),
3251                fit=False)
3252            newobj = dlg.Show(True)
3253            if newobj:
3254                calcobj = G2obj.ExpressionCalcObj(newobj)
3255                del data['SeqPseudoVars'][choices[selected]]
3256                data['SeqPseudoVars'][calcobj.eObj.expression] = newobj
3257                UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3258       
3259    def AddNewPseudoVar(event):
3260        'Create a new pseudo var expression'
3261        dlg = G2exG.ExpressionDialog(G2frame.dataDisplay,PSvarDict,
3262            header='Enter an expression for a PseudoVar here',
3263            VarLabel = "New PseudoVar",fit=False)
3264        obj = dlg.Show(True)
3265        dlg.Destroy()
3266        if obj:
3267            calcobj = G2obj.ExpressionCalcObj(obj)
3268            data['SeqPseudoVars'][calcobj.eObj.expression] = obj
3269            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3270           
3271    def AddNewDistPseudoVar(event):
3272        obj = None
3273        dlg = G2exG.BondDialog(
3274            G2frame.dataDisplay,Phases,PSvarDict,
3275            header='Select a Bond here',
3276            VarLabel = "New Bond")
3277        if dlg.ShowModal() == wx.ID_OK:
3278            pName,Oatom,Tatom = dlg.GetSelection()
3279            if Tatom:
3280                Phase = Phases[pName]
3281                General = Phase['General']
3282                cx,ct = General['AtomPtrs'][:2]
3283                pId = Phase['pId']
3284                SGData = General['SGData']
3285                sB = Tatom.find('(')+1
3286                symNo = 0
3287                if sB:
3288                    sF = Tatom.find(')')
3289                    symNo = int(Tatom[sB:sF])
3290                cellNo = [0,0,0]
3291                cB = Tatom.find('[')
3292                if cB>0:
3293                    cF = Tatom.find(']')+1
3294                    cellNo = eval(Tatom[cB:cF])
3295                Atoms = Phase['Atoms']
3296                aNames = [atom[ct-1] for atom in Atoms]
3297                oId = aNames.index(Oatom)
3298                tId = aNames.index(Tatom.split(' +')[0])
3299                # create an expression object
3300                obj = G2obj.ExpressionObj()
3301                obj.expression = 'Dist(%s,\n%s)'%(Oatom,Tatom.split(' d=')[0].replace(' ',''))
3302                obj.distance_dict = {'pId':pId,'SGData':SGData,'symNo':symNo,'cellNo':cellNo}
3303                obj.distance_atoms = [oId,tId]
3304        else: 
3305            dlg.Destroy()
3306            return
3307        dlg.Destroy()
3308        if obj:
3309            data['SeqPseudoVars'][obj.expression] = obj
3310            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3311
3312    def AddNewAnglePseudoVar(event):
3313        obj = None
3314        dlg = G2exG.AngleDialog(
3315            G2frame.dataDisplay,Phases,PSvarDict,
3316            header='Enter an Angle here',
3317            VarLabel = "New Angle")
3318        if dlg.ShowModal() == wx.ID_OK:
3319            pName,Oatom,Tatoms = dlg.GetSelection()
3320            if Tatoms:
3321                Phase = Phases[pName]
3322                General = Phase['General']
3323                cx,ct = General['AtomPtrs'][:2]
3324                pId = Phase['pId']
3325                SGData = General['SGData']
3326                Atoms = Phase['Atoms']
3327                aNames = [atom[ct-1] for atom in Atoms]
3328                tIds = []
3329                symNos = []
3330                cellNos = []
3331                oId = aNames.index(Oatom)
3332                Tatoms = Tatoms.split(';')
3333                for Tatom in Tatoms:
3334                    sB = Tatom.find('(')+1
3335                    symNo = 0
3336                    if sB:
3337                        sF = Tatom.find(')')
3338                        symNo = int(Tatom[sB:sF])
3339                    symNos.append(symNo)
3340                    cellNo = [0,0,0]
3341                    cB = Tatom.find('[')
3342                    if cB>0:
3343                        cF = Tatom.find(']')+1
3344                        cellNo = eval(Tatom[cB:cF])
3345                    cellNos.append(cellNo)
3346                    tIds.append(aNames.index(Tatom.split('+')[0]))
3347                # create an expression object
3348                obj = G2obj.ExpressionObj()
3349                obj.expression = 'Angle(%s,%s,\n%s)'%(Tatoms[0],Oatom,Tatoms[1])
3350                obj.angle_dict = {'pId':pId,'SGData':SGData,'symNo':symNos,'cellNo':cellNos}
3351                obj.angle_atoms = [oId,tIds]
3352        else: 
3353            dlg.Destroy()
3354            return
3355        dlg.Destroy()
3356        if obj:
3357            data['SeqPseudoVars'][obj.expression] = obj
3358            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3359           
3360    def UpdateParmDict(parmDict):
3361        '''generate the atom positions and the direct & reciprocal cell values,
3362        because they might be needed to evaluate the pseudovar
3363        '''
3364        Ddict = dict(zip(['D11','D22','D33','D12','D13','D23'],
3365                         ['A'+str(i) for i in range(6)])
3366                     )
3367        delList = []
3368        phaselist = []
3369        for item in parmDict: 
3370            if ':' not in item: continue
3371            key = item.split(':')
3372            if len(key) < 3: continue
3373            # remove the dA[xyz] terms, they would only bring confusion
3374            if key[0] and key[0] not in phaselist: phaselist.append(key[0])
3375            if key[2].startswith('dA'):
3376                delList.append(item)
3377            # compute and update the corrected reciprocal cell terms using the Dij values
3378            elif key[2] in Ddict:
3379                akey = key[0]+'::'+Ddict[key[2]]
3380                parmDict[akey] -= parmDict[item]
3381                delList.append(item)
3382        for item in delList:
3383            del parmDict[item]               
3384        for i in phaselist:
3385            pId = int(i)
3386            # apply cell symmetry
3387            A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata[pId],parmDict,zeroDict[pId])
3388            # convert to direct cell & add the unique terms to the dictionary
3389            for i,val in enumerate(G2lat.A2cell(A)):
3390                if i in uniqCellIndx[pId]:
3391                    lbl = str(pId)+'::'+cellUlbl[i]
3392                    parmDict[lbl] = val
3393            lbl = str(pId)+'::'+'Vol'
3394            parmDict[lbl] = G2lat.calc_V(A)
3395        return parmDict
3396
3397    def EvalPSvarDeriv(calcobj,parmDict,sampleDict,var,ESD):
3398        '''Evaluate an expression derivative with respect to a
3399        GSAS-II variable name.
3400
3401        Note this likely could be faster if the loop over calcobjs were done
3402        inside after the Dict was created.
3403        '''
3404        if not ESD:
3405            return 0.
3406        step = ESD/10
3407        Ddict = dict(zip(['D11','D22','D33','D12','D13','D23'],
3408                         ['A'+str(i) for i in range(6)])
3409                     )
3410        results = []
3411        phaselist = []
3412        VparmDict = sampleDict.copy()
3413        for incr in step,-step:
3414            VparmDict.update(parmDict.copy())           
3415            # as saved, the parmDict has updated 'A[xyz]' values, but 'dA[xyz]'
3416            # values are not zeroed: fix that!
3417            VparmDict.update({item:0.0 for item in parmDict if 'dA' in item})
3418            VparmDict[var] += incr
3419            G2mv.Dict2Map(VparmDict,[]) # apply constraints
3420            # generate the atom positions and the direct & reciprocal cell values now, because they might
3421            # needed to evaluate the pseudovar
3422            for item in VparmDict:
3423                if item in sampleDict:
3424                    continue 
3425                if ':' not in item: continue
3426                key = item.split(':')
3427                if len(key) < 3: continue
3428                # apply any new shifts to atom positions
3429                if key[2].startswith('dA'):
3430                    VparmDict[''.join(item.split('d'))] += VparmDict[item]
3431                    VparmDict[item] = 0.0
3432                # compute and update the corrected reciprocal cell terms using the Dij values
3433                if key[2] in Ddict:
3434                    if key[0] not in phaselist: phaselist.append(key[0])
3435                    akey = key[0]+'::'+Ddict[key[2]]
3436                    VparmDict[akey] -= VparmDict[item]
3437            for i in phaselist:
3438                pId = int(i)
3439                # apply cell symmetry
3440                A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata[pId],VparmDict,zeroDict[pId])
3441                # convert to direct cell & add the unique terms to the dictionary
3442                for i,val in enumerate(G2lat.A2cell(A)):
3443                    if i in uniqCellIndx[pId]:
3444                        lbl = str(pId)+'::'+cellUlbl[i]
3445                        VparmDict[lbl] = val
3446                lbl = str(pId)+'::'+'Vol'
3447                VparmDict[lbl] = G2lat.calc_V(A)
3448            # dict should be fully updated, use it & calculate
3449            calcobj.SetupCalc(VparmDict)
3450            results.append(calcobj.EvalExpression())
3451        if None in results:
3452            return None
3453        return (results[0] - results[1]) / (2.*step)
3454       
3455    def EnableParFitEqMenus():
3456        'Enables or disables the Parametric Fit menu items that require existing defs'
3457        if data['SeqParFitEqList']:
3458            val = True
3459        else:
3460            val = False
3461        G2frame.dataFrame.SequentialPfit.Enable(wxDELPARFIT,val)
3462        G2frame.dataFrame.SequentialPfit.Enable(wxEDITPARFIT,val)
3463        G2frame.dataFrame.SequentialPfit.Enable(wxDOPARFIT,val)
3464
3465    def ParEqEval(Values,calcObjList,varyList):
3466        '''Evaluate the parametric expression(s)
3467        :param list Values: a list of values for each variable parameter
3468        :param list calcObjList: a list of :class:`GSASIIobj.ExpressionCalcObj`
3469          expression objects to evaluate
3470        :param list varyList: a list of variable names for each value in Values
3471        '''
3472        result = []
3473        for calcobj in calcObjList:
3474            calcobj.UpdateVars(varyList,Values)
3475            if calcobj.depSig:
3476                result.append((calcobj.depVal-calcobj.EvalExpression())/calcobj.depSig)
3477            else:
3478                result.append(calcobj.depVal-calcobj.EvalExpression())
3479        return result
3480
3481    def DoParEqFit(event,eqObj=None):
3482        'Parametric fit minimizer'
3483        varyValueDict = {} # dict of variables and their initial values
3484        calcObjList = [] # expression objects, ready to go for each data point
3485        if eqObj is not None:
3486            eqObjList = [eqObj,]
3487        else:
3488            eqObjList = data['SeqParFitEqList']
3489        UseFlags = G2frame.SeqTable.GetColValues(0)         
3490        for obj in eqObjList:
3491            # assemble refined vars for this equation
3492            varyValueDict.update({var:val for var,val in obj.GetVariedVarVal()})
3493            # lookup dependent var position
3494            depVar = obj.GetDepVar()
3495            if depVar in colLabels:
3496                indx = colLabels.index(depVar)
3497            else:
3498                raise Exception('Dependent variable '+depVar+' not found')
3499            # assemble a list of the independent variables
3500            indepVars = obj.GetIndependentVars()
3501            # loop over each datapoint
3502            for j,row in enumerate(zip(*G2frame.colList)):
3503                if not UseFlags[j]: continue
3504                # assemble equations to fit
3505                calcobj = G2obj.ExpressionCalcObj(obj)
3506                # prepare a dict of needed independent vars for this expression
3507                indepVarDict = {var:row[i] for i,var in enumerate(colLabels) if var in indepVars}
3508                calcobj.SetupCalc(indepVarDict)               
3509                # values and sigs for current value of dependent var
3510                if row[indx] is None: continue
3511                calcobj.depVal = row[indx]
3512                calcobj.depSig = G2frame.colSigs[indx][j]
3513                calcObjList.append(calcobj)
3514        # varied parameters
3515        varyList = varyValueDict.keys()
3516        values = varyValues = [varyValueDict[key] for key in varyList]
3517        if not varyList:
3518            print 'no variables to refine!'
3519            return
3520        try:
3521            result = so.leastsq(ParEqEval,varyValues,full_output=True,   #ftol=Ftol,
3522                args=(calcObjList,varyList))
3523            values = result[0]
3524            covar = result[1]
3525            if covar is None:
3526                raise Exception
3527            chisq = np.sum(result[2]['fvec']**2)
3528            GOF = np.sqrt(chisq/(len(calcObjList)-len(varyList)))
3529            esdDict = {}
3530            for i,avar in enumerate(varyList):
3531                esdDict[avar] = np.sqrt(covar[i,i])
3532        except:
3533            print('====> Fit failed')
3534            return
3535        print('==== Fit Results ====')
3536        print '  chisq =  %.2f, GOF = %.2f'%(chisq,GOF)
3537        for obj in eqObjList:
3538            obj.UpdateVariedVars(varyList,values)
3539            ind = '      '
3540            print('  '+obj.GetDepVar()+' = '+obj.expression)
3541            for var in obj.assgnVars:
3542                print(ind+var+' = '+obj.assgnVars[var])
3543            for var in obj.freeVars:
3544                avar = "::"+obj.freeVars[var][0]
3545                val = obj.freeVars[var][1]
3546                if obj.freeVars[var][2]:
3547                    print(ind+var+' = '+avar + " = " + G2mth.ValEsd(val,esdDict[avar]))
3548                else:
3549                    print(ind+var+' = '+avar + " =" + G2mth.ValEsd(val,0))
3550        # create a plot for each parametric variable
3551        for fitnum,obj in enumerate(eqObjList):
3552            calcobj = G2obj.ExpressionCalcObj(obj)
3553            # lookup dependent var position
3554            indx = colLabels.index(obj.GetDepVar())
3555            # assemble a list of the independent variables
3556            indepVars = obj.GetIndependentVars()           
3557            # loop over each datapoint
3558            fitvals = []
3559            for j,row in enumerate(zip(*G2frame.colList)):
3560                calcobj.SetupCalc({var:row[i] for i,var in enumerate(colLabels) if var in indepVars})
3561                fitvals.append(calcobj.EvalExpression())
3562            G2plt.PlotSelectedSequence(G2frame,[indx],GetColumnInfo,SelectXaxis,fitnum,fitvals)
3563
3564    def SingleParEqFit(eqObj):
3565        DoParEqFit(None,eqObj)
3566
3567    def DelParFitEq(event):
3568        'Ask the user to select function to delete'
3569        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in data['SeqParFitEqList']]
3570        selected = G2G.ItemSelector(
3571            txtlst,G2frame.dataFrame,
3572            multiple=True,
3573            title='Select a parametric equation(s) to remove',
3574            header='Delete equation')
3575        if selected is None: return
3576        data['SeqParFitEqList'] = [obj for i,obj in enumerate(data['SeqParFitEqList']) if i not in selected]
3577        EnableParFitEqMenus()
3578        if data['SeqParFitEqList']: DoParEqFit(event)
3579       
3580    def EditParFitEq(event):
3581        'Edit an existing parametric equation'
3582        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in data['SeqParFitEqList']]
3583        if len(txtlst) == 1:
3584            selected = 0
3585        else:
3586            selected = G2G.ItemSelector(
3587                txtlst,G2frame.dataFrame,
3588                multiple=False,
3589                title='Select a parametric equation to edit',
3590                header='Edit equation')
3591        if selected is not None:
3592            dlg = G2exG.ExpressionDialog(G2frame.dataDisplay,VarDict,
3593                data['SeqParFitEqList'][selected],depVarDict=VarDict,
3594                header="Edit the formula for this minimization function",
3595                ExtraButton=['Fit',SingleParEqFit])
3596            newobj = dlg.Show(True)
3597            if newobj:
3598                data['SeqParFitEqList'][selected] = newobj
3599                EnableParFitEqMenus()
3600            if data['SeqParFitEqList']: DoParEqFit(event)
3601
3602    def AddNewParFitEq(event):
3603        'Create a new parametric equation to be fit to sequential results'
3604
3605        # compile the variable names used in previous freevars to avoid accidental name collisions
3606        usedvarlist = []
3607        for obj in data['SeqParFitEqList']:
3608            for var in obj.freeVars:
3609                if obj.freeVars[var][0] not in usedvarlist: usedvarlist.append(obj.freeVars[var][0])
3610
3611        dlg = G2exG.ExpressionDialog(G2frame.dataDisplay,VarDict,depVarDict=VarDict,
3612            header='Define an equation to minimize in the parametric fit',
3613            ExtraButton=['Fit',SingleParEqFit],usedVars=usedvarlist)
3614        obj = dlg.Show(True)
3615        dlg.Destroy()
3616        if obj:
3617            data['SeqParFitEqList'].append(obj)
3618            EnableParFitEqMenus()
3619            if data['SeqParFitEqList']: DoParEqFit(event)
3620               
3621    def CopyParFitEq(event):
3622        'Copy an existing parametric equation to be fit to sequential results'
3623        # compile the variable names used in previous freevars to avoid accidental name collisions
3624        usedvarlist = []
3625        for obj in data['SeqParFitEqList']:
3626            for var in obj.freeVars:
3627                if obj.freeVars[var][0] not in usedvarlist: usedvarlist.append(obj.freeVars[var][0])
3628        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in data['SeqParFitEqList']]
3629        if len(txtlst) == 1:
3630            selected = 0
3631        else:
3632            selected = G2G.ItemSelector(
3633                txtlst,G2frame.dataFrame,
3634                multiple=False,
3635                title='Select a parametric equation to copy',
3636                header='Copy equation')
3637        if selected is not None:
3638            newEqn = copy.deepcopy(data['SeqParFitEqList'][selected])
3639            for var in newEqn.freeVars:
3640                newEqn.freeVars[var][0] = G2obj.MakeUniqueLabel(newEqn.freeVars[var][0],usedvarlist)
3641            dlg = G2exG.ExpressionDialog(
3642                G2frame.dataDisplay,VarDict,newEqn,depVarDict=VarDict,
3643                header="Edit the formula for this minimization function",
3644                ExtraButton=['Fit',SingleParEqFit])
3645            newobj = dlg.Show(True)
3646            if newobj:
3647                data['SeqParFitEqList'].append(newobj)
3648                EnableParFitEqMenus()
3649            if data['SeqParFitEqList']: DoParEqFit(event)
3650                                           
3651    def GridSetToolTip(row,col):
3652        '''Routine to show standard uncertainties for each element in table
3653        as a tooltip
3654        '''
3655        if G2frame.colSigs[col]:
3656            return u'\u03c3 = '+str(G2frame.colSigs[col][row])
3657        return ''
3658       
3659    def GridColLblToolTip(col):
3660        '''Define a tooltip for a column. This will be the user-entered value
3661        (from data['variableLabels']) or the default name
3662        '''
3663        if col < 0 or col > len(colLabels):
3664            print 'Illegal column #',col
3665            return
3666        var = colLabels[col]
3667        return variableLabels.get(var,G2obj.fmtVarDescr(var))
3668       
3669    def SetLabelString(event):
3670        '''Define or edit the label for a column in the table, to be used
3671        as a tooltip and for plotting
3672        '''
3673        col = event.GetCol()
3674        if col < 0 or col > len(colLabels):
3675            return
3676        var = colLabels[col]
3677        lbl = variableLabels.get(var,G2obj.fmtVarDescr(var))
3678        dlg = G2G.SingleStringDialog(G2frame.dataFrame,'Set variable label',
3679                                 'Set a new name for variable '+var,lbl,size=(400,-1))
3680        if dlg.Show():
3681            variableLabels[var] = dlg.GetValue()
3682        dlg.Destroy()
3683
3684    def DoSequentialExport(event):
3685        '''Event handler for all Sequential Export menu items
3686        '''
3687        vals = G2frame.dataFrame.SeqExportLookup.get(event.GetId())
3688        if vals is None:
3689            print('Error: Id not found. This should not happen!')
3690        G2IO.ExportSequential(G2frame,data,*vals)
3691
3692    def onSelectSeqVars(event):
3693        '''Select which variables will be shown in table'''
3694        dlg = G2G.G2MultiChoiceDialog(G2frame.dataFrame, 'Select columns to hide',
3695                'Hide columns',colLabels[1:])
3696        if dlg.ShowModal() == wx.ID_OK:
3697            G2frame.SeqTblHideList = [colLabels[1:][sel] for sel in dlg.GetSelections()]
3698            dlg.Destroy()
3699            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3700        else:
3701            dlg.Destroy()
3702   
3703    #def GridRowLblToolTip(row): return 'Row ='+str(row)
3704   
3705    # lookup table for unique cell parameters by symmetry
3706    cellGUIlist = [
3707        [['m3','m3m'],(0,)],
3708        [['3R','3mR'],(0,3)],
3709        [['3','3m1','31m','6/m','6/mmm','4/m','4/mmm'],(0,2)],
3710        [['mmm'],(0,1,2)],
3711        [['2/m'+'a'],(0,1,2,3)],
3712        [['2/m'+'b'],(0,1,2,4)],
3713        [['2/m'+'c'],(0,1,2,5)],
3714        [['-1'],(0,1,2,3,4,5)],
3715        ]
3716    # cell labels
3717    cellUlbl = ('a','b','c',u'\u03B1',u'\u03B2',u'\u03B3') # unicode a,b,c,alpha,beta,gamma
3718
3719    #======================================================================
3720    # start processing sequential results here (UpdateSeqResults)
3721    #======================================================================
3722    if not data:
3723        print 'No sequential refinement results'
3724        return
3725    variableLabels = data.get('variableLabels',{})
3726    data['variableLabels'] = variableLabels
3727    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
3728    Controls = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.root,'Controls'))
3729    # create a place to store Pseudo Vars & Parametric Fit functions, if not present
3730    if 'SeqPseudoVars' not in data: data['SeqPseudoVars'] = {}
3731    if 'SeqParFitEqList' not in data: data['SeqParFitEqList'] = []
3732    histNames = data['histNames']
3733    if G2frame.dataDisplay:
3734        G2frame.dataDisplay.Destroy()
3735    G2frame.GetStatusBar().SetStatusText("Select column to export; Double click on column to plot data; on row for Covariance")
3736    sampleParms = GetSampleParms()
3737
3738    # make dict of varied atom coords keyed by absolute position
3739    newAtomDict = data[histNames[0]].get('newAtomDict',{}) # dict with atom positions; relative & absolute
3740    # Possible error: the next might need to be data[histNames[0]]['varyList']
3741    # error will arise if there constraints on coordinates?
3742    atomLookup = {newAtomDict[item][0]:item for item in newAtomDict if item in data['varyList']}
3743   
3744    # make dict of varied cell parameters equivalents
3745    ESDlookup = {} # provides the Dij term for each Ak term (where terms are refined)
3746    Dlookup = {} # provides the Ak term for each Dij term (where terms are refined)
3747    # N.B. These Dij vars are missing a histogram #
3748    newCellDict = {}
3749    for name in histNames:
3750        if name in data and 'newCellDict' in data[name]:
3751            newCellDict.update(data[name]['newCellDict'])
3752    cellAlist = []
3753    for item in newCellDict:
3754        cellAlist.append(newCellDict[item][0])
3755        if item in data.get('varyList',[]):
3756            ESDlookup[newCellDict[item][0]] = item
3757            Dlookup[item] = newCellDict[item][0]
3758    # add coordinate equivalents to lookup table
3759    for parm in atomLookup:
3760        Dlookup[atomLookup[parm]] = parm
3761        ESDlookup[parm] = atomLookup[parm]
3762
3763    # get unit cell & symmetry for all phases & initial stuff for later use
3764    RecpCellTerms = {}
3765    SGdata = {}
3766    uniqCellIndx = {}
3767    initialCell = {}
3768    RcellLbls = {}
3769    zeroDict = {}
3770    for phase in Phases:
3771        phasedict = Phases[phase]
3772        pId = phasedict['pId']
3773        pfx = str(pId)+'::' # prefix for A values from phase
3774        RcellLbls[pId] = [pfx+'A'+str(i) for i in range(6)]
3775        RecpCellTerms[pId] = G2lat.cell2A(phasedict['General']['Cell'][1:7])
3776        zeroDict[pId] = dict(zip(RcellLbls[pId],6*[0.,]))
3777        SGdata[pId] = phasedict['General']['SGData']
3778        laue = SGdata[pId]['SGLaue']
3779        if laue == '2/m':
3780            laue += SGdata[pId]['SGUniq']
3781        for symlist,celllist in cellGUIlist:
3782            if laue in symlist:
3783                uniqCellIndx[pId] = celllist
3784                break
3785        else: # should not happen
3786            uniqCellIndx[pId] = range(6)
3787        for i in uniqCellIndx[pId]:
3788            initialCell[str(pId)+'::A'+str(i)] =  RecpCellTerms[pId][i]
3789
3790    SetDataMenuBar(G2frame,G2frame.dataWindow.SequentialMenu)
3791    G2frame.dataFrame.SetLabel(G2frame.GetLabel().split('||')[0]+' || '+'Sequential refinement results')
3792    G2frame.GetStatusBar().SetStatusText('')
3793    G2frame.dataFrame.Bind(wx.EVT_MENU, OnRenameSelSeq, id=wxID_RENAMESEQSEL)
3794    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSelSeq, id=wxID_SAVESEQSEL)
3795    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSelSeqCSV, id=wxID_SAVESEQSELCSV)
3796    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSeqCSV, id=wxID_SAVESEQCSV)
3797    G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlotSelSeq, id=wxID_PLOTSEQSEL)
3798    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAveSelSeq, id=wxID_AVESEQSEL)
3799    #G2frame.dataFrame.Bind(wx.EVT_MENU, OnReOrgSelSeq, id=wxID_ORGSEQSEL)
3800    G2frame.dataFrame.Bind(wx.EVT_MENU, onSelectSeqVars, id=wxID_ORGSEQINC)
3801    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewPseudoVar, id=wxADDSEQVAR)
3802    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewDistPseudoVar, id=wxADDSEQDIST)
3803    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewAnglePseudoVar, id=wxADDSEQANGLE)
3804    G2frame.dataFrame.Bind(wx.EVT_MENU, DelPseudoVar, id=wxDELSEQVAR)
3805    G2frame.dataFrame.Bind(wx.EVT_MENU, EditPseudoVar, id=wxEDITSEQVAR)
3806    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewParFitEq, id=wxADDPARFIT)
3807    G2frame.dataFrame.Bind(wx.EVT_MENU, CopyParFitEq, id=wxCOPYPARFIT)
3808    G2frame.dataFrame.Bind(wx.EVT_MENU, DelParFitEq, id=wxDELPARFIT)
3809    G2frame.dataFrame.Bind(wx.EVT_MENU, EditParFitEq, id=wxEDITPARFIT)
3810    G2frame.dataFrame.Bind(wx.EVT_MENU, DoParEqFit, id=wxDOPARFIT)
3811
3812    for id in G2frame.dataFrame.SeqExportLookup:       
3813        G2frame.dataFrame.Bind(wx.EVT_MENU, DoSequentialExport, id=id)
3814
3815    EnablePseudoVarMenus()
3816    EnableParFitEqMenus()
3817
3818    # scan for locations where the variables change
3819    VaryListChanges = [] # histograms where there is a change
3820    combinedVaryList = []
3821    firstValueDict = {}
3822    vallookup = {}
3823    posdict = {}
3824    prevVaryList = []
3825    foundNames = []
3826    missing = 0
3827    for i,name in enumerate(histNames):
3828        if name not in data:
3829            if missing < 5:
3830                print(" Warning: "+name+" not found")
3831            elif missing == 5:
3832                print ' Warning: more are missing'
3833            missing += 1
3834            continue
3835        foundNames.append(name)
3836        maxPWL = 5
3837        for var,val,sig in zip(data[name]['varyList'],data[name]['variables'],data[name]['sig']):
3838            svar = striphist(var,'*') # wild-carded
3839            if 'PWL' in svar:
3840                if int(svar.split(':')[-1]) > maxPWL:
3841                    continue
3842            if svar not in combinedVaryList:
3843                # add variables to list as they appear
3844                combinedVaryList.append(svar)
3845                firstValueDict[svar] = (val,sig)
3846        if prevVaryList != data[name]['varyList']: # this refinement has a different refinement list from previous
3847            prevVaryList = data[name]['varyList']
3848            vallookup[name] = dict(zip(data[name]['varyList'],data[name]['variables']))
3849            posdict[name] = {}
3850            for var in data[name]['varyList']:
3851                svar = striphist(var,'*')
3852                if 'PWL' in svar:
3853                    if int(svar.split(':')[-1]) > maxPWL:
3854                        continue
3855                posdict[name][combinedVaryList.index(svar)] = svar
3856            VaryListChanges.append(name)
3857    if missing:
3858        print ' Warning: Total of %d data sets missing from sequential results'%(missing)
3859    #if len(VaryListChanges) > 1:
3860    #    G2frame.dataFrame.SequentialFile.Enable(wxID_ORGSEQSEL,True)
3861    #else:
3862    #    G2frame.dataFrame.SequentialFile.Enable(wxID_ORGSEQSEL,False)
3863    #-----------------------------------------------------------------------------------
3864    # build up the data table by columns -----------------------------------------------
3865    histNames = foundNames
3866    nRows = len(histNames)
3867    G2frame.colList = [nRows*[True]]
3868    G2frame.colSigs = [None]
3869    colLabels = ['Use']
3870    Types = [wg.GRID_VALUE_BOOL]
3871    # start with Rwp values
3872    if 'IMG ' not in histNames[0][:4]:
3873        G2frame.colList += [[data[name]['Rvals']['Rwp'] for name in histNames]]
3874        G2frame.colSigs += [None]
3875        colLabels += ['Rwp']
3876        Types += [wg.GRID_VALUE_FLOAT+':10,3',]
3877    # add % change in Chi^2 in last cycle
3878    if histNames[0][:4] not in ['SASD','IMG ','REFD'] and Controls.get('ShowCell'):
3879        G2frame.colList += [[100.*data[name]['Rvals'].get('DelChi2',-1) for name in histNames]]
3880        G2frame.colSigs += [None]
3881        colLabels += [u'\u0394\u03C7\u00B2 (%)']
3882        Types += [wg.GRID_VALUE_FLOAT+':10,5',]
3883    deltaChiCol = len(colLabels)-1
3884    # add changing sample parameters to table
3885    for key in sampleParms:
3886        G2frame.colList += [sampleParms[key]]
3887        G2frame.colSigs += [None]
3888        colLabels += [key]
3889        Types += [wg.GRID_VALUE_FLOAT,]
3890    sampleDict = {}
3891    for i,name in enumerate(histNames):
3892        sampleDict[name] = dict(zip(sampleParms.keys(),[sampleParms[key][i] for key in sampleParms.keys()])) 
3893    # add unique cell parameters TODO: review this where the cell symmetry changes (when possible)
3894    if Controls.get('ShowCell',False) and len(newCellDict):
3895        for pId in sorted(RecpCellTerms):
3896            pfx = str(pId)+'::' # prefix for A values from phase
3897            cells = []
3898            cellESDs = []
3899            colLabels += [pfx+cellUlbl[i] for i in uniqCellIndx[pId]]
3900            colLabels += [pfx+'Vol']
3901            Types += (len(uniqCellIndx[pId]))*[wg.GRID_VALUE_FLOAT+':10,5',]
3902            Types += [wg.GRID_VALUE_FLOAT+':10,3',]
3903            Albls = [pfx+'A'+str(i) for i in range(6)]
3904            for name in histNames:
3905                hId = Histograms[name]['hId']
3906                phfx = '%d:%d:'%(pId,hId)
3907                esdLookUp = {}
3908                dLookup = {}
3909                for item in data[name]['newCellDict']:
3910                    if phfx+item.split('::')[1] in data[name]['varyList']:
3911                        esdLookUp[newCellDict[item][0]] = item
3912                        dLookup[item] = newCellDict[item][0]
3913                covData = {'varyList': [dLookup.get(striphist(v),v) for v in data[name]['varyList']],
3914                    'covMatrix': data[name]['covMatrix']}
3915                A = RecpCellTerms[pId][:] # make copy of starting A values
3916                # update with refined values
3917                for i in range(6):
3918                    var = str(pId)+'::A'+str(i)
3919                    if var in cellAlist:
3920                        try:
3921                            val = data[name]['newCellDict'][esdLookUp[var]][1] # get refined value
3922                            A[i] = val # override with updated value
3923                        except KeyError:
3924                            A[i] = None
3925                # apply symmetry
3926                cellDict = dict(zip(Albls,A))
3927                if None in A:
3928                    c = 6*[None]
3929                    cE = 6*[None]
3930                    vol = None
3931                else:
3932                    A,zeros = G2stIO.cellFill(pfx,SGdata[pId],cellDict,zeroDict[pId])
3933                    # convert to direct cell & add only unique values to table
3934                    c = G2lat.A2cell(A)
3935                    vol = G2lat.calc_V(A)
3936                    cE = G2stIO.getCellEsd(pfx,SGdata[pId],A,covData)
3937                cells += [[c[i] for i in uniqCellIndx[pId]]+[vol]]
3938                cellESDs += [[cE[i] for i in uniqCellIndx[pId]]+[cE[-1]]]
3939            G2frame.colList += zip(*cells)
3940            G2frame.colSigs += zip(*cellESDs)
3941    # sort out the variables in their selected order
3942    varcols = 0
3943    for d in posdict.itervalues():
3944        varcols = max(varcols,max(d.keys())+1)
3945    # get labels for each column
3946    for i in range(varcols):
3947        lbl = ''
3948        for h in VaryListChanges:
3949            if posdict[h].get(i):
3950                if posdict[h].get(i) in lbl: continue
3951                if lbl != "": lbl += '/'
3952                lbl += posdict[h].get(i)
3953        colLabels.append(lbl)
3954    Types += varcols*[wg.GRID_VALUE_FLOAT,]
3955    vals = []
3956    esds = []
3957    varsellist = None        # will be a list of variable names in the order they are selected to appear
3958    # tabulate values for each hist, leaving None for blank columns
3959    for name in histNames:
3960        if name in posdict:
3961            varsellist = [posdict[name].get(i) for i in range(varcols)]
3962            # translate variable names to how they will be used in the headings
3963            vs = [striphist(v,'*') for v in data[name]['varyList']]
3964            # determine the index for each column (or None) in the data[]['variables'] and ['sig'] lists
3965            sellist = [vs.index(v) if v is not None else None for v in varsellist]
3966            #sellist = [i if striphist(v,'*') in varsellist else None for i,v in enumerate(data[name]['varyList'])]
3967        if not varsellist: raise Exception()
3968        vals.append([data[name]['variables'][s] if s is not None else None for s in sellist])
3969        esds.append([data[name]['sig'][s] if s is not None else None for s in sellist])
3970        #GSASIIpath.IPyBreak()
3971    G2frame.colList += zip(*vals)
3972    G2frame.colSigs += zip(*esds)
3973    # compute and add weight fractions to table if varied
3974    for phase in Phases:
3975        var = str(Phases[phase]['pId'])+':*:Scale'
3976        if var not in combinedVaryList: continue
3977        wtFrList = []
3978        sigwtFrList = []
3979        for i,name in enumerate(histNames):
3980            wtFrSum = 0.
3981            for phase1 in Phases:
3982                wtFrSum += Phases[phase1]['Histograms'][name]['Scale'][0]*Phases[phase1]['General']['Mass']
3983            var = str(Phases[phase]['pId'])+':'+str(i)+':Scale'
3984            wtFr = Phases[phase]['Histograms'][name]['Scale'][0]*Phases[phase]['General']['Mass']/wtFrSum
3985            wtFrList.append(wtFr)
3986            if var in data[name]['varyList']:
3987                sig = data[name]['sig'][data[name]['varyList'].index(var)]*wtFr/Phases[phase]['Histograms'][name]['Scale'][0]
3988            else:
3989                sig = 0.0
3990            sigwtFrList.append(sig)
3991        colLabels.append(str(Phases[phase]['pId'])+':*:WgtFrac')
3992        Types += [wg.GRID_VALUE_FLOAT+':10,5',]
3993        G2frame.colList += [wtFrList]
3994        G2frame.colSigs += [sigwtFrList]
3995               
3996    # tabulate constrained variables, removing histogram numbers if needed
3997    # from parameter label
3998    depValDict = {}
3999    depSigDict = {}
4000    for name in histNames:
4001        for var in data[name].get('depParmDict',{}):
4002            val,sig = data[name]['depParmDict'][var]
4003            svar = striphist(var,'*')
4004            if svar not in depValDict:
4005               depValDict[svar] = [val]
4006               depSigDict[svar] = [sig]
4007            else:
4008               depValDict[svar].append(val)
4009               depSigDict[svar].append(sig)
4010    # add the dependent constrained variables to the table
4011    for var in sorted(depValDict):
4012        if len(depValDict[var]) != len(histNames): continue
4013        colLabels.append(var)
4014        Types += [wg.GRID_VALUE_FLOAT+':10,5',]
4015        G2frame.colSigs += [depSigDict[var]]
4016        G2frame.colList += [depValDict[var]]
4017
4018    # add atom parameters to table
4019    colLabels += atomLookup.keys()
4020    for parm in sorted(atomLookup):
4021        G2frame.colList += [[data[name]['newAtomDict'][atomLookup[parm]][1] for name in histNames]]
4022        Types += [wg.GRID_VALUE_FLOAT+':10,5',]
4023        if atomLookup[parm] in data[histNames[0]]['varyList']:
4024            col = data[histNames[0]]['varyList'].index(atomLookup[parm])
4025            G2frame.colSigs += [[data[name]['sig'][col] for name in histNames]]
4026        else:
4027            G2frame.colSigs += [None]
4028    # evaluate Pseudovars, their ESDs and add them to grid
4029    for expr in data['SeqPseudoVars']:
4030        obj = data['SeqPseudoVars'][expr]
4031        calcobj = G2obj.ExpressionCalcObj(obj)
4032        valList = []
4033        esdList = []
4034        for seqnum,name in enumerate(histNames):
4035            sigs = data[name]['sig']
4036            G2mv.InitVars()
4037            parmDict = data[name].get('parmDict')
4038            constraintInfo = data[name].get('constraintInfo',[[],[],{},[],seqnum])
4039            groups,parmlist,constrDict,fixedList,ihst = constraintInfo
4040            varyList = data[name]['varyList']
4041            parmDict = data[name]['parmDict']
4042            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmDict,SeqHist=ihst)
4043            if 'Dist' in expr:
4044                derivs = G2mth.CalcDistDeriv(obj.distance_dict,obj.distance_atoms, parmDict)
4045                pId = obj.distance_dict['pId']
4046                aId,bId = obj.distance_atoms
4047                varyNames = ['%d::dA%s:%d'%(pId,ip,aId) for ip in ['x','y','z']]
4048                varyNames += ['%d::dA%s:%d'%(pId,ip,bId) for ip in ['x','y','z']]
4049                VCoV = G2mth.getVCov(varyNames,varyList,data[name]['covMatrix'])
4050                esdList.append(np.sqrt(np.inner(derivs,np.inner(VCoV,derivs.T)) ))
4051#                GSASIIpath.IPyBreak()
4052            elif 'Angle' in expr:
4053                derivs = G2mth.CalcAngleDeriv(obj.angle_dict,obj.angle_atoms, parmDict)
4054                pId = obj.angle_dict['pId']
4055                aId,bId = obj.angle_atoms
4056                varyNames = ['%d::dA%s:%d'%(pId,ip,aId) for ip in ['x','y','z']]
4057                varyNames += ['%d::dA%s:%d'%(pId,ip,bId[0]) for ip in ['x','y','z']]
4058                varyNames += ['%d::dA%s:%d'%(pId,ip,bId[1]) for ip in ['x','y','z']]
4059                VCoV = G2mth.getVCov(varyNames,varyList,data[name]['covMatrix'])
4060                esdList.append(np.sqrt(np.inner(derivs,np.inner(VCoV,derivs.T)) ))
4061            else:
4062                derivs = np.array(
4063                    [EvalPSvarDeriv(calcobj,parmDict.copy(),sampleDict[name],var,ESD)
4064                     for var,ESD in zip(varyList,sigs)])
4065                if None in list(derivs):
4066                    esdList.append(None)
4067                else:
4068                    esdList.append(np.sqrt(
4069                        np.inner(derivs,np.inner(data[name]['covMatrix'],derivs.T)) ))
4070            PSvarDict = parmDict.copy()
4071            PSvarDict.update(sampleDict[name])
4072            UpdateParmDict(PSvarDict)
4073            calcobj.UpdateDict(PSvarDict)
4074            valList.append(calcobj.EvalExpression())
4075#            if calcobj.su is not None: esdList[-1] = calcobj.su
4076        if not esdList:
4077            esdList = None
4078        G2frame.colList += [valList]
4079        G2frame.colSigs += [esdList]
4080        colLabels += [expr]
4081        Types += [wg.GRID_VALUE_FLOAT+':10,3']
4082    #---- table build done -------------------------------------------------------------
4083
4084    # Make dict needed for creating & editing pseudovars (PSvarDict).
4085   
4086    name = histNames[0]
4087    parmDict = data[name].get('parmDict',{})
4088    PSvarDict = parmDict.copy()
4089    PSvarDict.update(sampleParms)
4090    UpdateParmDict(PSvarDict)
4091    # Also dicts of variables
4092    # for Parametric fitting from the data table
4093    parmDict = dict(zip(colLabels,zip(*G2frame.colList)[0])) # scratch dict w/all values in table
4094    parmDict.update({var:val for var,val in newCellDict.values()}) #  add varied reciprocal cell terms
4095    del parmDict['Use']
4096    name = histNames[0]
4097
4098    #******************************************************************************
4099    # create a set of values for example evaluation of pseudovars and
4100    # this does not work for refinements that have differing numbers of variables.
4101    #raise Exception
4102    VarDict = {}
4103    for i,var in enumerate(colLabels):
4104        if var in ['Use','Rwp',u'\u0394\u03C7\u00B2 (%)']: continue
4105        if G2frame.colList[i][0] is None:
4106            val,sig = firstValueDict.get(var,[None,None])
4107        elif G2frame.colSigs[i]:
4108            val,sig = G2frame.colList[i][0],G2frame.colSigs[i][0]
4109        else:
4110            val,sig = G2frame.colList[i][0],None
4111#        if val is None: continue
4112#        elif sig is None or sig == 0.:
4113#            VarDict[var] = val
4114        if striphist(var) not in Dlookup:
4115            VarDict[var] = val
4116    # add recip cell coeff. values
4117    VarDict.update({var:val for var,val in newCellDict.values()})
4118
4119    # remove items to be hidden from table
4120    for l in reversed(range(len(colLabels))):
4121        if colLabels[l] in G2frame.SeqTblHideList:
4122            del colLabels[l]
4123            del G2frame.colList[l]
4124            del G2frame.colSigs[l]
4125
4126    G2frame.dataWindow.ClearData()
4127    G2frame.dataFrame.currentGrids = []
4128    G2frame.dataDisplay = G2G.GSGrid(parent=G2frame.dataWindow)
4129    G2frame.dataDisplay.SetSize(G2frame.dataWindow.GetSize())
4130    G2frame.SeqTable = G2G.Table([list(cl) for cl in zip(*G2frame.colList)],     # convert from columns to rows
4131        colLabels=colLabels,rowLabels=histNames,types=Types)
4132    G2frame.dataDisplay.SetTable(G2frame.SeqTable, True)
4133    #G2frame.dataDisplay.EnableEditing(False)
4134    # make all but first column read-only
4135    for c in range(1,len(colLabels)):
4136        for r in range(nRows):
4137            G2frame.dataDisplay.SetCellReadOnly(r,c)
4138    G2frame.dataDisplay.Bind(wg.EVT_GRID_LABEL_LEFT_DCLICK, PlotSelect)
4139    G2frame.dataDisplay.Bind(wg.EVT_GRID_LABEL_RIGHT_CLICK, SetLabelString)
4140    G2frame.dataDisplay.SetRowLabelSize(8*len(histNames[0]))       #pretty arbitrary 8
4141    G2frame.dataDisplay.SetMargins(0,0)
4142    G2frame.dataDisplay.AutoSizeColumns(False)
4143    # highlight unconverged shifts
4144    if histNames[0][:4] not in ['SASD','IMG ','REFD',]:
4145        for row,name in enumerate(histNames):
4146            deltaChi = G2frame.SeqTable.GetValue(row,deltaChiCol)
4147            if deltaChi > 10.:
4148                G2frame.dataDisplay.SetCellStyle(row,deltaChiCol,color=wx.Colour(255,0,0))
4149            elif deltaChi > 1.0:
4150                G2frame.dataDisplay.SetCellStyle(row,deltaChiCol,color=wx.Colour(255,255,0))
4151    G2frame.dataDisplay.InstallGridToolTip(GridSetToolTip,GridColLblToolTip)
4152    G2frame.dataDisplay.SendSizeEvent() # resize needed on mac
4153    G2frame.dataDisplay.Refresh() # shows colored text on mac
4154   
4155################################################################################
4156#####  Main PWDR panel
4157################################################################################           
4158       
4159def UpdatePWHKPlot(G2frame,kind,item):
4160    '''Called when the histogram main tree entry is called. Displays the
4161    histogram weight factor, refinement statistics for the histogram
4162    and the range of data for a simulation.
4163
4164    Also invokes a plot of the histogram.
4165    '''
4166    def onEditSimRange(event):
4167        'Edit simulation range'
4168        inp = [
4169            min(data[1][0]),
4170            max(data[1][0]),
4171            None
4172            ]
4173        inp[2] = (inp[1] - inp[0])/(len(data[1][0])-1.)
4174        names = ('start angle', 'end angle', 'step size')
4175        dlg = G2G.ScrolledMultiEditor(
4176            G2frame,[inp] * len(inp), range(len(inp)), names,
4177            header='Edit simulation range',
4178            minvals=(0.001,0.001,0.0001),
4179            maxvals=(180.,180.,.1),
4180            )
4181        dlg.CenterOnParent()
4182        val = dlg.ShowModal()
4183        dlg.Destroy()
4184        if val != wx.ID_OK: return
4185        if inp[0] > inp[1]:
4186            end,start,step = inp
4187        else:               
4188            start,end,step = inp
4189        step = abs(step)
4190        N = int((end-start)/step)+1
4191        newdata = np.linspace(start,end,N,True)
4192        if len(newdata) < 2: return # too small a range - reject
4193        data[1] = [newdata,np.zeros_like(newdata),np.ones_like(newdata),
4194            np.zeros_like(newdata),np.zeros_like(newdata),np.zeros_like(newdata)]
4195        Tmin = newdata[0]
4196        Tmax = newdata[-1]
4197        G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,item,'Limits'),
4198            [(Tmin,Tmax),[Tmin,Tmax]])
4199        UpdatePWHKPlot(G2frame,kind,item) # redisplay data screen
4200
4201    def OnPlot3DHKL(event):
4202        refList = data[1]['RefList']
4203        FoMax = np.max(refList.T[8+Super])
4204        Hmin = np.array([int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))])
4205        Hmax = np.array([int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))])
4206        Vpoint = np.array([int(np.mean(refList.T[0])),int(np.mean(refList.T[1])),int(np.mean(refList.T[2]))])
4207        controls = {'Type' : 'Fosq','Iscale' : False,'HKLmax' : Hmax,'HKLmin' : Hmin,'Zone':False,'viewKey':'L',
4208            'FoMax' : FoMax,'Scale' : 1.0,'Drawing':{'viewPoint':[Vpoint,[]],'default':Vpoint[:],
4209            'backColor':[0,0,0],'depthFog':False,'Zclip':10.0,'cameraPos':10.,'Zstep':0.05,'viewUp':[0,1,0],
4210            'Scale':1.0,'oldxy':[],'viewDir':[0,0,1]},'Super':Super,'SuperVec':SuperVec}
4211        G2plt.Plot3DSngl(G2frame,newPlot=True,Data=controls,hklRef=refList,Title=phaseName)
4212       
4213    def OnPlotAll3DHKL(event):
4214        choices = GetPatternTreeDataNames(G2frame,['HKLF',])
4215        dlg = G2G.G2MultiChoiceDialog(G2frame, 'Select reflection sets to plot',
4216            'Use data',choices)
4217        try:
4218            if dlg.ShowModal() == wx.ID_OK:
4219                refNames = [choices[i] for i in dlg.GetSelections()]
4220            else:
4221                return
4222        finally:
4223            dlg.Destroy()
4224        refList = np.zeros(0)
4225        for name in refNames:
4226            Id = GetPatternTreeItemId(G2frame,G2frame.root, name)
4227            reflData = G2frame.PatternTree.GetItemPyData(Id)[1]
4228            if len(refList):
4229                refList = np.concatenate((refList,reflData['RefList']))
4230            else:
4231                refList = reflData['RefList']
4232           
4233        FoMax = np.max(refList.T[8+Super])
4234        Hmin = np.array([int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))])
4235        Hmax = np.