source: trunk/GSASIIgrid.py @ 2602

Last change on this file since 2602 was 2602, checked in by toby, 5 years ago

redo drawing and editing of masks, document; add transfer angles to Integration controls

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