source: trunk/GSASIIgrid.py @ 2870

Last change on this file since 2870 was 2870, checked in by toby, 5 years ago

Allow hiding of columns in Seq Table

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