source: trunk/GSASIIgrid.py @ 2512

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

remove 'constraint' and 'restraint' from Consraints & Restraints tabs
work on automatic nucl-mag constraints
allow selection of possible mag atoms to use in new mag phase
fix issues with chemical composition restraints

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