source: trunk/GSASIIgrid.py @ 2769

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

remove commented imports from G2ctrls
force float for all common transform matrices
replace wx.TextCtrl? with G2G.ValidatedTxtCtrl? for floats in G2grid
(old code commented out for now - to be removed)
force r space group for H->R transformations (easily forgotten)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 233.9 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIgrid - data display routines
3########### SVN repository information ###################
4# $Date: 2017-04-01 13:41:24 +0000 (Sat, 01 Apr 2017) $
5# $Author: vondreele $
6# $Revision: 2769 $
7# $URL: trunk/GSASIIgrid.py $
8# $Id: GSASIIgrid.py 2769 2017-04-01 13:41:24Z 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: 2769 $")
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                sampleData = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,Id,'Sample Parameters'))
3069                for item in sampleParmDict:
3070                    sampleParmDict[item].append(sampleData.get(item,0))
3071        for item in sampleParmDict:
3072            frstValue = sampleParmDict[item][0]
3073            if np.any(np.array(sampleParmDict[item])-frstValue):
3074                if item.startswith('FreePrm'):
3075                    sampleParm[Controls[item]] = sampleParmDict[item]
3076                else:
3077                    sampleParm[item] = sampleParmDict[item]
3078        return sampleParm
3079
3080    def GetColumnInfo(col):
3081        '''returns column label, lists of values and errors (or None) for each column in the table
3082        for plotting. The column label is reformatted from Unicode to MatPlotLib encoding
3083        '''
3084        colName = G2frame.SeqTable.GetColLabelValue(col)
3085        plotName = variableLabels.get(colName,colName)
3086        plotName = plotSpCharFix(plotName)
3087        return plotName,G2frame.colList[col],G2frame.colSigs[col]
3088           
3089    def PlotSelect(event):
3090        'Plots a row (covariance) or column on double-click'
3091        cols = G2frame.dataDisplay.GetSelectedCols()
3092        rows = G2frame.dataDisplay.GetSelectedRows()
3093        if cols:
3094            G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
3095        elif rows:
3096            name = histNames[rows[0]]       #only does 1st one selected
3097            G2plt.PlotCovariance(G2frame,data[name])
3098        else:
3099            G2frame.ErrorDialog(
3100                'Select row or columns',
3101                'Nothing selected in table. Click on column or row label(s) to plot. N.B. Grid selection can be a bit funky.'
3102                )
3103           
3104    def OnPlotSelSeq(event):
3105        'plot the selected columns or row from menu command'
3106        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
3107        rows = G2frame.dataDisplay.GetSelectedRows()
3108        if cols:
3109            G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
3110        elif rows:
3111            name = histNames[rows[0]]       #only does 1st one selected
3112            G2plt.PlotCovariance(G2frame,data[name])
3113        else:
3114            G2frame.ErrorDialog(
3115                'Select columns',
3116                'No columns or rows selected in table. Click on row or column labels to select fields for plotting.'
3117                )
3118               
3119    def OnAveSelSeq(event):
3120        'average the selected columns from menu command'
3121        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
3122        useCol = -np.array(G2frame.SeqTable.GetColValues(0),dtype=bool)
3123        if cols:
3124            for col in cols:
3125                items = GetColumnInfo(col)[1]
3126                noneMask = np.array([item is None for item in items])
3127                info = ma.array(items,mask=useCol+noneMask)
3128                ave = ma.mean(ma.compressed(info))
3129                sig = ma.std(ma.compressed(info))
3130                print ' Average for '+G2frame.SeqTable.GetColLabelValue(col)+': '+'%.6g'%(ave)+' +/- '+'%.6g'%(sig)
3131        else:
3132            G2frame.ErrorDialog(
3133                'Select columns',
3134                'No columns selected in table. Click on column labels to select fields for averaging.'
3135                )
3136               
3137    def OnRenameSelSeq(event):
3138        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
3139        colNames = [G2frame.SeqTable.GetColLabelValue(c) for c in cols]
3140        newNames = colNames[:]
3141        for i,name in enumerate(colNames):
3142            if name in variableLabels:
3143                newNames[i] = variableLabels[name]
3144        if not cols:
3145            G2frame.ErrorDialog('Select columns',
3146                'No columns selected in table. Click on column labels to select fields for rename.')
3147            return
3148        dlg = G2G.MultiStringDialog(G2frame.dataDisplay,'Set column names',colNames,newNames)
3149        if dlg.Show():
3150            newNames = dlg.GetValues()           
3151            variableLabels.update(dict(zip(colNames,newNames)))
3152        data['variableLabels'] = variableLabels
3153        dlg.Destroy()
3154        UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3155        G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
3156           
3157    def OnReOrgSelSeq(event):
3158        'Reorder the columns'
3159        G2G.GetItemOrder(G2frame,VaryListChanges,vallookup,posdict)   
3160        UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3161
3162    def OnSaveSelSeqCSV(event):
3163        'export the selected columns to a .csv file from menu command'
3164        OnSaveSelSeq(event,csv=True)
3165       
3166    def OnSaveSeqCSV(event):
3167        'export all columns to a .csv file from menu command'
3168        OnSaveSelSeq(event,csv=True,allcols=True)
3169       
3170    def OnSaveSelSeq(event,csv=False,allcols=False):
3171        'export the selected columns to a .txt or .csv file from menu command'
3172        def WriteCSV():
3173            def WriteList(headerItems):
3174                line = ''
3175                for lbl in headerItems:
3176                    if line: line += ','
3177                    line += '"'+lbl+'"'
3178                return line
3179            head = ['name']
3180            for col in cols:
3181                item = G2frame.SeqTable.GetColLabelValue(col)
3182                # get rid of labels that have Unicode characters
3183                if not all([ord(c) < 128 and ord(c) != 0 for c in item]): item = '?'
3184                if col in havesig:
3185                    head += [item,'esd-'+item]
3186                else:
3187                    head += [item]
3188            SeqFile.write(WriteList(head)+'\n')
3189            for row,name in enumerate(saveNames):
3190                line = '"'+saveNames[row]+'"'
3191                for col in cols:
3192                    if col in havesig:
3193                        line += ','+str(saveData[col][row])+','+str(saveSigs[col][row])
3194                    else:
3195                        line += ','+str(saveData[col][row])
3196                SeqFile.write(line+'\n')
3197        def WriteSeq():
3198            lenName = len(saveNames[0])
3199            line = %s  '%('name'.center(lenName))
3200            for col in cols:
3201                item = G2frame.SeqTable.GetColLabelValue(col)
3202                if col in havesig:
3203                    line += ' %12s %12s '%(item.center(12),'esd'.center(12))
3204                else:
3205                    line += ' %12s '%(item.center(12))
3206            SeqFile.write(line+'\n')
3207            for row,name in enumerate(saveNames):
3208                line = " '%s' "%(saveNames[row])
3209                for col in cols:
3210                    if col in havesig:
3211                        line += ' %12.6f %12.6f '%(saveData[col][row],saveSigs[col][row])
3212                    else:
3213                        line += ' %12.6f '%saveData[col][row]
3214                SeqFile.write(line+'\n')
3215
3216        # start of OnSaveSelSeq code
3217        if allcols:
3218            cols = range(G2frame.SeqTable.GetNumberCols())
3219        else:
3220            cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
3221        nrows = G2frame.SeqTable.GetNumberRows()
3222        if not cols:
3223            G2frame.ErrorDialog('Select columns',
3224                             'No columns selected in table. Click on column labels to select fields for output.')
3225            return
3226        saveNames = [G2frame.SeqTable.GetRowLabelValue(r) for r in range(nrows)]
3227        saveData = {}
3228        saveSigs = {}
3229        havesig = []
3230        for col in cols:
3231            name,vals,sigs = GetColumnInfo(col)
3232            saveData[col] = vals
3233            if sigs:
3234                havesig.append(col)
3235                saveSigs[col] = sigs
3236        if csv:
3237            wild = 'CSV output file (*.csv)|*.csv'
3238        else:
3239            wild = 'Text output file (*.txt)|*.txt'
3240        pth = G2G.GetExportPath(G2frame)
3241        dlg = wx.FileDialog(
3242            G2frame,
3243            'Choose text output file for your selection', pth, '', 
3244            wild,wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3245        try:
3246            if dlg.ShowModal() == wx.ID_OK:
3247                SeqTextFile = dlg.GetPath()
3248                SeqTextFile = G2IO.FileDlgFixExt(dlg,SeqTextFile) 
3249                SeqFile = open(SeqTextFile,'w')
3250                if csv:
3251                    WriteCSV()
3252                else:
3253                    WriteSeq()
3254                SeqFile.close()
3255        finally:
3256            dlg.Destroy()
3257               
3258    def striphist(var,insChar=''):
3259        'strip a histogram number from a var name'
3260        sv = var.split(':')
3261        if len(sv) <= 1: return var
3262        if sv[1]:
3263            sv[1] = insChar
3264        return ':'.join(sv)
3265       
3266    def plotSpCharFix(lbl):
3267        'Change selected unicode characters to their matplotlib equivalent'
3268        for u,p in [
3269            (u'\u03B1',r'$\alpha$'),
3270            (u'\u03B2',r'$\beta$'),
3271            (u'\u03B3',r'$\gamma$'),
3272            (u'\u0394\u03C7',r'$\Delta\chi$'),
3273            ]:
3274            lbl = lbl.replace(u,p)
3275        return lbl
3276   
3277    def SelectXaxis():
3278        'returns a selected column number (or None) as the X-axis selection'
3279        ncols = G2frame.SeqTable.GetNumberCols()
3280        colNames = [G2frame.SeqTable.GetColLabelValue(r) for r in range(ncols)]
3281        dlg = G2G.G2SingleChoiceDialog(
3282            G2frame.dataDisplay,
3283            'Select x-axis parameter for plot or Cancel for sequence number',
3284            'Select X-axis',
3285            colNames)
3286        try:
3287            if dlg.ShowModal() == wx.ID_OK:
3288                col = dlg.GetSelection()
3289            else:
3290                col = None
3291        finally:
3292            dlg.Destroy()
3293        return col
3294   
3295    def EnablePseudoVarMenus():
3296        'Enables or disables the PseudoVar menu items that require existing defs'
3297        if data['SeqPseudoVars']:
3298            val = True
3299        else:
3300            val = False
3301        G2frame.dataFrame.SequentialPvars.Enable(wxDELSEQVAR,val)
3302        G2frame.dataFrame.SequentialPvars.Enable(wxEDITSEQVAR,val)
3303
3304    def DelPseudoVar(event):
3305        'Ask the user to select a pseudo var expression to delete'
3306        choices = data['SeqPseudoVars'].keys()
3307        selected = G2G.ItemSelector(
3308            choices,G2frame.dataFrame,
3309            multiple=True,
3310            title='Select expressions to remove',
3311            header='Delete expression')
3312        if selected is None: return
3313        for item in selected:
3314            del data['SeqPseudoVars'][choices[item]]
3315        if selected:
3316            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3317
3318    def EditPseudoVar(event):
3319        'Edit an existing pseudo var expression'
3320        choices = data['SeqPseudoVars'].keys()
3321        if len(choices) == 1:
3322            selected = 0
3323        else:
3324            selected = G2G.ItemSelector(
3325                choices,G2frame.dataFrame,
3326                multiple=False,
3327                title='Select an expression to edit',
3328                header='Edit expression')
3329        if selected is not None:
3330            dlg = G2exG.ExpressionDialog(
3331                G2frame.dataDisplay,PSvarDict,
3332                data['SeqPseudoVars'][choices[selected]],
3333                header="Edit the PseudoVar expression",
3334                VarLabel="PseudoVar #"+str(selected+1),
3335                fit=False)
3336            newobj = dlg.Show(True)
3337            if newobj:
3338                calcobj = G2obj.ExpressionCalcObj(newobj)
3339                del data['SeqPseudoVars'][choices[selected]]
3340                data['SeqPseudoVars'][calcobj.eObj.expression] = newobj
3341                UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3342       
3343    def AddNewPseudoVar(event):
3344        'Create a new pseudo var expression'
3345        dlg = G2exG.ExpressionDialog(G2frame.dataDisplay,PSvarDict,
3346            header='Enter an expression for a PseudoVar here',
3347            VarLabel = "New PseudoVar",fit=False)
3348        obj = dlg.Show(True)
3349        dlg.Destroy()
3350        if obj:
3351            calcobj = G2obj.ExpressionCalcObj(obj)
3352            data['SeqPseudoVars'][calcobj.eObj.expression] = obj
3353            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3354           
3355    def AddNewDistPseudoVar(event):
3356        obj = None
3357        dlg = G2exG.BondDialog(
3358            G2frame.dataDisplay,Phases,PSvarDict,
3359            header='Select a Bond here',
3360            VarLabel = "New Bond")
3361        if dlg.ShowModal() == wx.ID_OK:
3362            pName,Oatom,Tatom = dlg.GetSelection()
3363            if Tatom:
3364                Phase = Phases[pName]
3365                General = Phase['General']
3366                cx,ct = General['AtomPtrs'][:2]
3367                pId = Phase['pId']
3368                SGData = General['SGData']
3369                sB = Tatom.find('(')+1
3370                symNo = 0
3371                if sB:
3372                    sF = Tatom.find(')')
3373                    symNo = int(Tatom[sB:sF])
3374                cellNo = [0,0,0]
3375                cB = Tatom.find('[')
3376                if cB>0:
3377                    cF = Tatom.find(']')+1
3378                    cellNo = eval(Tatom[cB:cF])
3379                Atoms = Phase['Atoms']
3380                aNames = [atom[ct-1] for atom in Atoms]
3381                oId = aNames.index(Oatom)
3382                tId = aNames.index(Tatom.split(' +')[0])
3383                # create an expression object
3384                obj = G2obj.ExpressionObj()
3385                obj.expression = 'Dist(%s,\n%s)'%(Oatom,Tatom.split(' d=')[0].replace(' ',''))
3386                obj.distance_dict = {'pId':pId,'SGData':SGData,'symNo':symNo,'cellNo':cellNo}
3387                obj.distance_atoms = [oId,tId]
3388        else: 
3389            dlg.Destroy()
3390            return
3391        dlg.Destroy()
3392        if obj:
3393            data['SeqPseudoVars'][obj.expression] = obj
3394            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3395
3396    def AddNewAnglePseudoVar(event):
3397        obj = None
3398        dlg = G2exG.AngleDialog(
3399            G2frame.dataDisplay,Phases,PSvarDict,
3400            header='Enter an Angle here',
3401            VarLabel = "New Angle")
3402        if dlg.ShowModal() == wx.ID_OK:
3403            pName,Oatom,Tatoms = dlg.GetSelection()
3404            if Tatoms:
3405                Phase = Phases[pName]
3406                General = Phase['General']
3407                cx,ct = General['AtomPtrs'][:2]
3408                pId = Phase['pId']
3409                SGData = General['SGData']
3410                Atoms = Phase['Atoms']
3411                aNames = [atom[ct-1] for atom in Atoms]
3412                tIds = []
3413                symNos = []
3414                cellNos = []
3415                oId = aNames.index(Oatom)
3416                Tatoms = Tatoms.split(';')
3417                for Tatom in Tatoms:
3418                    sB = Tatom.find('(')+1
3419                    symNo = 0
3420                    if sB:
3421                        sF = Tatom.find(')')
3422                        symNo = int(Tatom[sB:sF])
3423                    symNos.append(symNo)
3424                    cellNo = [0,0,0]
3425                    cB = Tatom.find('[')
3426                    if cB>0:
3427                        cF = Tatom.find(']')+1
3428                        cellNo = eval(Tatom[cB:cF])
3429                    cellNos.append(cellNo)
3430                    tIds.append(aNames.index(Tatom.split('+')[0]))
3431                # create an expression object
3432                obj = G2obj.ExpressionObj()
3433                obj.expression = 'Angle(%s,%s,\n%s)'%(Tatoms[0],Oatom,Tatoms[1])
3434                obj.angle_dict = {'pId':pId,'SGData':SGData,'symNo':symNos,'cellNo':cellNos}
3435                obj.angle_atoms = [oId,tIds]
3436        else: 
3437            dlg.Destroy()
3438            return
3439        dlg.Destroy()
3440        if obj:
3441            data['SeqPseudoVars'][obj.expression] = obj
3442            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3443           
3444    def UpdateParmDict(parmDict):
3445        '''generate the atom positions and the direct & reciprocal cell values,
3446        because they might be needed to evaluate the pseudovar
3447        '''
3448        Ddict = dict(zip(['D11','D22','D33','D12','D13','D23'],
3449                         ['A'+str(i) for i in range(6)])
3450                     )
3451        delList = []
3452        phaselist = []
3453        for item in parmDict: 
3454            if ':' not in item: continue
3455            key = item.split(':')
3456            if len(key) < 3: continue
3457            # remove the dA[xyz] terms, they would only bring confusion
3458            if key[0] and key[0] not in phaselist: phaselist.append(key[0])
3459            if key[2].startswith('dA'):
3460                delList.append(item)
3461            # compute and update the corrected reciprocal cell terms using the Dij values
3462            elif key[2] in Ddict:
3463                akey = key[0]+'::'+Ddict[key[2]]
3464                parmDict[akey] -= parmDict[item]
3465                delList.append(item)
3466        for item in delList:
3467            del parmDict[item]               
3468        for i in phaselist:
3469            pId = int(i)
3470            # apply cell symmetry
3471            A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata[pId],parmDict,zeroDict[pId])
3472            # convert to direct cell & add the unique terms to the dictionary
3473            for i,val in enumerate(G2lat.A2cell(A)):
3474                if i in uniqCellIndx[pId]:
3475                    lbl = str(pId)+'::'+cellUlbl[i]
3476                    parmDict[lbl] = val
3477            lbl = str(pId)+'::'+'Vol'
3478            parmDict[lbl] = G2lat.calc_V(A)
3479        return parmDict
3480
3481    def EvalPSvarDeriv(calcobj,parmDict,sampleDict,var,ESD):
3482        '''Evaluate an expression derivative with respect to a
3483        GSAS-II variable name.
3484
3485        Note this likely could be faster if the loop over calcobjs were done
3486        inside after the Dict was created.
3487        '''
3488        if not ESD:
3489            return 0.
3490        step = ESD/10
3491        Ddict = dict(zip(['D11','D22','D33','D12','D13','D23'],
3492                         ['A'+str(i) for i in range(6)])
3493                     )
3494        results = []
3495        phaselist = []
3496        VparmDict = sampleDict.copy()
3497        for incr in step,-step:
3498            VparmDict.update(parmDict.copy())           
3499            # as saved, the parmDict has updated 'A[xyz]' values, but 'dA[xyz]'
3500            # values are not zeroed: fix that!
3501            VparmDict.update({item:0.0 for item in parmDict if 'dA' in item})
3502            VparmDict[var] += incr
3503            G2mv.Dict2Map(VparmDict,[]) # apply constraints
3504            # generate the atom positions and the direct & reciprocal cell values now, because they might
3505            # needed to evaluate the pseudovar
3506            for item in VparmDict:
3507                if item in sampleDict:
3508                    continue 
3509                if ':' not in item: continue
3510                key = item.split(':')
3511                if len(key) < 3: continue
3512                # apply any new shifts to atom positions
3513                if key[2].startswith('dA'):
3514                    VparmDict[''.join(item.split('d'))] += VparmDict[item]
3515                    VparmDict[item] = 0.0
3516                # compute and update the corrected reciprocal cell terms using the Dij values
3517                if key[2] in Ddict:
3518                    if key[0] not in phaselist: phaselist.append(key[0])
3519                    akey = key[0]+'::'+Ddict[key[2]]
3520                    VparmDict[akey] -= VparmDict[item]
3521            for i in phaselist:
3522                pId = int(i)
3523                # apply cell symmetry
3524                A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata[pId],VparmDict,zeroDict[pId])
3525                # convert to direct cell & add the unique terms to the dictionary
3526                for i,val in enumerate(G2lat.A2cell(A)):
3527                    if i in uniqCellIndx[pId]:
3528                        lbl = str(pId)+'::'+cellUlbl[i]
3529                        VparmDict[lbl] = val
3530                lbl = str(pId)+'::'+'Vol'
3531                VparmDict[lbl] = G2lat.calc_V(A)
3532            # dict should be fully updated, use it & calculate
3533            calcobj.SetupCalc(VparmDict)
3534            results.append(calcobj.EvalExpression())
3535        if None in results:
3536            return None
3537        return (results[0] - results[1]) / (2.*step)
3538       
3539    def EnableParFitEqMenus():
3540        'Enables or disables the Parametric Fit menu items that require existing defs'
3541        if data['SeqParFitEqList']:
3542            val = True
3543        else:
3544            val = False
3545        G2frame.dataFrame.SequentialPfit.Enable(wxDELPARFIT,val)
3546        G2frame.dataFrame.SequentialPfit.Enable(wxEDITPARFIT,val)
3547        G2frame.dataFrame.SequentialPfit.Enable(wxDOPARFIT,val)
3548
3549    def ParEqEval(Values,calcObjList,varyList):
3550        '''Evaluate the parametric expression(s)
3551        :param list Values: a list of values for each variable parameter
3552        :param list calcObjList: a list of :class:`GSASIIobj.ExpressionCalcObj`
3553          expression objects to evaluate
3554        :param list varyList: a list of variable names for each value in Values
3555        '''
3556        result = []
3557        for calcobj in calcObjList:
3558            calcobj.UpdateVars(varyList,Values)
3559            if calcobj.depSig:
3560                result.append((calcobj.depVal-calcobj.EvalExpression())/calcobj.depSig)
3561            else:
3562                result.append(calcobj.depVal-calcobj.EvalExpression())
3563        return result
3564
3565    def DoParEqFit(event,eqObj=None):
3566        'Parametric fit minimizer'
3567        varyValueDict = {} # dict of variables and their initial values
3568        calcObjList = [] # expression objects, ready to go for each data point
3569        if eqObj is not None:
3570            eqObjList = [eqObj,]
3571        else:
3572            eqObjList = data['SeqParFitEqList']
3573        UseFlags = G2frame.SeqTable.GetColValues(0)         
3574        for obj in eqObjList:
3575            # assemble refined vars for this equation
3576            varyValueDict.update({var:val for var,val in obj.GetVariedVarVal()})
3577            # lookup dependent var position
3578            depVar = obj.GetDepVar()
3579            if depVar in colLabels:
3580                indx = colLabels.index(depVar)
3581            else:
3582                raise Exception('Dependent variable '+depVar+' not found')
3583            # assemble a list of the independent variables
3584            indepVars = obj.GetIndependentVars()
3585            # loop over each datapoint
3586            for j,row in enumerate(zip(*G2frame.colList)):
3587                if not UseFlags[j]: continue
3588                # assemble equations to fit
3589                calcobj = G2obj.ExpressionCalcObj(obj)
3590                # prepare a dict of needed independent vars for this expression
3591                indepVarDict = {var:row[i] for i,var in enumerate(colLabels) if var in indepVars}
3592                calcobj.SetupCalc(indepVarDict)               
3593                # values and sigs for current value of dependent var
3594                if row[indx] is None: continue
3595                calcobj.depVal = row[indx]
3596                calcobj.depSig = G2frame.colSigs[indx][j]
3597                calcObjList.append(calcobj)
3598        # varied parameters
3599        varyList = varyValueDict.keys()
3600        values = varyValues = [varyValueDict[key] for key in varyList]
3601        if not varyList:
3602            print 'no variables to refine!'
3603            return
3604        try:
3605            result = so.leastsq(ParEqEval,varyValues,full_output=True,   #ftol=Ftol,
3606                args=(calcObjList,varyList))
3607            values = result[0]
3608            covar = result[1]
3609            if covar is None:
3610                raise Exception
3611            chisq = np.sum(result[2]['fvec']**2)
3612            GOF = np.sqrt(chisq/(len(calcObjList)-len(varyList)))
3613            esdDict = {}
3614            for i,avar in enumerate(varyList):
3615                esdDict[avar] = np.sqrt(covar[i,i])
3616        except:
3617            print('====> Fit failed')
3618            return
3619        print('==== Fit Results ====')
3620        print '  chisq =  %.2f, GOF = %.2f'%(chisq,GOF)
3621        for obj in eqObjList:
3622            obj.UpdateVariedVars(varyList,values)
3623            ind = '      '
3624            print('  '+obj.GetDepVar()+' = '+obj.expression)
3625            for var in obj.assgnVars:
3626                print(ind+var+' = '+obj.assgnVars[var])
3627            for var in obj.freeVars:
3628                avar = "::"+obj.freeVars[var][0]
3629                val = obj.freeVars[var][1]
3630                if obj.freeVars[var][2]:
3631                    print(ind+var+' = '+avar + " = " + G2mth.ValEsd(val,esdDict[avar]))
3632                else:
3633                    print(ind+var+' = '+avar + " =" + G2mth.ValEsd(val,0))
3634        # create a plot for each parametric variable
3635        for fitnum,obj in enumerate(eqObjList):
3636            calcobj = G2obj.ExpressionCalcObj(obj)
3637            # lookup dependent var position
3638            indx = colLabels.index(obj.GetDepVar())
3639            # assemble a list of the independent variables
3640            indepVars = obj.GetIndependentVars()           
3641            # loop over each datapoint
3642            fitvals = []
3643            for j,row in enumerate(zip(*G2frame.colList)):
3644                calcobj.SetupCalc({var:row[i] for i,var in enumerate(colLabels) if var in indepVars})
3645                fitvals.append(calcobj.EvalExpression())
3646            G2plt.PlotSelectedSequence(G2frame,[indx],GetColumnInfo,SelectXaxis,fitnum,fitvals)
3647
3648    def SingleParEqFit(eqObj):
3649        DoParEqFit(None,eqObj)
3650
3651    def DelParFitEq(event):
3652        'Ask the user to select function to delete'
3653        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in data['SeqParFitEqList']]
3654        selected = G2G.ItemSelector(
3655            txtlst,G2frame.dataFrame,
3656            multiple=True,
3657            title='Select a parametric equation(s) to remove',
3658            header='Delete equation')
3659        if selected is None: return
3660        data['SeqParFitEqList'] = [obj for i,obj in enumerate(data['SeqParFitEqList']) if i not in selected]
3661        EnableParFitEqMenus()
3662        if data['SeqParFitEqList']: DoParEqFit(event)
3663       
3664    def EditParFitEq(event):
3665        'Edit an existing parametric equation'
3666        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in data['SeqParFitEqList']]
3667        if len(txtlst) == 1:
3668            selected = 0
3669        else:
3670            selected = G2G.ItemSelector(
3671                txtlst,G2frame.dataFrame,
3672                multiple=False,
3673                title='Select a parametric equation to edit',
3674                header='Edit equation')
3675        if selected is not None:
3676            dlg = G2exG.ExpressionDialog(G2frame.dataDisplay,VarDict,
3677                data['SeqParFitEqList'][selected],depVarDict=VarDict,
3678                header="Edit the formula for this minimization function",
3679                ExtraButton=['Fit',SingleParEqFit])
3680            newobj = dlg.Show(True)
3681            if newobj:
3682                data['SeqParFitEqList'][selected] = newobj
3683                EnableParFitEqMenus()
3684            if data['SeqParFitEqList']: DoParEqFit(event)
3685
3686    def AddNewParFitEq(event):
3687        'Create a new parametric equation to be fit to sequential results'
3688
3689        # compile the variable names used in previous freevars to avoid accidental name collisions
3690        usedvarlist = []
3691        for obj in data['SeqParFitEqList']:
3692            for var in obj.freeVars:
3693                if obj.freeVars[var][0] not in usedvarlist: usedvarlist.append(obj.freeVars[var][0])
3694
3695        dlg = G2exG.ExpressionDialog(G2frame.dataDisplay,VarDict,depVarDict=VarDict,
3696            header='Define an equation to minimize in the parametric fit',
3697            ExtraButton=['Fit',SingleParEqFit],usedVars=usedvarlist)
3698        obj = dlg.Show(True)
3699        dlg.Destroy()
3700        if obj:
3701            data['SeqParFitEqList'].append(obj)
3702            EnableParFitEqMenus()
3703            if data['SeqParFitEqList']: DoParEqFit(event)
3704               
3705    def CopyParFitEq(event):
3706        'Copy an existing parametric equation to be fit to sequential results'
3707        # compile the variable names used in previous freevars to avoid accidental name collisions
3708        usedvarlist = []
3709        for obj in data['SeqParFitEqList']:
3710            for var in obj.freeVars:
3711                if obj.freeVars[var][0] not in usedvarlist: usedvarlist.append(obj.freeVars[var][0])
3712        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in data['SeqParFitEqList']]
3713        if len(txtlst) == 1:
3714            selected = 0
3715        else:
3716            selected = G2G.ItemSelector(
3717                txtlst,G2frame.dataFrame,
3718                multiple=False,
3719                title='Select a parametric equation to copy',
3720                header='Copy equation')
3721        if selected is not None:
3722            newEqn = copy.deepcopy(data['SeqParFitEqList'][selected])
3723            for var in newEqn.freeVars:
3724                newEqn.freeVars[var][0] = G2obj.MakeUniqueLabel(newEqn.freeVars[var][0],usedvarlist)
3725            dlg = G2exG.ExpressionDialog(
3726                G2frame.dataDisplay,VarDict,newEqn,depVarDict=VarDict,
3727                header="Edit the formula for this minimization function",
3728                ExtraButton=['Fit',SingleParEqFit])
3729            newobj = dlg.Show(True)
3730            if newobj:
3731                data['SeqParFitEqList'].append(newobj)
3732                EnableParFitEqMenus()
3733            if data['SeqParFitEqList']: DoParEqFit(event)
3734                                           
3735    def GridSetToolTip(row,col):
3736        '''Routine to show standard uncertainties for each element in table
3737        as a tooltip
3738        '''
3739        if G2frame.colSigs[col]:
3740            return u'\u03c3 = '+str(G2frame.colSigs[col][row])
3741        return ''
3742       
3743    def GridColLblToolTip(col):
3744        '''Define a tooltip for a column. This will be the user-entered value
3745        (from data['variableLabels']) or the default name
3746        '''
3747        if col < 0 or col > len(colLabels):
3748            print 'Illegal column #',col
3749            return
3750        var = colLabels[col]
3751        return variableLabels.get(var,G2obj.fmtVarDescr(var))
3752       
3753    def SetLabelString(event):
3754        '''Define or edit the label for a column in the table, to be used
3755        as a tooltip and for plotting
3756        '''
3757        col = event.GetCol()
3758        if col < 0 or col > len(colLabels):
3759            return
3760        var = colLabels[col]
3761        lbl = variableLabels.get(var,G2obj.fmtVarDescr(var))
3762        dlg = G2G.SingleStringDialog(G2frame.dataFrame,'Set variable label',
3763                                 'Set a new name for variable '+var,lbl,size=(400,-1))
3764        if dlg.Show():
3765            variableLabels[var] = dlg.GetValue()
3766        dlg.Destroy()
3767
3768    def DoSequentialExport(event):
3769        '''Event handler for all Sequential Export menu items
3770        '''
3771        vals = G2frame.dataFrame.SeqExportLookup.get(event.GetId())
3772        if vals is None:
3773            print('Error: Id not found. This should not happen!')
3774        G2IO.ExportSequential(G2frame,data,*vals)
3775   
3776    #def GridRowLblToolTip(row): return 'Row ='+str(row)
3777   
3778    # lookup table for unique cell parameters by symmetry
3779    cellGUIlist = [
3780        [['m3','m3m'],(0,)],
3781        [['3R','3mR'],(0,3)],
3782        [['3','3m1','31m','6/m','6/mmm','4/m','4/mmm'],(0,2)],
3783        [['mmm'],(0,1,2)],
3784        [['2/m'+'a'],(0,1,2,3)],
3785        [['2/m'+'b'],(0,1,2,4)],
3786        [['2/m'+'c'],(0,1,2,5)],
3787        [['-1'],(0,1,2,3,4,5)],
3788        ]
3789    # cell labels
3790    cellUlbl = ('a','b','c',u'\u03B1',u'\u03B2',u'\u03B3') # unicode a,b,c,alpha,beta,gamma
3791
3792    #======================================================================
3793    # start processing sequential results here (UpdateSeqResults)
3794    #======================================================================
3795    if not data:
3796        print 'No sequential refinement results'
3797        return
3798    variableLabels = data.get('variableLabels',{})
3799    data['variableLabels'] = variableLabels
3800    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
3801    Controls = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.root,'Controls'))
3802    # create a place to store Pseudo Vars & Parametric Fit functions, if not present
3803    if 'SeqPseudoVars' not in data: data['SeqPseudoVars'] = {}
3804    if 'SeqParFitEqList' not in data: data['SeqParFitEqList'] = []
3805    histNames = data['histNames']
3806    if G2frame.dataDisplay:
3807        G2frame.dataDisplay.Destroy()
3808    if not G2frame.dataFrame.GetStatusBar():
3809        Status = G2frame.dataFrame.CreateStatusBar()
3810        Status.SetStatusText("Select column to export; Double click on column to plot data; on row for Covariance")
3811    sampleParms = GetSampleParms()
3812
3813    # make dict of varied atom coords keyed by absolute position
3814    newAtomDict = data[histNames[0]].get('newAtomDict',{}) # dict with atom positions; relative & absolute
3815    # Possible error: the next might need to be data[histNames[0]]['varyList']
3816    # error will arise if there constraints on coordinates?
3817    atomLookup = {newAtomDict[item][0]:item for item in newAtomDict if item in data['varyList']}
3818   
3819    # make dict of varied cell parameters equivalents
3820    ESDlookup = {} # provides the Dij term for each Ak term (where terms are refined)
3821    Dlookup = {} # provides the Ak term for each Dij term (where terms are refined)
3822    # N.B. These Dij vars are missing a histogram #
3823    newCellDict = {}
3824    for name in histNames:
3825        if name in data and 'newCellDict' in data[name]:
3826            newCellDict.update(data[name]['newCellDict'])
3827#    newCellDict = data[histNames[0]].get('newCellDict',{})
3828    cellAlist = []
3829    for item in newCellDict:
3830        cellAlist.append(newCellDict[item][0])
3831        if item in data.get('varyList',[]):
3832            ESDlookup[newCellDict[item][0]] = item
3833            Dlookup[item] = newCellDict[item][0]
3834    # add coordinate equivalents to lookup table
3835    for parm in atomLookup:
3836        Dlookup[atomLookup[parm]] = parm
3837        ESDlookup[parm] = atomLookup[parm]
3838
3839    # get unit cell & symmetry for all phases & initial stuff for later use
3840    RecpCellTerms = {}
3841    SGdata = {}
3842    uniqCellIndx = {}
3843    initialCell = {}
3844    RcellLbls = {}
3845    zeroDict = {}
3846    for phase in Phases:
3847        phasedict = Phases[phase]
3848        pId = phasedict['pId']
3849        pfx = str(pId)+'::' # prefix for A values from phase
3850        RcellLbls[pId] = [pfx+'A'+str(i) for i in range(6)]
3851        RecpCellTerms[pId] = G2lat.cell2A(phasedict['General']['Cell'][1:7])
3852        zeroDict[pId] = dict(zip(RcellLbls[pId],6*[0.,]))
3853        SGdata[pId] = phasedict['General']['SGData']
3854        laue = SGdata[pId]['SGLaue']
3855        if laue == '2/m':
3856            laue += SGdata[pId]['SGUniq']
3857        for symlist,celllist in cellGUIlist:
3858            if laue in symlist:
3859                uniqCellIndx[pId] = celllist
3860                break
3861        else: # should not happen
3862            uniqCellIndx[pId] = range(6)
3863        for i in uniqCellIndx[pId]:
3864            initialCell[str(pId)+'::A'+str(i)] =  RecpCellTerms[pId][i]
3865
3866    SetDataMenuBar(G2frame,G2frame.dataFrame.SequentialMenu)
3867    G2frame.dataFrame.SetLabel('Sequential refinement results')
3868    if not G2frame.dataFrame.GetStatusBar():
3869        Status = G2frame.dataFrame.CreateStatusBar()
3870        Status.SetStatusText('')
3871    G2frame.dataFrame.Bind(wx.EVT_MENU, OnRenameSelSeq, id=wxID_RENAMESEQSEL)
3872    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSelSeq, id=wxID_SAVESEQSEL)
3873    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSelSeqCSV, id=wxID_SAVESEQSELCSV)
3874    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSeqCSV, id=wxID_SAVESEQCSV)
3875    G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlotSelSeq, id=wxID_PLOTSEQSEL)
3876    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAveSelSeq, id=wxID_AVESEQSEL)
3877    G2frame.dataFrame.Bind(wx.EVT_MENU, OnReOrgSelSeq, id=wxID_ORGSEQSEL)
3878    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewPseudoVar, id=wxADDSEQVAR)
3879    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewDistPseudoVar, id=wxADDSEQDIST)
3880    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewAnglePseudoVar, id=wxADDSEQANGLE)
3881    G2frame.dataFrame.Bind(wx.EVT_MENU, DelPseudoVar, id=wxDELSEQVAR)
3882    G2frame.dataFrame.Bind(wx.EVT_MENU, EditPseudoVar, id=wxEDITSEQVAR)
3883    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewParFitEq, id=wxADDPARFIT)
3884    G2frame.dataFrame.Bind(wx.EVT_MENU, CopyParFitEq, id=wxCOPYPARFIT)
3885    G2frame.dataFrame.Bind(wx.EVT_MENU, DelParFitEq, id=wxDELPARFIT)
3886    G2frame.dataFrame.Bind(wx.EVT_MENU, EditParFitEq, id=wxEDITPARFIT)
3887    G2frame.dataFrame.Bind(wx.EVT_MENU, DoParEqFit, id=wxDOPARFIT)
3888
3889    for id in G2frame.dataFrame.SeqExportLookup:       
3890        G2frame.dataFrame.Bind(wx.EVT_MENU, DoSequentialExport, id=id)
3891
3892    EnablePseudoVarMenus()
3893    EnableParFitEqMenus()
3894
3895    # scan for locations where the variables change
3896    VaryListChanges = [] # histograms where there is a change
3897    combinedVaryList = []
3898    firstValueDict = {}
3899    vallookup = {}
3900    posdict = {}
3901    prevVaryList = []
3902    foundNames = []
3903    missing = 0
3904    for i,name in enumerate(histNames):
3905        if name not in data:
3906            if missing < 5:
3907                print(" Warning: "+name+" not found")
3908            elif missing == 5:
3909                print ' Warning: more are missing'
3910            missing += 1
3911            continue
3912        foundNames.append(name)
3913        maxPWL = 5
3914        for var,val,sig in zip(data[name]['varyList'],data[name]['variables'],data[name]['sig']):
3915            svar = striphist(var,'*') # wild-carded
3916            if 'PWL' in svar:
3917                if int(svar.split(':')[-1]) > maxPWL:
3918                    continue
3919            if svar not in combinedVaryList:
3920                # add variables to list as they appear
3921                combinedVaryList.append(svar)
3922                firstValueDict[svar] = (val,sig)
3923        if prevVaryList != data[name]['varyList']: # this refinement has a different refinement list from previous
3924            prevVaryList = data[name]['varyList']
3925            vallookup[name] = dict(zip(data[name]['varyList'],data[name]['variables']))
3926            posdict[name] = {}
3927            for var in data[name]['varyList']:
3928                svar = striphist(var,'*')
3929                if 'PWL' in svar:
3930                    if int(svar.split(':')[-1]) > maxPWL:
3931                        continue
3932                posdict[name][combinedVaryList.index(svar)] = svar
3933            VaryListChanges.append(name)
3934    if missing:
3935        print ' Warning: Total of %d data sets missing from sequential results'%(missing)
3936    if len(VaryListChanges) > 1:
3937        G2frame.dataFrame.SequentialFile.Enable(wxID_ORGSEQSEL,True)
3938    else:
3939        G2frame.dataFrame.SequentialFile.Enable(wxID_ORGSEQSEL,False)
3940    #-----------------------------------------------------------------------------------
3941    # build up the data table by columns -----------------------------------------------
3942    histNames = foundNames
3943    nRows = len(histNames)
3944    G2frame.colList = [nRows*[True]]
3945    G2frame.colSigs = [None]
3946    colLabels = ['Use']
3947    Types = [wg.GRID_VALUE_BOOL]
3948    # start with Rwp values
3949    if 'IMG ' not in histNames[0][:4]:
3950        G2frame.colList += [[data[name]['Rvals']['Rwp'] for name in histNames]]
3951        G2frame.colSigs += [None]
3952        colLabels += ['Rwp']
3953        Types += [wg.GRID_VALUE_FLOAT+':10,3',]
3954    # add % change in Chi^2 in last cycle
3955    if histNames[0][:4] not in ['SASD','IMG '] and Controls.get('ShowCell'):
3956        G2frame.colList += [[100.*data[name]['Rvals'].get('DelChi2',-1) for name in histNames]]
3957        G2frame.colSigs += [None]
3958        colLabels += [u'\u0394\u03C7\u00B2 (%)']
3959        Types += [wg.GRID_VALUE_FLOAT+':10,5',]
3960    deltaChiCol = len(colLabels)-1
3961    # add changing sample parameters to table
3962    for key in sampleParms:
3963        G2frame.colList += [sampleParms[key]]
3964        G2frame.colSigs += [None]
3965        colLabels += [key]
3966        Types += [wg.GRID_VALUE_FLOAT,]
3967    sampleDict = {}
3968    for i,name in enumerate(histNames):
3969        sampleDict[name] = dict(zip(sampleParms.keys(),[sampleParms[key][i] for key in sampleParms.keys()])) 
3970    # add unique cell parameters TODO: review this where the cell symmetry changes (when possible)
3971    if Controls.get('ShowCell',False) and len(newCellDict):
3972        for pId in sorted(RecpCellTerms):
3973            pfx = str(pId)+'::' # prefix for A values from phase
3974            cells = []
3975            cellESDs = []
3976            colLabels += [pfx+cellUlbl[i] for i in uniqCellIndx[pId]]
3977            colLabels += [pfx+'Vol']
3978            Types += (len(uniqCellIndx[pId]))*[wg.GRID_VALUE_FLOAT+':10,5',]
3979            Types += [wg.GRID_VALUE_FLOAT+':10,3',]
3980            Albls = [pfx+'A'+str(i) for i in range(6)]
3981            for hId,name in enumerate(histNames):
3982                phfx = '%d:%d:'%(pId,hId)
3983                esdLookUp = {}
3984                dLookup = {}
3985                for item in data[name]['newCellDict']:
3986                    if phfx+item.split('::')[1] in data[name]['varyList']:
3987                        esdLookUp[newCellDict[item][0]] = item
3988                        dLookup[item] = newCellDict[item][0]
3989                covData = {'varyList': [dLookup.get(striphist(v),v) for v in data[name]['varyList']],
3990                    'covMatrix': data[name]['covMatrix']}
3991                A = RecpCellTerms[pId][:] # make copy of starting A values
3992                # update with refined values
3993                for i in range(6):
3994                    var = str(pId)+'::A'+str(i)
3995                    if var in cellAlist:
3996                        try:
3997                            val = data[name]['newCellDict'][esdLookUp[var]][1] # get refined value
3998                            A[i] = val # override with updated value
3999                        except KeyError:
4000                            A[i] = None
4001                # apply symmetry
4002                cellDict = dict(zip(Albls,A))
4003                if None in A:
4004                    c = 6*[None]
4005                    cE = 6*[None]
4006                    vol = None
4007                else:
4008                    A,zeros = G2stIO.cellFill(pfx,SGdata[pId],cellDict,zeroDict[pId])
4009                    # convert to direct cell & add only unique values to table
4010                    c = G2lat.A2cell(A)
4011                    vol = G2lat.calc_V(A)
4012                    cE = G2stIO.getCellEsd(pfx,SGdata[pId],A,covData)
4013                cells += [[c[i] for i in uniqCellIndx[pId]]+[vol]]
4014                cellESDs += [[cE[i] for i in uniqCellIndx[pId]]+[cE[-1]]]
4015            G2frame.colList += zip(*cells)
4016            G2frame.colSigs += zip(*cellESDs)
4017    # sort out the variables in their selected order
4018    varcols = 0
4019    for d in posdict.itervalues():
4020        varcols = max(varcols,max(d.keys())+1)
4021    # get labels for each column
4022    for i in range(varcols):
4023        lbl = ''
4024        for h in VaryListChanges:
4025            if posdict[h].get(i):
4026                if posdict[h].get(i) in lbl: continue
4027                if lbl != "": lbl += '/'
4028                lbl += posdict[h].get(i)
4029        colLabels.append(lbl)
4030    Types += varcols*[wg.GRID_VALUE_FLOAT,]
4031    vals = []
4032    esds = []
4033    varsellist = None        # will be a list of variable names in the order they are selected to appear
4034    # tabulate values for each hist, leaving None for blank columns
4035    for name in histNames:
4036        if name in posdict:
4037            varsellist = [posdict[name].get(i) for i in range(varcols)]
4038            # translate variable names to how they will be used in the headings
4039            vs = [striphist(v,'*') for v in data[name]['varyList']]
4040            # determine the index for each column (or None) in the data[]['variables'] and ['sig'] lists
4041            sellist = [vs.index(v) if v is not None else None for v in varsellist]
4042            #sellist = [i if striphist(v,'*') in varsellist else None for i,v in enumerate(data[name]['varyList'])]
4043        if not varsellist: raise Exception()
4044        vals.append([data[name]['variables'][s] if s is not None else None for s in sellist])
4045        esds.append([data[name]['sig'][s] if s is not None else None for s in sellist])
4046        #GSASIIpath.IPyBreak()
4047    G2frame.colList += zip(*vals)
4048    G2frame.colSigs += zip(*esds)
4049    # compute and add weight fractions to table if varied
4050    for phase in Phases:
4051        var = str(Phases[phase]['pId'])+':*:Scale'
4052        if var not in combinedVaryList: continue
4053        wtFrList = []
4054        sigwtFrList = []
4055        for i,name in enumerate(histNames):
4056            wtFrSum = 0.
4057            for phase1 in Phases:
4058                wtFrSum += Phases[phase1]['Histograms'][name]['Scale'][0]*Phases[phase1]['General']['Mass']
4059            var = str(Phases[phase]['pId'])+':'+str(i)+':Scale'
4060            wtFr = Phases[phase]['Histograms'][name]['Scale'][0]*Phases[phase]['General']['Mass']/wtFrSum
4061            wtFrList.append(wtFr)
4062            if var in data[name]['varyList']:
4063                sig = data[name]['sig'][data[name]['varyList'].index(var)]*wtFr/Phases[phase]['Histograms'][name]['Scale'][0]
4064            else:
4065                sig = 0.0
4066            sigwtFrList.append(sig)
4067        colLabels.append(str(Phases[phase]['pId'])+':*:WgtFrac')
4068        Types += [wg.GRID_VALUE_FLOAT+':10,5',]
4069        G2frame.colList += [wtFrList]
4070        G2frame.colSigs += [sigwtFrList]
4071               
4072    # tabulate constrained variables, removing histogram numbers if needed
4073    # from parameter label
4074    depValDict = {}
4075    depSigDict = {}
4076    for name in histNames:
4077        for var in data[name].get('depParmDict',{}):
4078            val,sig = data[name]['depParmDict'][var]
4079            svar = striphist(var,'*')
4080            if svar not in depValDict:
4081               depValDict[svar] = [val]
4082               depSigDict[svar] = [sig]
4083            else:
4084               depValDict[svar].append(val)
4085               depSigDict[svar].append(sig)
4086    # add the dependent constrained variables to the table
4087    for var in sorted(depValDict):
4088        if len(depValDict[var]) != len(histNames): continue
4089        colLabels.append(var)
4090        Types += [wg.GRID_VALUE_FLOAT+':10,5',]
4091        G2frame.colSigs += [depSigDict[var]]
4092        G2frame.colList += [depValDict[var]]
4093
4094    # add atom parameters to table
4095    colLabels += atomLookup.keys()
4096    for parm in sorted(atomLookup):
4097        G2frame.colList += [[data[name]['newAtomDict'][atomLookup[parm]][1] for name in histNames]]
4098        Types += [wg.GRID_VALUE_FLOAT+':10,5',]
4099        if atomLookup[parm] in data[histNames[0]]['varyList']:
4100            col = data[histNames[0]]['varyList'].index(atomLookup[parm])
4101            G2frame.colSigs += [[data[name]['sig'][col] for name in histNames]]
4102        else:
4103            G2frame.colSigs += [None]
4104    # evaluate Pseudovars, their ESDs and add them to grid
4105    for expr in data['SeqPseudoVars']:
4106        obj = data['SeqPseudoVars'][expr]
4107        calcobj = G2obj.ExpressionCalcObj(obj)
4108        valList = []
4109        esdList = []
4110        for seqnum,name in enumerate(histNames):
4111            sigs = data[name]['sig']
4112            G2mv.InitVars()
4113            parmDict = data[name].get('parmDict')
4114            constraintInfo = data[name].get('constraintInfo',[[],[],{},[],seqnum])
4115            groups,parmlist,constrDict,fixedList,ihst = constraintInfo
4116            varyList = data[name]['varyList']
4117            parmDict = data[name]['parmDict']
4118            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmDict,SeqHist=ihst)
4119            if 'Dist' in expr:
4120                derivs = G2mth.CalcDistDeriv(obj.distance_dict,obj.distance_atoms, parmDict)
4121                pId = obj.distance_dict['pId']
4122                aId,bId = obj.distance_atoms
4123                varyNames = ['%d::dA%s:%d'%(pId,ip,aId) for ip in ['x','y','z']]
4124                varyNames += ['%d::dA%s:%d'%(pId,ip,bId) for ip in ['x','y','z']]
4125                VCoV = G2mth.getVCov(varyNames,varyList,data[name]['covMatrix'])
4126                esdList.append(np.sqrt(np.inner(derivs,np.inner(VCoV,derivs.T)) ))
4127#                GSASIIpath.IPyBreak()
4128            elif 'Angle' in expr:
4129                derivs = G2mth.CalcAngleDeriv(obj.angle_dict,obj.angle_atoms, parmDict)
4130                pId = obj.angle_dict['pId']
4131                aId,bId = obj.angle_atoms
4132                varyNames = ['%d::dA%s:%d'%(pId,ip,aId) for ip in ['x','y','z']]
4133                varyNames += ['%d::dA%s:%d'%(pId,ip,bId[0]) for ip in ['x','y','z']]
4134                varyNames += ['%d::dA%s:%d'%(pId,ip,bId[1]) for ip in ['x','y','z']]
4135                VCoV = G2mth.getVCov(varyNames,varyList,data[name]['covMatrix'])
4136                esdList.append(np.sqrt(np.inner(derivs,np.inner(VCoV,derivs.T)) ))
4137            else:
4138                derivs = np.array(
4139                    [EvalPSvarDeriv(calcobj,parmDict.copy(),sampleDict[name],var,ESD)
4140                     for var,ESD in zip(varyList,sigs)])
4141                if None in list(derivs):
4142                    esdList.append(None)
4143                else:
4144                    esdList.append(np.sqrt(
4145                        np.inner(derivs,np.inner(data[name]['covMatrix'],derivs.T)) ))
4146            PSvarDict = parmDict.copy()
4147            PSvarDict.update(sampleDict[name])
4148            UpdateParmDict(PSvarDict)
4149            calcobj.UpdateDict(PSvarDict)
4150            valList.append(calcobj.EvalExpression())
4151#            if calcobj.su is not None: esdList[-1] = calcobj.su
4152        if not esdList:
4153            esdList = None
4154        G2frame.colList += [valList]
4155        G2frame.colSigs += [esdList]
4156        colLabels += [expr]
4157        Types += [wg.GRID_VALUE_FLOAT+':10,3']
4158    #---- table build done -------------------------------------------------------------
4159
4160    # Make dict needed for creating & editing pseudovars (PSvarDict).
4161   
4162    name = histNames[0]
4163    parmDict = data[name].get('parmDict',{})
4164    PSvarDict = parmDict.copy()
4165    PSvarDict.update(sampleParms)
4166    UpdateParmDict(PSvarDict)
4167    # Also dicts of variables
4168    # for Parametric fitting from the data table
4169    parmDict = dict(zip(colLabels,zip(*G2frame.colList)[0])) # scratch dict w/all values in table
4170    parmDict.update({var:val for var,val in newCellDict.values()}) #  add varied reciprocal cell terms
4171    del parmDict['Use']
4172    name = histNames[0]
4173
4174    #******************************************************************************
4175    # create a set of values for example evaluation of pseudovars and
4176    # this does not work for refinements that have differing numbers of variables.
4177    #raise Exception
4178    VarDict = {}
4179    for i,var in enumerate(colLabels):
4180        if var in ['Use','Rwp',u'\u0394\u03C7\u00B2 (%)']: continue
4181        if G2frame.colList[i][0] is None:
4182            val,sig = firstValueDict.get(var,[None,None])
4183        elif G2frame.colSigs[i]:
4184            val,sig = G2frame.colList[i][0],G2frame.colSigs[i][0]
4185        else:
4186            val,sig = G2frame.colList[i][0],None
4187#        if val is None: continue
4188#        elif sig is None or sig == 0.:
4189#            VarDict[var] = val
4190        if striphist(var) not in Dlookup:
4191            VarDict[var] = val
4192    # add recip cell coeff. values
4193    VarDict.update({var:val for var,val in newCellDict.values()})
4194
4195    G2frame.dataFrame.currentGrids = []
4196    G2frame.dataDisplay = G2G.GSGrid(parent=G2frame.dataFrame)
4197    G2frame.SeqTable = G2G.Table([list(cl) for cl in zip(*G2frame.colList)],     # convert from columns to rows
4198        colLabels=colLabels,rowLabels=histNames,types=Types)
4199    G2frame.dataDisplay.SetTable(G2frame.SeqTable, True)
4200    #G2frame.dataDisplay.EnableEditing(False)
4201    # make all but first column read-only
4202    for c in range(1,len(colLabels)):
4203        for r in range(nRows):
4204            G2frame.dataDisplay.SetCellReadOnly(r,c)
4205    G2frame.dataDisplay.Bind(wg.EVT_GRID_LABEL_LEFT_DCLICK, PlotSelect)
4206    G2frame.dataDisplay.Bind(wg.EVT_GRID_LABEL_RIGHT_CLICK, SetLabelString)
4207    G2frame.dataDisplay.SetRowLabelSize(8*len(histNames[0]))       #pretty arbitrary 8
4208    G2frame.dataDisplay.SetMargins(0,0)
4209    G2frame.dataDisplay.AutoSizeColumns(False)
4210    if prevSize:
4211        G2frame.dataFrame.setSizePosLeft(prevSize)
4212    else:
4213        G2frame.dataFrame.setSizePosLeft([700,350])
4214    # highlight unconverged shifts
4215    if histNames[0][:4] not in ['SASD','IMG ']:
4216        for row,name in enumerate(histNames):
4217            deltaChi = G2frame.SeqTable.GetValue(row,deltaChiCol)
4218            if deltaChi > 10.:
4219                G2frame.dataDisplay.SetCellStyle(row,deltaChiCol,color=wx.Colour(255,0,0))
4220            elif deltaChi > 1.0:
4221                G2frame.dataDisplay.SetCellStyle(row,deltaChiCol,color=wx.Colour(255,255,0))
4222    G2frame.dataDisplay.InstallGridToolTip(GridSetToolTip,GridColLblToolTip)
4223    G2frame.dataDisplay.SendSizeEvent() # resize needed on mac
4224    G2frame.dataDisplay.Refresh() # shows colored text on mac
4225   
4226################################################################################
4227#####  Main PWDR panel
4228################################################################################           
4229       
4230def UpdatePWHKPlot(G2frame,kind,item):
4231    '''Called when the histogram main tree entry is called. Displays the
4232    histogram weight factor, refinement statistics for the histogram
4233    and the range of data for a simulation.
4234
4235    Also invokes a plot of the histogram.
4236    '''
4237    def onEditSimRange(event):
4238        'Edit simulation range'
4239        inp = [
4240            min(data[1][0]),
4241            max(data[1][0]),
4242            None
4243            ]
4244        inp[2] = (inp[1] - inp[0])/(len(data[1][0])-1.)
4245        names = ('start angle', 'end angle', 'step size')
4246        dlg = G2G.ScrolledMultiEditor(
4247            G2frame,[inp] * len(inp), range(len(inp)), names,
4248            header='Edit simulation range',
4249            minvals=(0.001,0.001,0.0001),
4250            maxvals=(180.,180.,.1),
4251            )
4252        dlg.CenterOnParent()
4253        val = dlg.ShowModal()
4254        dlg.Destroy()
4255        if val != wx.ID_OK: return
4256        if inp[0] > inp[1]:
4257            end,start,step = inp
4258        else:               
4259            start,end,step = inp
4260        step = abs(step)
4261        N = int((end-start)/step)+1
4262        newdata = np.linspace(start,end,N,True)
4263        if len(newdata) < 2: return # too small a range - reject
4264        data[1] = [newdata,np.zeros_like(newdata),np.ones_like(newdata),
4265            np.zeros_like(newdata),np.zeros_like(newdata),np.zeros_like(newdata)]
4266        Tmin = newdata[0]
4267        Tmax = newdata[-1]
4268        G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,item,'Limits'),
4269            [(Tmin