source: trunk/GSASIIgrid.py @ 2695

Last change on this file since 2695 was 2695, checked in by vondreele, 7 years ago

use min(intensity) as default for PDF Flat Bkg - gives better start for optimize PDF
refactor sequential refinements so that old results are updated (not erased)

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