source: trunk/GSASIIgrid.py @ 2482

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

allow atom constraints between phases

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