source: trunk/GSASIIgrid.py @ 2777

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

implement import of getPDFx G(R) files NB: these have no matching PWDR entries
replace all scipy.fft with numpy.fft
add a plot SLD button for reflectometry
for PDF Peaks - Atom elements from periodic table - Bond No. still not working so doesn't really matter

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