source: trunk/GSASIIgrid.py @ 2767

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

make clearer H->R & R->H transformation labels
implement generic unit scatterer for reflectometry (& small angle) modeling
implement plot of scattering contrast with transition markers

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