source: trunk/GSASIIgrid.py @ 2842

Last change on this file since 2842 was 2842, checked in by toby, 6 years ago

cleanups: crash in show LS params; show constraint vars; set filters blank. Tutorials: set import to use download directory; Copy of phase/hist parms: remove current hist from selection; cleanup labels; Extensive revisions to SeqRef? tutorial

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