source: trunk/GSASIIgrid.py @ 2681

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

fix missing 'Oblique' problem in G2imgGUI
fix inner 2-theta limit issue in OnTransferAngles?
PDF Peaks can now clear all peaks
PDF peak picking now gives pos,mag & sig=0.085 as default
calculated PDF peak fit curve now plotted on G(R) from PDF Peaks
fitting of PDF peaks implemented - works fine; needs text output

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 223.5 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIgrid - data display routines
3########### SVN repository information ###################
4# $Date: 2017-01-31 21:38:48 +0000 (Tue, 31 Jan 2017) $
5# $Author: vondreele $
6# $Revision: 2681 $
7# $URL: trunk/GSASIIgrid.py $
8# $Id: GSASIIgrid.py 2681 2017-01-31 21:38:48Z 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: 2681 $")
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        self.ImageEdit = wx.Menu(title='')
2075        self.ImageMenu.Append(menu=self.ImageEdit, title='Operations')
2076        self.ImageEdit.Append(help='Calibrate detector by fitting to calibrant lines', 
2077            id=wxID_IMCALIBRATE, kind=wx.ITEM_NORMAL,text='Calibrate')
2078        self.ImageEdit.Append(help='Recalibrate detector by fitting to calibrant lines', 
2079            id=wxID_IMRECALIBRATE, kind=wx.ITEM_NORMAL,text='Recalibrate')
2080        self.ImageEdit.Append(help='Recalibrate all images by fitting to calibrant lines', 
2081            id=wxID_IMRECALIBALL, kind=wx.ITEM_NORMAL,text='Recalibrate all')           
2082        self.ImageEdit.Append(help='Clear calibration data points and rings',id=wxID_IMCLEARCALIB, 
2083            kind=wx.ITEM_NORMAL,text='Clear calibration')
2084        self.ImageEdit.Append(help='Integrate selected image',id=wxID_IMINTEGRATE, 
2085            kind=wx.ITEM_NORMAL,text='Integrate')
2086        self.ImageEdit.Append(help='Integrate all images selected from list',id=wxID_INTEGRATEALL,
2087            kind=wx.ITEM_NORMAL,text='Integrate all')
2088        self.ImageEdit.Append(help='Copy image controls to other images', 
2089            id=wxID_IMCOPYCONTROLS, kind=wx.ITEM_NORMAL,text='Copy Controls')
2090        self.ImageEdit.Append(help='Copy selected image controls to other images', 
2091            id=wxID_IMCOPYSELECTED, kind=wx.ITEM_NORMAL,text='Copy Selected')
2092        self.ImageEdit.Append(help='Save image controls to file', 
2093            id=wxID_IMSAVECONTROLS, kind=wx.ITEM_NORMAL,text='Save Controls')
2094        self.ImageEdit.Append(help='Save controls from selected images to file', 
2095            id=wxID_SAVESELECTEDCONTROLS, kind=wx.ITEM_NORMAL,text='Save Multiple Controls')
2096        self.ImageEdit.Append(help='Load image controls from file',
2097            id=wxID_IMLOADCONTROLS, kind=wx.ITEM_NORMAL,text='Load Controls')
2098        self.ImageEdit.Append(help='Transfer integration range for other detector distances', 
2099            id=wxID_IMXFERCONTROLS, kind=wx.ITEM_NORMAL,text='Xfer angles')
2100        self.ImageEdit.Append(help='Open Auto-integration window to integrate a series of images', 
2101            id=wxID_IMAUTOINTEG, kind=wx.ITEM_NORMAL,text='Auto Integrate')
2102        self.PostfillDataMenu()
2103           
2104        # IMG / Masks
2105        self.MaskMenu = wx.MenuBar()
2106        self.PrefillDataMenu(self.MaskMenu)
2107        self.MaskEdit = wx.Menu(title='')
2108        self.MaskMenu.Append(menu=self.MaskEdit, title='Operations')
2109        submenu = wx.Menu()
2110        self.MaskEdit.AppendMenu(
2111            wx.ID_ANY,'Create new', submenu,
2112            help=''
2113            )
2114        self.MaskEdit.Append(help='Copy mask to other images', 
2115            id=wxID_MASKCOPY, kind=wx.ITEM_NORMAL,text='Copy mask')
2116        self.MaskEdit.Append(help='Save mask to file', 
2117            id=wxID_MASKSAVE, kind=wx.ITEM_NORMAL,text='Save mask')
2118        self.MaskEdit.Append(help='Load mask from file; ignoring threshold', 
2119            id=wxID_MASKLOADNOT, kind=wx.ITEM_NORMAL,text='Load mask')
2120        self.MaskEdit.Append(help='Load mask from file keeping the threshold value', 
2121            id=wxID_MASKLOAD, kind=wx.ITEM_NORMAL,text='Load mask w/threshold')
2122        self.MaskEdit.Append(help='Auto search for spot masks; NB: will clear old spot masks', 
2123            id=wxID_FINDSPOTS, kind=wx.ITEM_NORMAL,text='Auto spot masks')
2124        self.MaskEdit.Append(help='Delete all spot masks', 
2125            id=wxID_DELETESPOTS, kind=wx.ITEM_NORMAL,text='Delete spot masks')       
2126        submenu.Append(help='Create an arc mask with mouse input', 
2127            id=wxID_NEWMASKARC, kind=wx.ITEM_NORMAL,text='Arc mask')
2128        submenu.Append(help='Create a frame mask with mouse input', 
2129            id=wxID_NEWMASKFRAME, kind=wx.ITEM_NORMAL,text='Frame mask')
2130        submenu.Append(help='Create a polygon mask with mouse input', 
2131            id=wxID_NEWMASKPOLY, kind=wx.ITEM_NORMAL,text='Polygon mask')
2132        submenu.Append(help='Create a ring mask with mouse input', 
2133            id=wxID_NEWMASKRING, kind=wx.ITEM_NORMAL,text='Ring mask')
2134        submenu.Append(help='Create spot masks with mouse input', 
2135            id=wxID_NEWMASKSPOT, kind=wx.ITEM_NORMAL,text='Spot mask')
2136        self.PostfillDataMenu()
2137           
2138        # IMG / Stress/Strain
2139        self.StrStaMenu = wx.MenuBar()
2140        self.PrefillDataMenu(self.StrStaMenu)
2141        self.StrStaEdit = wx.Menu(title='')
2142        self.StrStaMenu.Append(menu=self.StrStaEdit, title='Operations')
2143        self.StrStaEdit.Append(help='Append d-zero for one ring', 
2144            id=wxID_APPENDDZERO, kind=wx.ITEM_NORMAL,text='Append d-zero')
2145        self.StrStaEdit.Append(help='Fit stress/strain data', 
2146            id=wxID_STRSTAFIT, kind=wx.ITEM_NORMAL,text='Fit stress/strain')
2147        self.StrStaEdit.Append(help='Plot intensity distribution', 
2148            id=wxID_STRSTAPLOT, kind=wx.ITEM_NORMAL,text='Plot intensity distribution')
2149        self.StrStaEdit.Append(help='Update d-zero from ave d-zero',
2150            id=wxID_UPDATEDZERO, kind=wx.ITEM_NORMAL,text='Update d-zero')       
2151        self.StrStaEdit.Append(help='Fit stress/strain data for all images', 
2152            id=wxID_STRSTAALLFIT, kind=wx.ITEM_NORMAL,text='All image fit')
2153        self.StrStaEdit.Append(help='Copy stress/strain data to other images', 
2154            id=wxID_STRSTACOPY, kind=wx.ITEM_NORMAL,text='Copy stress/strain')
2155        self.StrStaEdit.Append(help='Save stress/strain data to file', 
2156            id=wxID_STRSTASAVE, kind=wx.ITEM_NORMAL,text='Save stress/strain')
2157        self.StrStaEdit.Append(help='Load stress/strain data from file', 
2158            id=wxID_STRSTALOAD, kind=wx.ITEM_NORMAL,text='Load stress/strain')
2159        self.StrStaEdit.Append(help='Load sample data from file', 
2160            id=wxID_STRSTSAMPLE, kind=wx.ITEM_NORMAL,text='Load sample data')
2161        self.PostfillDataMenu()
2162           
2163        # PDF / PDF Controls
2164        self.PDFMenu = wx.MenuBar()
2165        self.PrefillDataMenu(self.PDFMenu)
2166        self.PDFEdit = wx.Menu(title='')
2167        self.PDFMenu.Append(menu=self.PDFEdit, title='PDF Controls')
2168        self.PDFEdit.Append(help='Add one or more elements to sample composition',id=wxID_PDFADDELEMENT, kind=wx.ITEM_NORMAL,
2169            text='Add elements')
2170        self.PDFEdit.Append(help='Delete element from sample composition',id=wxID_PDFDELELEMENT, kind=wx.ITEM_NORMAL,
2171            text='Delete element')
2172        self.PDFEdit.Append(help='Copy PDF controls', id=wxID_PDFCOPYCONTROLS, kind=wx.ITEM_NORMAL,
2173            text='Copy controls')
2174        self.PDFEdit.Append(help='Load PDF controls from file',id=wxID_PDFLOADCONTROLS, kind=wx.ITEM_NORMAL,
2175            text='Load Controls')
2176        self.PDFEdit.Append(help='Save PDF controls to file', id=wxID_PDFSAVECONTROLS, kind=wx.ITEM_NORMAL,
2177            text='Save controls')
2178        self.PDFEdit.Append(help='Compute PDF', id=wxID_PDFCOMPUTE, kind=wx.ITEM_NORMAL,
2179            text='Compute PDF')
2180        self.PDFEdit.Append(help='Compute all PDFs with or w/o optimization',
2181                            id=wxID_PDFCOMPUTEALL, kind=wx.ITEM_NORMAL,
2182            text='Compute all PDFs')
2183#        self.PDFEdit.Append(help='Optimize PDF', id=wxID_PDFOPT, kind=wx.ITEM_NORMAL,
2184#            text='Optimize corrections for r<Rmin section of current G(r)')
2185        self.PostfillDataMenu()
2186       
2187        # PDF / PDF Peaks
2188        self.PDFPksMenu = wx.MenuBar()
2189        self.PrefillDataMenu(self.PDFPksMenu)
2190        self.PDFPksEdit = wx.Menu(title='')
2191        self.PDFPksMenu.Append(menu=self.PDFPksEdit, title='PDF Peaks')
2192        self.PDFPksEdit.Append(help='Fit PDF peaks', id=wxID_PDFPKSFIT, kind=wx.ITEM_NORMAL,
2193            text='Fit Peaks')
2194        self.PDFPksEdit.Append(help='Fit all PDF peaks', id=wxID_PDFPKSFITALL, kind=wx.ITEM_NORMAL,
2195            text='Fit all PDF peakss')
2196        self.PDFPksEdit.Append(help='Copy PDF peaks', id=wxID_PDFCOPYPEAKS, kind=wx.ITEM_NORMAL,
2197            text='Copy peaks')
2198        self.PDFPksEdit.Append(help='Clear PDF peaks', id=wxID_CLEARPDFPEAKS, kind=wx.ITEM_NORMAL,
2199            text='Clear peaks')       
2200        self.PostfillDataMenu()
2201
2202       
2203        # Phase / General tab
2204        self.DataGeneral = wx.MenuBar()
2205        self.PrefillDataMenu(self.DataGeneral)
2206        self.DataGeneral.Append(menu=wx.Menu(title=''),title='Select tab')
2207        self.GeneralCalc = wx.Menu(title='')
2208        self.DataGeneral.Append(menu=self.GeneralCalc,title='Compute')
2209        self.GeneralCalc.Append(help='Compute Fourier map',id=wxID_FOURCALC, kind=wx.ITEM_NORMAL,
2210            text='Fourier map')
2211        self.GeneralCalc.Append(help='Search Fourier map',id=wxID_FOURSEARCH, kind=wx.ITEM_NORMAL,
2212            text='Search map')
2213        self.GeneralCalc.Append(help='Run charge flipping',id=wxID_CHARGEFLIP, kind=wx.ITEM_NORMAL,
2214            text='Charge flipping')
2215        self.GeneralCalc.Append(help='Run 4D charge flipping',id=wxID_4DCHARGEFLIP, kind=wx.ITEM_NORMAL,
2216            text='4D Charge flipping')
2217        self.GeneralCalc.Enable(wxID_4DCHARGEFLIP,False)   
2218        self.GeneralCalc.Append(help='Clear map',id=wxID_FOURCLEAR, kind=wx.ITEM_NORMAL,
2219            text='Clear map')
2220        self.GeneralCalc.Append(help='Run Monte Carlo - Simulated Annealing',id=wxID_SINGLEMCSA, kind=wx.ITEM_NORMAL,
2221            text='MC/SA')
2222        self.GeneralCalc.Append(help='Run Monte Carlo - Simulated Annealing on multiprocessors',id=wxID_MULTIMCSA, kind=wx.ITEM_NORMAL,
2223            text='Multi MC/SA')            #currently not useful
2224        self.GeneralCalc.Append(help='Transform crystal structure',id=wxID_TRANSFORMSTRUCTURE, kind=wx.ITEM_NORMAL,
2225            text='Transform')
2226        self.PostfillDataMenu()
2227       
2228        # Phase / Data tab
2229        self.DataMenu = wx.MenuBar()
2230        self.PrefillDataMenu(self.DataMenu)
2231        self.DataMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2232        self.DataEdit = wx.Menu(title='')
2233        self.DataMenu.Append(menu=self.DataEdit, title='Edit Phase')
2234        self.DataEdit.Append(id=wxID_DATACOPY, kind=wx.ITEM_NORMAL,text='Copy data',
2235            help='Copy phase data to other histograms')
2236        self.DataEdit.Append(id=wxID_DATACOPYFLAGS, kind=wx.ITEM_NORMAL,text='Copy flags',
2237            help='Copy phase data flags to other histograms')
2238        self.DataEdit.Append(id=wxID_DATASELCOPY, kind=wx.ITEM_NORMAL,text='Copy selected data',
2239            help='Copy selected phase data to other histograms')
2240        self.DataEdit.Append(id=wxID_DATAUSE, kind=wx.ITEM_NORMAL,text='Select used data',
2241            help='Select all histograms to use')
2242        self.DataEdit.Append(id=wxID_PWDRADD, kind=wx.ITEM_NORMAL,text='Add powder histograms',
2243            help='Select new powder histograms to be used for this phase')
2244        self.DataEdit.Append(id=wxID_HKLFADD, kind=wx.ITEM_NORMAL,text='Add single crystal histograms',
2245            help='Select new single crystal histograms to be used for this phase')
2246        self.DataEdit.Append(id=wxID_DATADELETE, kind=wx.ITEM_NORMAL,text='Remove histograms',
2247            help='Remove histograms from use for this phase')
2248        self.PostfillDataMenu()
2249           
2250        # Phase / Atoms tab
2251        self.AtomsMenu = wx.MenuBar()
2252        self.PrefillDataMenu(self.AtomsMenu)
2253        self.AtomsMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2254        self.AtomEdit = wx.Menu(title='')
2255        self.AtomCompute = wx.Menu(title='')
2256        self.AtomsMenu.Append(menu=self.AtomEdit, title='Edit Atoms')
2257        self.AtomsMenu.Append(menu=self.AtomCompute, title='Compute')
2258        submenu = wx.Menu()
2259        self.AtomEdit.AppendMenu(wx.ID_ANY, 'On selected atoms...', submenu, 
2260            help='Set/Act on selected atoms')
2261        submenu.Append(wxID_ATOMSSETSEL,
2262            help='Set refinement flags for selected atoms',
2263            kind=wx.ITEM_NORMAL,
2264            text='Refine selected')
2265        submenu.Append(id=wxID_ATOMSMODIFY, kind=wx.ITEM_NORMAL,text='Modify parameters',
2266            help='Modify parameters values for all selected atoms')
2267        submenu.Append(id=wxID_ATOMSEDITINSERT, kind=wx.ITEM_NORMAL,text='Insert atom',
2268            help='Inserts an H atom before all selected atoms')
2269        submenu.Append(id=wxID_ADDHATOM, kind=wx.ITEM_NORMAL,text='Calc H atoms',
2270            help='Insert H atoms in expected bonding positions for selected atoms')
2271        submenu.Append(id=wxID_ATOMSEDITDELETE, kind=wx.ITEM_NORMAL,text='Delete atom',
2272            help='Delete selected atoms')
2273        submenu.Append(id=wxID_ATOMSTRANSFORM, kind=wx.ITEM_NORMAL,text='Transform atoms',
2274            help='Symmetry transform selected atoms')
2275#        self.AtomEdit.Append(id=wxID_ATOMSROTATE, kind=wx.ITEM_NORMAL,text='Rotate atoms',
2276#            help='Select atoms to rotate first')
2277        submenu.Append(wxID_ATOMSSETALL,
2278            help='Set refinement flags for all atoms',
2279            kind=wx.ITEM_NORMAL,
2280            text='Select All')
2281       
2282        self.AtomEdit.Append(id=wxID_ATOMSEDITADD, kind=wx.ITEM_NORMAL,text='Append atom',
2283            help='Appended as an H atom')
2284        self.AtomEdit.Append(id=wxID_ATOMSVIEWADD, kind=wx.ITEM_NORMAL,text='Append view point',
2285            help='Appended as an H atom')
2286        self.AtomEdit.Append(id=wxID_ATOMVIEWINSERT, kind=wx.ITEM_NORMAL,text='Insert view point',
2287            help='Select atom row to insert before; inserted as an H atom')
2288        self.AtomEdit.Append(id=wxID_UPDATEHATOM, kind=wx.ITEM_NORMAL,text='Update H atoms',
2289            help='Update H atoms in standard positions')
2290        self.AtomEdit.Append(id=wxID_ATOMMOVE, kind=wx.ITEM_NORMAL,text='Move selected atom to view point',
2291            help='Select a single atom to be moved to view point in plot')
2292        self.AtomEdit.Append(id=wxID_MAKEMOLECULE, kind=wx.ITEM_NORMAL,text='Assemble molecule',
2293            help='Select a single atom to assemble as a molecule from scattered atom positions')
2294        self.AtomEdit.Append(id=wxID_RELOADDRAWATOMS, kind=wx.ITEM_NORMAL,text='Reload draw atoms',
2295            help='Reload atom drawing list')
2296        submenu = wx.Menu()
2297        self.AtomEdit.AppendMenu(wx.ID_ANY, 'Reimport atoms', submenu, 
2298            help='Reimport atoms from file; sequence must match')
2299        # setup a cascade menu for the formats that have been defined
2300        self.ReImportMenuId = {}  # points to readers for each menu entry
2301        for reader in self.G2frame.ImportPhaseReaderlist:
2302            item = submenu.Append(
2303                wx.ID_ANY,help=reader.longFormatName,
2304                kind=wx.ITEM_NORMAL,text='reimport coordinates from '+reader.formatName+' file')
2305            self.ReImportMenuId[item.GetId()] = reader
2306        item = submenu.Append(
2307            wx.ID_ANY,
2308            help='Reimport coordinates, try to determine format from file',
2309            kind=wx.ITEM_NORMAL,
2310            text='guess format from file')
2311        self.ReImportMenuId[item.GetId()] = None # try all readers
2312
2313        self.AtomCompute.Append(id=wxID_ATOMSDISAGL, kind=wx.ITEM_NORMAL,text='Show Distances && Angles',
2314            help='Compute distances & angles for selected atoms')
2315        self.AtomCompute.Append(id=wxID_ATOMSPDISAGL, kind=wx.ITEM_NORMAL,text='Save Distances && Angles',
2316            help='Compute distances & angles for selected atoms')
2317        self.AtomCompute.ISOcalc = self.AtomCompute.Append(
2318            id=wxID_ISODISP, kind=wx.ITEM_NORMAL,
2319            text='ISODISTORT mode values',
2320            help='Compute values of ISODISTORT modes from atom parameters')
2321        self.AtomCompute.Append(id=wxID_ATOMSDENSITY, kind=wx.ITEM_NORMAL,
2322            text='Density',
2323            help='Compute density for current phase')
2324        self.PostfillDataMenu()
2325       
2326        # Phase / Imcommensurate "waves" tab
2327        self.WavesData = wx.MenuBar()
2328        self.PrefillDataMenu(self.WavesData)
2329        self.WavesData.Append(menu=wx.Menu(title=''),title='Select tab')
2330        self.WavesDataEdit = wx.Menu(title='')
2331        self.WavesData.Append(menu=self.WavesDataEdit, title='Edit Wave')
2332        self.WavesDataEdit.Append(id=wxID_WAVEVARY, kind=wx.ITEM_NORMAL,text='Global wave vary',
2333            help='Global setting of wave vary flags')
2334        self.PostfillDataMenu()
2335       
2336        # Phase / Layer tab
2337        self.LayerData = wx.MenuBar()
2338        self.PrefillDataMenu(self.LayerData)
2339        self.LayerData.Append(menu=wx.Menu(title=''),title='Select tab')
2340        self.LayerDataEdit = wx.Menu(title='')
2341        self.LayerData.Append(menu=self.LayerDataEdit, title='Operations')
2342        self.LayerDataEdit.Append(id=wxID_LOADDIFFAX, kind=wx.ITEM_NORMAL,text='Load from DIFFaX file',
2343            help='Load layer info from DIFFaX file')
2344        self.LayerDataEdit.Append(id=wxID_COPYPHASE, kind=wx.ITEM_NORMAL,text='Copy phase cell',
2345            help='Copy phase cell from another project')
2346        self.LayerDataEdit.Append(id=wxID_LAYERSIMULATE, kind=wx.ITEM_NORMAL,text='Simulate pattern',
2347            help='Simulate diffraction pattern from layer stacking')
2348        self.LayerDataEdit.Append(id=wxID_LAYERSFIT, kind=wx.ITEM_NORMAL,text='Fit pattern',
2349            help='Fit diffraction pattern with layer stacking model')
2350        self.LayerDataEdit.Append(id=wxID_SEQUENCESIMULATE, kind=wx.ITEM_NORMAL,text='Sequence simulations',
2351            help='Sequence simulation changing one parameter')
2352        self.PostfillDataMenu()
2353                 
2354        # Phase / Draw Options tab
2355        self.DataDrawOptions = wx.MenuBar()
2356        self.PrefillDataMenu(self.DataDrawOptions)
2357        self.DataDrawOptions.Append(menu=wx.Menu(title=''),title='Select tab')
2358        self.PostfillDataMenu()
2359       
2360        # Phase / Draw Atoms tab
2361        self.DrawAtomsMenu = wx.MenuBar()
2362        self.PrefillDataMenu(self.DrawAtomsMenu)
2363        self.DrawAtomsMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2364        self.DrawAtomEdit = wx.Menu(title='')
2365        self.DrawAtomCompute = wx.Menu(title='')
2366        self.DrawAtomRestraint = wx.Menu(title='')
2367        self.DrawAtomRigidBody = wx.Menu(title='')
2368        self.DrawAtomsMenu.Append(menu=self.DrawAtomEdit, title='Edit Figure')
2369        self.DrawAtomsMenu.Append(menu=self.DrawAtomCompute,title='Compute')
2370        self.DrawAtomsMenu.Append(menu=self.DrawAtomRestraint, title='Restraints')
2371        self.DrawAtomsMenu.Append(menu=self.DrawAtomRigidBody, title='Rigid body')
2372        self.DrawAtomEdit.Append(id=wxID_DRAWATOMSTYLE, kind=wx.ITEM_NORMAL,text='Atom style',
2373            help='Select atoms first')
2374        self.DrawAtomEdit.Append(id=wxID_DRAWATOMLABEL, kind=wx.ITEM_NORMAL,text='Atom label',
2375            help='Select atoms first')
2376        self.DrawAtomEdit.Append(id=wxID_DRAWATOMCOLOR, kind=wx.ITEM_NORMAL,text='Atom color',
2377            help='Select atoms first')
2378        self.DrawAtomEdit.Append(id=wxID_DRAWATOMRESETCOLOR, kind=wx.ITEM_NORMAL,text='Reset atom colors',
2379            help='Resets all atom colors to defaults')
2380        self.DrawAtomEdit.Append(id=wxID_DRWAEDITRADII, kind=wx.ITEM_NORMAL,text='Edit atom radii',
2381            help='Edit drawing atom radii')
2382        self.DrawAtomEdit.Append(id=wxID_DRAWVIEWPOINT, kind=wx.ITEM_NORMAL,text='View point',
2383            help='View point is 1st atom selected')
2384        self.DrawAtomEdit.Append(id=wxID_DRAWADDEQUIV, kind=wx.ITEM_NORMAL,text='Add atoms',
2385            help='Add symmetry & cell equivalents to drawing set from selected atoms')
2386        self.DrawAtomEdit.Append(id=wxID_DRAWADDSPHERE, kind=wx.ITEM_NORMAL,text='Add sphere of atoms',
2387            help='Add atoms within sphere of enclosure')
2388        self.DrawAtomEdit.Append(id=wxID_DRAWTRANSFORM, kind=wx.ITEM_NORMAL,text='Transform draw atoms',
2389            help='Transform selected atoms by symmetry & cell translations')
2390        self.DrawAtomEdit.Append(id=wxID_DRAWFILLCOORD, kind=wx.ITEM_NORMAL,text='Fill CN-sphere',
2391            help='Fill coordination sphere for selected atoms')           
2392        self.DrawAtomEdit.Append(id=wxID_DRAWFILLCELL, kind=wx.ITEM_NORMAL,text='Fill unit cell',
2393            help='Fill unit cell with selected atoms')
2394        self.DrawAtomEdit.Append(id=wxID_DRAWDELETE, kind=wx.ITEM_NORMAL,text='Delete atoms',
2395            help='Delete atoms from drawing set')
2396        self.DrawAtomCompute.Append(id=wxID_DRAWDISTVP, kind=wx.ITEM_NORMAL,text='View pt. dist.',
2397            help='Compute distance of selected atoms from view point')   
2398        self.DrawAtomCompute.Append(id=wxID_DRAWDISAGLTOR, kind=wx.ITEM_NORMAL,text='Dist. Ang. Tors.',
2399            help='Compute distance, angle or torsion for 2-4 selected atoms')   
2400        self.DrawAtomCompute.Append(id=wxID_DRAWPLANE, kind=wx.ITEM_NORMAL,text='Best plane',
2401            help='Compute best plane for 4+ selected atoms')   
2402        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRBOND, kind=wx.ITEM_NORMAL,text='Add bond restraint',
2403            help='Add bond restraint for selected atoms (2)')
2404        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRANGLE, kind=wx.ITEM_NORMAL,text='Add angle restraint',
2405            help='Add angle restraint for selected atoms (3: one end 1st)')
2406        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRPLANE, kind=wx.ITEM_NORMAL,text='Add plane restraint',
2407            help='Add plane restraint for selected atoms (4+)')
2408        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRCHIRAL, kind=wx.ITEM_NORMAL,text='Add chiral restraint',
2409            help='Add chiral restraint for selected atoms (4: center atom 1st)')
2410        self.DrawAtomRigidBody.Append(id=wxID_DRAWDEFINERB, kind=wx.ITEM_NORMAL,text='Define rigid body',
2411            help='Define rigid body with selected atoms')
2412        self.PostfillDataMenu()
2413
2414        # Phase / MCSA tab
2415        self.MCSAMenu = wx.MenuBar()
2416        self.PrefillDataMenu(self.MCSAMenu)
2417        self.MCSAMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2418        self.MCSAEdit = wx.Menu(title='')
2419        self.MCSAMenu.Append(menu=self.MCSAEdit, title='MC/SA')
2420        self.MCSAEdit.Append(id=wxID_ADDMCSAATOM, kind=wx.ITEM_NORMAL,text='Add atom', 
2421            help='Add single atom to MC/SA model')
2422        self.MCSAEdit.Append(id=wxID_ADDMCSARB, kind=wx.ITEM_NORMAL,text='Add rigid body', 
2423            help='Add rigid body to MC/SA model' )
2424        self.MCSAEdit.Append(id=wxID_CLEARMCSARB, kind=wx.ITEM_NORMAL,text='Clear rigid bodies', 
2425            help='Clear all atoms & rigid bodies from MC/SA model' )
2426        self.MCSAEdit.Append(id=wxID_MOVEMCSA, kind=wx.ITEM_NORMAL,text='Move MC/SA solution', 
2427            help='Move MC/SA solution to atom list' )
2428        self.MCSAEdit.Append(id=wxID_MCSACLEARRESULTS, kind=wx.ITEM_NORMAL,text='Clear results', 
2429            help='Clear table of MC/SA results' )
2430        self.PostfillDataMenu()
2431           
2432        # Phase / Texture tab
2433        self.TextureMenu = wx.MenuBar()
2434        self.PrefillDataMenu(self.TextureMenu)
2435        self.TextureMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2436        self.TextureEdit = wx.Menu(title='')
2437        self.TextureMenu.Append(menu=self.TextureEdit, title='Texture')
2438        self.TextureEdit.Append(id=wxID_REFINETEXTURE, kind=wx.ITEM_NORMAL,text='Refine texture', 
2439            help='Refine the texture coefficients from sequential results')
2440#        self.TextureEdit.Append(id=wxID_CLEARTEXTURE, kind=wx.ITEM_NORMAL,text='Clear texture',
2441#            help='Clear the texture coefficients' )
2442        self.PostfillDataMenu()
2443           
2444        # Phase / Pawley tab
2445        self.PawleyMenu = wx.MenuBar()
2446        self.PrefillDataMenu(self.PawleyMenu)
2447        self.PawleyMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2448        self.PawleyEdit = wx.Menu(title='')
2449        self.PawleyMenu.Append(menu=self.PawleyEdit,title='Operations')
2450        self.PawleyEdit.Append(id=wxID_PAWLEYSET, kind=wx.ITEM_NORMAL,text='Pawley settings',
2451            help='Change Pawley refinement settings')
2452        self.PawleyEdit.Append(id=wxID_PAWLEYLOAD, kind=wx.ITEM_NORMAL,text='Pawley create',
2453            help='Initialize Pawley reflection list')
2454        self.PawleyEdit.Append(id=wxID_PAWLEYESTIMATE, kind=wx.ITEM_NORMAL,text='Pawley estimate',
2455            help='Estimate initial Pawley intensities')
2456        self.PawleyEdit.Append(id=wxID_PAWLEYUPDATE, kind=wx.ITEM_NORMAL,text='Pawley update',
2457            help='Update negative Pawley intensities with -0.5*Fobs and turn off refinement')
2458        self.PawleyEdit.Append(id=wxID_PAWLEYSELALL, kind=wx.ITEM_NORMAL,text='Select all',
2459            help='Select all reflections to be refined')
2460        self.PawleyEdit.Append(id=wxID_PAWLEYSELNONE, kind=wx.ITEM_NORMAL,text='Select none',
2461            help='Set flag for all reflections for no refinement')
2462        self.PawleyEdit.Append(id=wxID_PAWLEYSELTOGGLE, kind=wx.ITEM_NORMAL,text='Toggle Selection',
2463            help='Toggle Selection flag for all reflections to opposite setting')
2464        self.PostfillDataMenu()
2465           
2466        # Phase / Map peaks tab
2467        self.MapPeaksMenu = wx.MenuBar()
2468        self.PrefillDataMenu(self.MapPeaksMenu)
2469        self.MapPeaksMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2470        self.MapPeaksEdit = wx.Menu(title='')
2471        self.MapPeaksMenu.Append(menu=self.MapPeaksEdit, title='Map peaks')
2472        self.MapPeaksEdit.Append(id=wxID_PEAKSMOVE, kind=wx.ITEM_NORMAL,text='Move peaks', 
2473            help='Move selected peaks to atom list')
2474        self.MapPeaksEdit.Append(id=wxID_PEAKSVIEWPT, kind=wx.ITEM_NORMAL,text='View point',
2475            help='View point is 1st peak selected')
2476        self.MapPeaksEdit.Append(id=wxID_PEAKSDISTVP, kind=wx.ITEM_NORMAL,text='View pt. dist.',
2477            help='Compute distance of selected peaks from view point')   
2478        self.MapPeaksEdit.Append(id=wxID_SHOWBONDS, kind=wx.ITEM_NORMAL,text='Hide bonds',
2479            help='Hide or show bonds between peak positions')   
2480        self.MapPeaksEdit.Append(id=wxID_PEAKSDA, kind=wx.ITEM_NORMAL,text='Calc dist/ang', 
2481            help='Calculate distance or angle for selection')
2482        self.MapPeaksEdit.Append(id=wxID_FINDEQVPEAKS, kind=wx.ITEM_NORMAL,text='Equivalent peaks', 
2483            help='Find equivalent peaks')
2484        self.MapPeaksEdit.Append(id=wxID_PEAKSUNIQUE, kind=wx.ITEM_NORMAL,text='Unique peaks', 
2485            help='Select unique set')
2486        self.MapPeaksEdit.Append(id=wxID_PEAKSDELETE, kind=wx.ITEM_NORMAL,text='Delete peaks', 
2487            help='Delete selected peaks')
2488        self.MapPeaksEdit.Append(id=wxID_PEAKSCLEAR, kind=wx.ITEM_NORMAL,text='Clear peaks', 
2489            help='Clear the map peak list')
2490        self.PostfillDataMenu()
2491
2492        # Phase / Rigid bodies tab
2493        self.RigidBodiesMenu = wx.MenuBar()
2494        self.PrefillDataMenu(self.RigidBodiesMenu)
2495        self.RigidBodiesMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2496        self.RigidBodiesEdit = wx.Menu(title='')
2497        self.RigidBodiesMenu.Append(menu=self.RigidBodiesEdit, title='Edit Body')
2498        self.RigidBodiesEdit.Append(id=wxID_ASSIGNATMS2RB, kind=wx.ITEM_NORMAL,text='Assign atoms to rigid body',
2499            help='Select & position rigid body in structure of existing atoms')
2500        self.RigidBodiesEdit.Append(id=wxID_AUTOFINDRESRB, kind=wx.ITEM_NORMAL,text='Auto find residues',
2501            help='Auto find of residue RBs in macromolecule')
2502        self.RigidBodiesEdit.Append(id=wxID_COPYRBPARMS, kind=wx.ITEM_NORMAL,text='Copy rigid body parms',
2503            help='Copy rigid body location & TLS parameters')
2504        self.RigidBodiesEdit.Append(id=wxID_GLOBALTHERM, kind=wx.ITEM_NORMAL,text='Global thermal motion',
2505            help='Global setting of residue thermal motion models')
2506        self.RigidBodiesEdit.Append(id=wxID_GLOBALRESREFINE, kind=wx.ITEM_NORMAL,text='Global residue refine',
2507            help='Global setting of residue RB refinement flags')
2508        self.RigidBodiesEdit.Append(id=wxID_RBREMOVEALL, kind=wx.ITEM_NORMAL,text='Remove all rigid bodies',
2509            help='Remove all rigid body assignment for atoms')
2510        self.PostfillDataMenu()
2511    # end of GSAS-II menu definitions
2512       
2513    def _init_ctrls(self, parent,name=None,size=None,pos=None):
2514        wx.Frame.__init__(
2515            self,parent=parent,
2516            #style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX | wx.FRAME_FLOAT_ON_PARENT ,
2517            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX,
2518            size=size,pos=pos,title='GSAS-II data display')
2519        self._init_menus()
2520        if name:
2521            self.SetLabel(name)
2522        self.Show()
2523       
2524    def __init__(self,parent,frame,data=None,name=None, size=None,pos=None):
2525        self.G2frame = frame
2526        self._init_ctrls(parent,name,size,pos)
2527        self.data = data
2528        clientSize = wx.ClientDisplayRect()
2529        Size = self.GetSize()
2530        xPos = clientSize[2]-Size[0]
2531        self.SetPosition(wx.Point(xPos,clientSize[1]+250))
2532        self.AtomGrid = []
2533        self.selectedRow = 0
2534        self.lastSize = Size       
2535        self.manualPhaseSize = None
2536        self.userReSize = False
2537        wx.Frame.Bind(self,wx.EVT_SIZE,self.OnReSize)
2538           
2539    def OnReSize(self,event):
2540        '''Keep track of size changes for Phase windows
2541        '''
2542        id = self.G2frame.PatternTree.GetSelection()
2543        try:
2544            parent = self.G2frame.PatternTree.GetItemParent(id)
2545        except:         #avoid bad tree item on start via gpx file selection
2546            parent = 0
2547        if self.userReSize and parent and self.G2frame.PatternTree.GetItemText(parent) == "Phases": 
2548            if self.lastSize == event.EventObject.GetSize():
2549#                if GSASIIpath.GetConfigValue('debug'):
2550#                    print 'no save size=',self.lastSize
2551                return
2552            self.manualPhaseSize = event.EventObject.GetSize()
2553            self.lastSize = event.EventObject.GetSize()
2554#            if GSASIIpath.GetConfigValue('debug'):
2555#                print 'Saving Phase size=',self.manualPhaseSize
2556                #HowDidIgetHere()
2557        event.Skip()
2558
2559    def SendSizeEvent(self):
2560        '''Prevent SendSizeEvent from overriding the saved size
2561        '''
2562        self.userReSize = False
2563        wx.Frame.SendSizeEvent(self)
2564        self.userReSize = True
2565       
2566    def setSizePosLeft(self,Size):
2567        '''Place the dataFrame window so that the upper left-hand corner remains in the same place;
2568        The size is dictated by parameter Width, unless overridden by a previous Phase window resize
2569        '''
2570        self.userReSize = False
2571        Size = list(Size)
2572        id = self.G2frame.PatternTree.GetSelection()
2573        try:            #avoid bad tree item on start via gpx file selection
2574            pid = self.G2frame.PatternTree.GetItemParent(id)
2575        except:
2576            pid = 0
2577        if pid:
2578            parent = self.G2frame.PatternTree.GetItemText(pid)
2579            # is this a phase window and has a previous window has been resized?
2580            if self.manualPhaseSize and parent == "Phases":
2581                Size = list(self.manualPhaseSize)
2582        Pos = self.GetPosition()
2583        clientSize = wx.ClientDisplayRect()     #display window size (e.g. 1304x768)
2584        Size[1] = min(Size[1],clientSize[2]-300)
2585        Size[0] = max(Size[0],300)
2586#        print 'current position/width:',Pos,Width
2587        self.SetSize(Size)
2588        Size[1] += 1        #kluge to ensure scrollbar settings & window properly displayed
2589        self.SetSize(Size)
2590        Pos[0] += self.lastSize[0]-Size[0]
2591        offSet = 0
2592        if Pos[0] < clientSize[2]:
2593            offSet = Pos[0]+Size[0]-clientSize[2]
2594        if offSet > 0:
2595            Pos[0] -= offSet
2596        self.SetPosition(wx.Point(Pos[0],Pos[1]))
2597        self.lastSize = Size
2598        self.userReSize = True
2599       
2600    def Clear(self):
2601        self.ClearBackground()
2602        self.DestroyChildren()
2603                   
2604
2605################################################################################
2606#####  Notebook Tree Item editor
2607################################################################################                 
2608def UpdateNotebook(G2frame,data):
2609    '''Called when the data tree notebook entry is selected. Allows for
2610    editing of the text in that tree entry
2611    '''
2612    def OnNoteBook(event):
2613        event.Skip()
2614        data = G2frame.dataDisplay.GetValue().split('\n')
2615        G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,G2frame.root,'Notebook'),data)
2616        if 'nt' not in os.name:
2617            G2frame.dataDisplay.AppendText('\n')
2618                   
2619    if G2frame.dataDisplay:
2620        G2frame.dataDisplay.Destroy()
2621    G2frame.dataFrame.SetLabel('Notebook')
2622    G2frame.dataDisplay = wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
2623        style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER | wx.TE_DONTWRAP)
2624    G2frame.dataDisplay.Bind(wx.EVT_TEXT_ENTER,OnNoteBook)
2625    G2frame.dataDisplay.Bind(wx.EVT_KILL_FOCUS,OnNoteBook)
2626    for line in data:
2627        G2frame.dataDisplay.AppendText(line+"\n")
2628    G2frame.dataDisplay.AppendText('Notebook entry @ '+time.ctime()+"\n")
2629    G2frame.dataFrame.setSizePosLeft([400,250])
2630           
2631################################################################################
2632#####  Controls Tree Item editor
2633################################################################################           
2634def UpdateControls(G2frame,data):
2635    '''Edit overall GSAS-II controls in main Controls data tree entry
2636    '''
2637    #patch
2638    if 'deriv type' not in data:
2639        data = {}
2640        data['deriv type'] = 'analytic Hessian'
2641        data['min dM/M'] = 0.0001
2642        data['shift factor'] = 1.
2643        data['max cyc'] = 3       
2644        data['F**2'] = False
2645    if 'shift factor' not in data:
2646        data['shift factor'] = 1.
2647    if 'max cyc' not in data:
2648        data['max cyc'] = 3
2649    if 'F**2' not in data:
2650        data['F**2'] = False
2651    if 'Author' not in data:
2652        data['Author'] = 'no name'
2653    if 'FreePrm1' not in data:
2654        data['FreePrm1'] = 'Sample humidity (%)'
2655    if 'FreePrm2' not in data:
2656        data['FreePrm2'] = 'Sample voltage (V)'
2657    if 'FreePrm3' not in data:
2658        data['FreePrm3'] = 'Applied load (MN)'
2659    if 'Copy2Next' not in data:
2660        data['Copy2Next'] = False
2661    if 'Reverse Seq' not in data:
2662        data['Reverse Seq'] = False
2663    if 'UsrReject' not in data:
2664        data['UsrReject'] = {'minF/sig':0,'MinExt':0.01,'MaxDF/F':20.,'MaxD':500.,'MinD':0.05}
2665    if 'HatomFix' not in data:
2666        data['HatomFix'] = False
2667    if 'Marquardt' not in data:
2668        data['Marquardt'] = -3
2669   
2670    #end patch
2671
2672    def SeqSizer():
2673       
2674        def OnSelectData(event):
2675            choices = GetPatternTreeDataNames(G2frame,['PWDR','HKLF',])
2676            sel = []
2677            try:
2678                if 'Seq Data' in data:
2679                    for item in data['Seq Data']:
2680                        sel.append(choices.index(item))
2681                    sel = [choices.index(item) for item in data['Seq Data']]
2682            except ValueError:  #data changed somehow - start fresh
2683                sel = []
2684            dlg = G2G.G2MultiChoiceDialog(G2frame.dataFrame, 'Sequential refinement',
2685                'Select dataset to include',choices)
2686            dlg.SetSelections(sel)
2687            names = []
2688            if dlg.ShowModal() == wx.ID_OK:
2689                for sel in dlg.GetSelections():
2690                    names.append(choices[sel])
2691                data['Seq Data'] = names               
2692                G2frame.EnableSeqRefineMenu()
2693            dlg.Destroy()
2694            wx.CallAfter(UpdateControls,G2frame,data)
2695           
2696        def OnReverse(event):
2697            data['Reverse Seq'] = reverseSel.GetValue()
2698           
2699        def OnCopySel(event):
2700            data['Copy2Next'] = copySel.GetValue() 
2701                   
2702        seqSizer = wx.BoxSizer(wx.VERTICAL)
2703        dataSizer = wx.BoxSizer(wx.HORIZONTAL)
2704        dataSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Sequential Refinement: '),0,WACV)
2705        selSeqData = wx.Button(G2frame.dataDisplay,-1,label=' Select data')
2706        selSeqData.Bind(wx.EVT_BUTTON,OnSelectData)
2707        dataSizer.Add(selSeqData,0,WACV)
2708        SeqData = data.get('Seq Data',[])
2709        if not SeqData:
2710            lbl = ' (no data selected)'
2711        else:
2712            lbl = ' ('+str(len(SeqData))+' dataset(s) selected)'
2713
2714        dataSizer.Add(wx.StaticText(G2frame.dataDisplay,label=lbl),0,WACV)
2715        seqSizer.Add(dataSizer,0)
2716        if SeqData:
2717            selSizer = wx.BoxSizer(wx.HORIZONTAL)
2718            reverseSel = wx.CheckBox(G2frame.dataDisplay,-1,label=' Reverse order?')
2719            reverseSel.Bind(wx.EVT_CHECKBOX,OnReverse)
2720            reverseSel.SetValue(data['Reverse Seq'])
2721            selSizer.Add(reverseSel,0,WACV)
2722            copySel =  wx.CheckBox(G2frame.dataDisplay,-1,label=' Copy results to next histogram?')
2723            copySel.Bind(wx.EVT_CHECKBOX,OnCopySel)
2724            copySel.SetValue(data['Copy2Next'])
2725            selSizer.Add(copySel,0,WACV)
2726            seqSizer.Add(selSizer,0)
2727        return seqSizer
2728       
2729    def LSSizer():       
2730       
2731        def OnDerivType(event):
2732            data['deriv type'] = derivSel.GetValue()
2733            derivSel.SetValue(data['deriv type'])
2734            wx.CallAfter(UpdateControls,G2frame,data)
2735           
2736        def OnConvergence(event):
2737            event.Skip()
2738            try:
2739                value = max(1.e-9,min(1.0,float(Cnvrg.GetValue())))
2740            except ValueError:
2741                value = 0.0001
2742            data['min dM/M'] = value
2743            Cnvrg.SetValue('%.2g'%(value))
2744           
2745        def OnMaxCycles(event):
2746            data['max cyc'] = int(maxCyc.GetValue())
2747            maxCyc.SetValue(str(data['max cyc']))
2748           
2749        def OnMarqLam(event):
2750            data['Marquardt'] = int(marqLam.GetValue())
2751            marqLam.SetValue(str(data['Marquardt']))
2752                       
2753        def OnFactor(event):
2754            event.Skip()
2755            try:
2756                value = min(max(float(Factr.GetValue()),0.00001),100.)
2757            except ValueError:
2758                value = 1.0
2759            data['shift factor'] = value
2760            Factr.SetValue('%.5f'%(value))
2761           
2762        def OnFsqRef(event):
2763            data['F**2'] = fsqRef.GetValue()
2764           
2765#        def OnHatomFix(event):
2766#            data['HatomFix'] = Hfix.GetValue()
2767       
2768        def OnUsrRej(event):
2769            event.Skip()
2770            Obj = event.GetEventObject()
2771            item,limits = Indx[Obj]
2772            try:
2773                value = min(max(float(Obj.GetValue()),limits[0]),limits[1])
2774            except ValueError:
2775                value = data['UsrReject'][item]
2776            data['UsrReject'][item] = value
2777            Obj.SetValue('%.2f'%(value))
2778
2779        LSSizer = wx.FlexGridSizer(cols=4,vgap=5,hgap=5)
2780        LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Refinement derivatives: '),0,WACV)
2781        Choice=['analytic Jacobian','numeric','analytic Hessian']
2782        derivSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['deriv type'],choices=Choice,
2783            style=wx.CB_READONLY|wx.CB_DROPDOWN)
2784        derivSel.SetValue(data['deriv type'])
2785        derivSel.Bind(wx.EVT_COMBOBOX, OnDerivType)
2786           
2787        LSSizer.Add(derivSel,0,WACV)
2788        LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Min delta-M/M: '),0,WACV)
2789        Cnvrg = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2g'%(data['min dM/M']),style=wx.TE_PROCESS_ENTER)
2790        Cnvrg.Bind(wx.EVT_TEXT_ENTER,OnConvergence)
2791        Cnvrg.Bind(wx.EVT_KILL_FOCUS,OnConvergence)
2792        LSSizer.Add(Cnvrg,0,WACV)
2793        Indx = {}
2794        if 'Hessian' in data['deriv type']:
2795            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Max cycles: '),0,WACV)
2796            Choice = ['0','1','2','3','5','10','15','20']
2797            maxCyc = wx.ComboBox(parent=G2frame.dataDisplay,value=str(data['max cyc']),choices=Choice,
2798                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2799#            maxCyc.SetValue(str(data['max cyc']))
2800            maxCyc.Bind(wx.EVT_COMBOBOX, OnMaxCycles)
2801            LSSizer.Add(maxCyc,0,WACV)
2802            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Initial lambda = 10**'),0,WACV)
2803            MarqChoice = ['-3','-2','-1','0','1','2','3','4']
2804            marqLam = wx.ComboBox(parent=G2frame.dataDisplay,value=str(data['Marquardt']),choices=MarqChoice,
2805                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2806            marqLam.Bind(wx.EVT_COMBOBOX,OnMarqLam)
2807            LSSizer.Add(marqLam,0,WACV)
2808        else:
2809            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Initial shift factor: '),0,WACV)
2810            Factr = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.5f'%(data['shift factor']),style=wx.TE_PROCESS_ENTER)
2811            Factr.Bind(wx.EVT_TEXT_ENTER,OnFactor)
2812            Factr.Bind(wx.EVT_KILL_FOCUS,OnFactor)
2813            LSSizer.Add(Factr,0,WACV)
2814        if G2frame.Sngl:
2815            userReject = data['UsrReject']
2816            usrRej = {'minF/sig':[' Min obs/sig (0-5): ',[0,5], ],'MinExt':[' Min extinct. (0-.9): ',[0,.9],],
2817                'MaxDF/F':[' Max delt-F/sig (3-1000): ',[3.,1000.],],'MaxD':[' Max d-spacing (3-500): ',[3,500],],
2818                'MinD':[' Min d-spacing (0.1-2.0): ',[0.1,2.0],]}
2819
2820            fsqRef = wx.CheckBox(G2frame.dataDisplay,-1,label='Refine HKLF as F^2? ')
2821            fsqRef.SetValue(data['F**2'])
2822            fsqRef.Bind(wx.EVT_CHECKBOX,OnFsqRef)
2823            LSSizer.Add(fsqRef,0,WACV)
2824            LSSizer.Add((1,0),)
2825            for item in usrRej:
2826                LSSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=usrRej[item][0]),0,WACV)
2827                usrrej = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2f'%(userReject[item]),style=wx.TE_PROCESS_ENTER)
2828                Indx[usrrej] = [item,usrRej[item][1]]
2829                usrrej.Bind(wx.EVT_TEXT_ENTER,OnUsrRej)
2830                usrrej.Bind(wx.EVT_KILL_FOCUS,OnUsrRej)
2831                LSSizer.Add(usrrej,0,WACV)
2832#        Hfix = wx.CheckBox(G2frame.dataDisplay,-1,label='Regularize H atoms? ')
2833#        Hfix.SetValue(data['HatomFix'])
2834#        Hfix.Bind(wx.EVT_CHECKBOX,OnHatomFix)
2835#        LSSizer.Add(Hfix,0,WACV)   #for now
2836        return LSSizer
2837       
2838    def AuthSizer():
2839
2840        def OnAuthor(event):
2841            event.Skip()
2842            data['Author'] = auth.GetValue()
2843
2844        Author = data['Author']
2845        authSizer = wx.BoxSizer(wx.HORIZONTAL)
2846        authSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' CIF Author (last, first):'),0,WACV)
2847        auth = wx.TextCtrl(G2frame.dataDisplay,-1,value=Author,style=wx.TE_PROCESS_ENTER)
2848        auth.Bind(wx.EVT_TEXT_ENTER,OnAuthor)
2849        auth.Bind(wx.EVT_KILL_FOCUS,OnAuthor)
2850        authSizer.Add(auth,0,WACV)
2851        return authSizer
2852       
2853       
2854    if G2frame.dataDisplay:
2855        G2frame.dataDisplay.Destroy()
2856    if not G2frame.dataFrame.GetStatusBar():
2857        Status = G2frame.dataFrame.CreateStatusBar()
2858        Status.SetStatusText('')
2859    G2frame.dataFrame.SetLabel('Controls')
2860    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
2861    SetDataMenuBar(G2frame,G2frame.dataFrame.ControlsMenu)
2862    mainSizer = wx.BoxSizer(wx.VERTICAL)
2863    mainSizer.Add((5,5),0)
2864    mainSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Refinement Controls:'),0,WACV)   
2865    mainSizer.Add(LSSizer())
2866    mainSizer.Add((5,5),0)
2867    mainSizer.Add(SeqSizer())
2868    mainSizer.Add((5,5),0)
2869    mainSizer.Add(AuthSizer())
2870    mainSizer.Add((5,5),0)
2871       
2872    mainSizer.Layout()   
2873    G2frame.dataDisplay.SetSizer(mainSizer)
2874    G2frame.dataFrame.setSizePosLeft(mainSizer.Fit(G2frame.dataFrame))
2875     
2876################################################################################
2877#####  Comments
2878################################################################################           
2879       
2880def UpdateComments(G2frame,data):                   
2881
2882    if G2frame.dataDisplay:
2883        G2frame.dataDisplay.Destroy()
2884    G2frame.dataFrame.SetLabel('Comments')
2885    G2frame.dataDisplay = wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
2886        style=wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_DONTWRAP)
2887    for line in data:
2888        if '\n' not in line:
2889            G2frame.dataDisplay.AppendText(line+'\n')
2890        else:
2891            G2frame.dataDisplay.AppendText(line)
2892    G2frame.dataFrame.setSizePosLeft([400,250])
2893           
2894################################################################################
2895#####  Display of Sequential Results
2896################################################################################           
2897       
2898def UpdateSeqResults(G2frame,data,prevSize=None):
2899    """
2900    Called when the Sequential Results data tree entry is selected
2901    to show results from a sequential refinement.
2902   
2903    :param wx.Frame G2frame: main GSAS-II data tree windows
2904
2905    :param dict data: a dictionary containing the following items: 
2906
2907            * 'histNames' - list of histogram names in order as processed by Sequential Refinement
2908            * 'varyList' - list of variables - identical over all refinements in sequence
2909              note that this is the original list of variables, prior to processing
2910              constraints.
2911            * 'variableLabels' -- a dict of labels to be applied to each parameter
2912              (this is created as an empty dict if not present in data).
2913            * keyed by histName - dictionaries for all data sets processed, which contains:
2914
2915              * 'variables'- result[0] from leastsq call
2916              * 'varyList' - list of variables passed to leastsq call (not same as above)
2917              * 'sig' - esds for variables
2918              * 'covMatrix' - covariance matrix from individual refinement
2919              * 'title' - histogram name; same as dict item name
2920              * 'newAtomDict' - new atom parameters after shifts applied
2921              * 'newCellDict' - refined cell parameters after shifts to A0-A5 from Dij terms applied'
2922    """
2923
2924    def GetSampleParms():
2925        '''Make a dictionary of the sample parameters are not the same over the
2926        refinement series.
2927        '''
2928        if 'IMG' in histNames[0]:
2929            sampleParmDict = {'Sample load':[],}
2930        else:
2931            sampleParmDict = {'Temperature':[],'Pressure':[],'Time':[],
2932                'FreePrm1':[],'FreePrm2':[],'FreePrm3':[],'Omega':[],
2933                'Chi':[],'Phi':[],'Azimuth':[],}
2934        Controls = G2frame.PatternTree.GetItemPyData(
2935            GetPatternTreeItemId(G2frame,G2frame.root, 'Controls'))
2936        sampleParm = {}
2937        for name in histNames:
2938            if 'IMG' in name:
2939                for item in sampleParmDict:
2940                    sampleParmDict[item].append(data[name]['parmDict'].get(item,0))
2941            else:
2942                Id = GetPatternTreeItemId(G2frame,G2frame.root,name)
2943                sampleData = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,Id,'Sample Parameters'))
2944                for item in sampleParmDict:
2945                    sampleParmDict[item].append(sampleData.get(item,0))
2946        for item in sampleParmDict:
2947            frstValue = sampleParmDict[item][0]
2948            if np.any(np.array(sampleParmDict[item])-frstValue):
2949                if item.startswith('FreePrm'):
2950                    sampleParm[Controls[item]] = sampleParmDict[item]
2951                else:
2952                    sampleParm[item] = sampleParmDict[item]
2953        return sampleParm
2954
2955    def GetColumnInfo(col):
2956        '''returns column label, lists of values and errors (or None) for each column in the table
2957        for plotting. The column label is reformatted from Unicode to MatPlotLib encoding
2958        '''
2959        colName = G2frame.SeqTable.GetColLabelValue(col)
2960        plotName = variableLabels.get(colName,colName)
2961        plotName = plotSpCharFix(plotName)
2962        return plotName,colList[col],colSigs[col]
2963           
2964    def PlotSelect(event):
2965        'Plots a row (covariance) or column on double-click'
2966        cols = G2frame.dataDisplay.GetSelectedCols()
2967        rows = G2frame.dataDisplay.GetSelectedRows()
2968        if cols:
2969            G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
2970        elif rows:
2971            name = histNames[rows[0]]       #only does 1st one selected
2972            G2plt.PlotCovariance(G2frame,data[name])
2973        else:
2974            G2frame.ErrorDialog(
2975                'Select row or columns',
2976                'Nothing selected in table. Click on column or row label(s) to plot. N.B. Grid selection can be a bit funky.'
2977                )
2978           
2979    def OnPlotSelSeq(event):
2980        'plot the selected columns or row from menu command'
2981        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
2982        rows = G2frame.dataDisplay.GetSelectedRows()
2983        if cols:
2984            G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
2985        elif rows:
2986            name = histNames[rows[0]]       #only does 1st one selected
2987            G2plt.PlotCovariance(G2frame,data[name])
2988        else:
2989            G2frame.ErrorDialog(
2990                'Select columns',
2991                'No columns or rows selected in table. Click on row or column labels to select fields for plotting.'
2992                )
2993               
2994    def OnAveSelSeq(event):
2995        'average the selected columns from menu command'
2996        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
2997        if cols:
2998            for col in cols:
2999                ave = np.mean(GetColumnInfo(col)[1])
3000                sig = np.std(GetColumnInfo(col)[1])
3001                print ' Average for '+G2frame.SeqTable.GetColLabelValue(col)+': '+'%.6g'%(ave)+' +/- '+'%.6g'%(sig)
3002        else:
3003            G2frame.ErrorDialog(
3004                'Select columns',
3005                'No columns selected in table. Click on column labels to select fields for averaging.'
3006                )
3007               
3008    def OnRenameSelSeq(event):
3009        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
3010        colNames = [G2frame.SeqTable.GetColLabelValue(c) for c in cols]
3011        newNames = colNames[:]
3012        for i,name in enumerate(colNames):
3013            if name in variableLabels:
3014                newNames[i] = variableLabels[name]
3015        if not cols:
3016            G2frame.ErrorDialog('Select columns',
3017                'No columns selected in table. Click on column labels to select fields for rename.')
3018            return
3019        dlg = G2G.MultiStringDialog(G2frame.dataDisplay,'Set column names',colNames,newNames)
3020        if dlg.Show():
3021            newNames = dlg.GetValues()           
3022            variableLabels.update(dict(zip(colNames,newNames)))
3023        data['variableLabels'] = variableLabels
3024        dlg.Destroy()
3025        UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3026        G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
3027           
3028    def OnReOrgSelSeq(event):
3029        'Reorder the columns'
3030        G2G.GetItemOrder(G2frame,VaryListChanges,vallookup,posdict)   
3031        UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3032
3033    def OnSaveSelSeqCSV(event):
3034        'export the selected columns to a .csv file from menu command'
3035        OnSaveSelSeq(event,csv=True)
3036       
3037    def OnSaveSeqCSV(event):
3038        'export all columns to a .csv file from menu command'
3039        OnSaveSelSeq(event,csv=True,allcols=True)
3040       
3041    def OnSaveSelSeq(event,csv=False,allcols=False):
3042        'export the selected columns to a .txt or .csv file from menu command'
3043        def WriteCSV():
3044            def WriteList(headerItems):
3045                line = ''
3046                for lbl in headerItems:
3047                    if line: line += ','
3048                    line += '"'+lbl+'"'
3049                return line
3050            head = ['name']
3051            for col in cols:
3052                item = G2frame.SeqTable.GetColLabelValue(col)
3053                # get rid of labels that have Unicode characters
3054                if not all([ord(c) < 128 and ord(c) != 0 for c in item]): item = '?'
3055                if col in havesig:
3056                    head += [item,'esd-'+item]
3057                else:
3058                    head += [item]
3059            SeqFile.write(WriteList(head)+'\n')
3060            for row,name in enumerate(saveNames):
3061                line = '"'+saveNames[row]+'"'
3062                for col in cols:
3063                    if col in havesig:
3064                        line += ','+str(saveData[col][row])+','+str(saveSigs[col][row])
3065                    else:
3066                        line += ','+str(saveData[col][row])
3067                SeqFile.write(line+'\n')
3068        def WriteSeq():
3069            lenName = len(saveNames[0])
3070            line = %s  '%('name'.center(lenName))
3071            for col in cols:
3072                item = G2frame.SeqTable.GetColLabelValue(col)
3073                if col in havesig:
3074                    line += ' %12s %12s '%(item.center(12),'esd'.center(12))
3075                else:
3076                    line += ' %12s '%(item.center(12))
3077            SeqFile.write(line+'\n')
3078            for row,name in enumerate(saveNames):
3079                line = " '%s' "%(saveNames[row])
3080                for col in cols:
3081                    if col in havesig:
3082                        line += ' %12.6f %12.6f '%(saveData[col][row],saveSigs[col][row])
3083                    else:
3084                        line += ' %12.6f '%saveData[col][row]
3085                SeqFile.write(line+'\n')
3086
3087        # start of OnSaveSelSeq code
3088        if allcols:
3089            cols = range(G2frame.SeqTable.GetNumberCols())
3090        else:
3091            cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
3092        nrows = G2frame.SeqTable.GetNumberRows()
3093        if not cols:
3094            G2frame.ErrorDialog('Select columns',
3095                             'No columns selected in table. Click on column labels to select fields for output.')
3096            return
3097        saveNames = [G2frame.SeqTable.GetRowLabelValue(r) for r in range(nrows)]
3098        saveData = {}
3099        saveSigs = {}
3100        havesig = []
3101        for col in cols:
3102            name,vals,sigs = GetColumnInfo(col)
3103            saveData[col] = vals
3104            if sigs:
3105                havesig.append(col)
3106                saveSigs[col] = sigs
3107        if csv:
3108            wild = 'CSV output file (*.csv)|*.csv'
3109        else:
3110            wild = 'Text output file (*.txt)|*.txt'
3111        pth = G2G.GetExportPath(G2frame)
3112        dlg = wx.FileDialog(
3113            G2frame,
3114            'Choose text output file for your selection', pth, '', 
3115            wild,wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3116        try:
3117            if dlg.ShowModal() == wx.ID_OK:
3118                SeqTextFile = dlg.GetPath()
3119                SeqTextFile = G2IO.FileDlgFixExt(dlg,SeqTextFile) 
3120                SeqFile = open(SeqTextFile,'w')
3121                if csv:
3122                    WriteCSV()
3123                else:
3124                    WriteSeq()
3125                SeqFile.close()
3126        finally:
3127            dlg.Destroy()
3128               
3129    def striphist(var,insChar=''):
3130        'strip a histogram number from a var name'
3131        sv = var.split(':')
3132        if len(sv) <= 1: return var
3133        if sv[1]:
3134            sv[1] = insChar
3135        return ':'.join(sv)
3136       
3137    def plotSpCharFix(lbl):
3138        'Change selected unicode characters to their matplotlib equivalent'
3139        for u,p in [
3140            (u'\u03B1',r'$\alpha$'),
3141            (u'\u03B2',r'$\beta$'),
3142            (u'\u03B3',r'$\gamma$'),
3143            (u'\u0394\u03C7',r'$\Delta\chi$'),
3144            ]:
3145            lbl = lbl.replace(u,p)
3146        return lbl
3147   
3148    def SelectXaxis():
3149        'returns a selected column number (or None) as the X-axis selection'
3150        ncols = G2frame.SeqTable.GetNumberCols()
3151        colNames = [G2frame.SeqTable.GetColLabelValue(r) for r in range(ncols)]
3152        dlg = G2G.G2SingleChoiceDialog(
3153            G2frame.dataDisplay,
3154            'Select x-axis parameter for plot or Cancel for sequence number',
3155            'Select X-axis',
3156            colNames)
3157        try:
3158            if dlg.ShowModal() == wx.ID_OK:
3159                col = dlg.GetSelection()
3160            else:
3161                col = None
3162        finally:
3163            dlg.Destroy()
3164        return col
3165   
3166    def EnablePseudoVarMenus():
3167        'Enables or disables the PseudoVar menu items that require existing defs'
3168        if Controls['SeqPseudoVars']:
3169            val = True
3170        else:
3171            val = False
3172        G2frame.dataFrame.SequentialPvars.Enable(wxDELSEQVAR,val)
3173        G2frame.dataFrame.SequentialPvars.Enable(wxEDITSEQVAR,val)
3174
3175    def DelPseudoVar(event):
3176        'Ask the user to select a pseudo var expression to delete'
3177        choices = Controls['SeqPseudoVars'].keys()
3178        selected = G2G.ItemSelector(
3179            choices,G2frame.dataFrame,
3180            multiple=True,
3181            title='Select expressions to remove',
3182            header='Delete expression')
3183        if selected is None: return
3184        for item in selected:
3185            del Controls['SeqPseudoVars'][choices[item]]
3186        if selected:
3187            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3188
3189    def EditPseudoVar(event):
3190        'Edit an existing pseudo var expression'
3191        choices = Controls['SeqPseudoVars'].keys()
3192        if len(choices) == 1:
3193            selected = 0
3194        else:
3195            selected = G2G.ItemSelector(
3196                choices,G2frame.dataFrame,
3197                multiple=False,
3198                title='Select an expression to edit',
3199                header='Edit expression')
3200        if selected is not None:
3201            dlg = G2exG.ExpressionDialog(
3202                G2frame.dataDisplay,PSvarDict,
3203                Controls['SeqPseudoVars'][choices[selected]],
3204                header="Edit the PseudoVar expression",
3205                VarLabel="PseudoVar #"+str(selected+1),
3206                fit=False)
3207            newobj = dlg.Show(True)
3208            if newobj:
3209                calcobj = G2obj.ExpressionCalcObj(newobj)
3210                del Controls['SeqPseudoVars'][choices[selected]]
3211                Controls['SeqPseudoVars'][calcobj.eObj.expression] = newobj
3212                UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3213       
3214    def AddNewPseudoVar(event):
3215        'Create a new pseudo var expression'
3216        dlg = G2exG.ExpressionDialog(
3217            G2frame.dataDisplay,PSvarDict,
3218            header='Enter an expression for a PseudoVar here',
3219            VarLabel = "New PseudoVar",
3220            fit=False)
3221        obj = dlg.Show(True)
3222        dlg.Destroy()
3223        if obj:
3224            calcobj = G2obj.ExpressionCalcObj(obj)
3225            Controls['SeqPseudoVars'][calcobj.eObj.expression] = obj
3226            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3227           
3228    def AddNewDistPseudoVar(event):
3229        obj = None
3230        dlg = G2exG.BondDialog(
3231            G2frame.dataDisplay,Phases,PSvarDict,
3232            header='Select a Bond here',
3233            VarLabel = "New Bond")
3234        if dlg.ShowModal() == wx.ID_OK:
3235            pName,Oatom,Tatom = dlg.GetSelection()
3236            if Tatom:
3237                Phase = Phases[pName]
3238                General = Phase['General']
3239                cx,ct = General['AtomPtrs'][:2]
3240                pId = Phase['pId']
3241                SGData = General['SGData']
3242                sB = Tatom.find('(')+1
3243                symNo = 0
3244                if sB:
3245                    sF = Tatom.find(')')
3246                    symNo = int(Tatom[sB:sF])
3247                cellNo = [0,0,0]
3248                cB = Tatom.find('[')
3249                if cB>0:
3250                    cF = Tatom.find(']')+1
3251                    cellNo = eval(Tatom[cB:cF])
3252                Atoms = Phase['Atoms']
3253                aNames = [atom[ct-1] for atom in Atoms]
3254                oId = aNames.index(Oatom)
3255                tId = aNames.index(Tatom.split(' +')[0])
3256                # create an expression object
3257                obj = G2obj.ExpressionObj()
3258                obj.expression = 'Dist(%s,\n%s)'%(Oatom,Tatom.split(' d=')[0].replace(' ',''))
3259                obj.distance_dict = {'pId':pId,'SGData':SGData,'symNo':symNo,'cellNo':cellNo}
3260                obj.distance_atoms = [oId,tId]
3261        else: 
3262            dlg.Destroy()
3263            return
3264        dlg.Destroy()
3265        if obj:
3266            Controls['SeqPseudoVars'][obj.expression] = obj
3267            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3268
3269    def AddNewAnglePseudoVar(event):
3270        obj = None
3271        dlg = G2exG.AngleDialog(
3272            G2frame.dataDisplay,Phases,PSvarDict,
3273            header='Enter an Angle here',
3274            VarLabel = "New Angle")
3275        if dlg.ShowModal() == wx.ID_OK:
3276            pName,Oatom,Tatoms = dlg.GetSelection()
3277            if Tatoms:
3278                Phase = Phases[pName]
3279                General = Phase['General']
3280                cx,ct = General['AtomPtrs'][:2]
3281                pId = Phase['pId']
3282                SGData = General['SGData']
3283                Atoms = Phase['Atoms']
3284                aNames = [atom[ct-1] for atom in Atoms]
3285                tIds = []
3286                symNos = []
3287                cellNos = []
3288                oId = aNames.index(Oatom)
3289                Tatoms = Tatoms.split(';')
3290                for Tatom in Tatoms:
3291                    sB = Tatom.find('(')+1
3292                    symNo = 0
3293                    if sB:
3294                        sF = Tatom.find(')')
3295                        symNo = int(Tatom[sB:sF])
3296                    symNos.append(symNo)
3297                    cellNo = [0,0,0]
3298                    cB = Tatom.find('[')
3299                    if cB>0:
3300                        cF = Tatom.find(']')+1
3301                        cellNo = eval(Tatom[cB:cF])
3302                    cellNos.append(cellNo)
3303                    tIds.append(aNames.index(Tatom.split('+')[0]))
3304                # create an expression object
3305                obj = G2obj.ExpressionObj()
3306                obj.expression = 'Angle(%s,%s,\n%s)'%(Tatoms[0],Oatom,Tatoms[1])
3307                obj.angle_dict = {'pId':pId,'SGData':SGData,'symNo':symNos,'cellNo':cellNos}
3308                obj.angle_atoms = [oId,tIds]
3309        else: 
3310            dlg.Destroy()
3311            return
3312        dlg.Destroy()
3313        if obj:
3314            Controls['SeqPseudoVars'][obj.expression] = obj
3315            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3316           
3317    def UpdateParmDict(parmDict):
3318        '''generate the atom positions and the direct & reciprocal cell values,
3319        because they might be needed to evaluate the pseudovar
3320        '''
3321        Ddict = dict(zip(['D11','D22','D33','D12','D13','D23'],
3322                         ['A'+str(i) for i in range(6)])
3323                     )
3324        delList = []
3325        phaselist = []
3326        for item in parmDict: 
3327            if ':' not in item: continue
3328            key = item.split(':')
3329            if len(key) < 3: continue
3330            # remove the dA[xyz] terms, they would only bring confusion
3331            if key[2].startswith('dA'):
3332                delList.append(item)
3333            # compute and update the corrected reciprocal cell terms using the Dij values
3334            elif key[2] in Ddict:
3335                if key[0] not in phaselist: phaselist.append(key[0])
3336                akey = key[0]+'::'+Ddict[key[2]]
3337                parmDict[akey] -= parmDict[item]
3338                delList.append(item)
3339        for item in delList:
3340            del parmDict[item]               
3341        for i in phaselist:
3342            pId = int(i)
3343            # apply cell symmetry
3344            A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata[pId],parmDict,zeroDict[pId])
3345            # convert to direct cell & add the unique terms to the dictionary
3346            for i,val in enumerate(G2lat.A2cell(A)):
3347                if i in uniqCellIndx[pId]:
3348                    lbl = str(pId)+'::'+cellUlbl[i]
3349                    parmDict[lbl] = val
3350            lbl = str(pId)+'::'+'Vol'
3351            parmDict[lbl] = G2lat.calc_V(A)
3352        return parmDict
3353
3354    def EvalPSvarDeriv(calcobj,parmDict,sampleDict,var,ESD):
3355        '''Evaluate an expression derivative with respect to a
3356        GSAS-II variable name.
3357
3358        Note this likely could be faster if the loop over calcobjs were done
3359        inside after the Dict was created.
3360        '''
3361        step = ESD/10
3362        Ddict = dict(zip(['D11','D22','D33','D12','D13','D23'],
3363                         ['A'+str(i) for i in range(6)])
3364                     )
3365        results = []
3366        phaselist = []
3367        VparmDict = sampleDict.copy()
3368        for incr in step,-step:
3369            VparmDict.update(parmDict.copy())           
3370            # as saved, the parmDict has updated 'A[xyz]' values, but 'dA[xyz]'
3371            # values are not zeroed: fix that!
3372            VparmDict.update({item:0.0 for item in parmDict if 'dA' in item})
3373            VparmDict[var] += incr
3374            G2mv.Dict2Map(VparmDict,[]) # apply constraints
3375            # generate the atom positions and the direct & reciprocal cell values now, because they might
3376            # needed to evaluate the pseudovar
3377            for item in VparmDict:
3378                if item in sampleDict:
3379                    continue 
3380                if ':' not in item: continue
3381                key = item.split(':')
3382                if len(key) < 3: continue
3383                # apply any new shifts to atom positions
3384                if key[2].startswith('dA'):
3385                    VparmDict[''.join(item.split('d'))] += VparmDict[item]
3386                    VparmDict[item] = 0.0
3387                # compute and update the corrected reciprocal cell terms using the Dij values
3388                if key[2] in Ddict:
3389                    if key[0] not in phaselist: phaselist.append(key[0])
3390                    akey = key[0]+'::'+Ddict[key[2]]
3391                    VparmDict[akey] -= VparmDict[item]
3392            for i in phaselist:
3393                pId = int(i)
3394                # apply cell symmetry
3395                A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata[pId],VparmDict,zeroDict[pId])
3396                # convert to direct cell & add the unique terms to the dictionary
3397                for i,val in enumerate(G2lat.A2cell(A)):
3398                    if i in uniqCellIndx[pId]:
3399                        lbl = str(pId)+'::'+cellUlbl[i]
3400                        VparmDict[lbl] = val
3401                lbl = str(pId)+'::'+'Vol'
3402                VparmDict[lbl] = G2lat.calc_V(A)
3403            # dict should be fully updated, use it & calculate
3404            calcobj.SetupCalc(VparmDict)
3405            results.append(calcobj.EvalExpression())
3406        return (results[0] - results[1]) / (2.*step)
3407       
3408    def EnableParFitEqMenus():
3409        'Enables or disables the Parametric Fit menu items that require existing defs'
3410        if Controls['SeqParFitEqList']:
3411            val = True
3412        else:
3413            val = False
3414        G2frame.dataFrame.SequentialPfit.Enable(wxDELPARFIT,val)
3415        G2frame.dataFrame.SequentialPfit.Enable(wxEDITPARFIT,val)
3416        G2frame.dataFrame.SequentialPfit.Enable(wxDOPARFIT,val)
3417
3418    def ParEqEval(Values,calcObjList,varyList):
3419        '''Evaluate the parametric expression(s)
3420        :param list Values: a list of values for each variable parameter
3421        :param list calcObjList: a list of :class:`GSASIIobj.ExpressionCalcObj`
3422          expression objects to evaluate
3423        :param list varyList: a list of variable names for each value in Values
3424        '''
3425        result = []
3426        for calcobj in calcObjList:
3427            calcobj.UpdateVars(varyList,Values)
3428            result.append((calcobj.depVal-calcobj.EvalExpression())/calcobj.depSig)
3429        return result
3430
3431    def DoParEqFit(event,eqObj=None):
3432        'Parametric fit minimizer'
3433        varyValueDict = {} # dict of variables and their initial values
3434        calcObjList = [] # expression objects, ready to go for each data point
3435        if eqObj is not None:
3436            eqObjList = [eqObj,]
3437        else:
3438            eqObjList = Controls['SeqParFitEqList']
3439        UseFlags = G2frame.SeqTable.GetColValues(0)         
3440        for obj in eqObjList:
3441            # assemble refined vars for this equation
3442            varyValueDict.update({var:val for var,val in obj.GetVariedVarVal()})
3443            # lookup dependent var position
3444            depVar = obj.GetDepVar()
3445            if depVar in colLabels:
3446                indx = colLabels.index(depVar)
3447            else:
3448                raise Exception('Dependent variable '+depVar+' not found')
3449            # assemble a list of the independent variables
3450            indepVars = obj.GetIndependentVars()
3451            # loop over each datapoint
3452            for j,row in enumerate(zip(*colList)):
3453                if not UseFlags[j]: continue
3454                # assemble equations to fit
3455                calcobj = G2obj.ExpressionCalcObj(obj)
3456                # prepare a dict of needed independent vars for this expression
3457                indepVarDict = {var:row[i] for i,var in enumerate(colLabels) if var in indepVars}
3458                calcobj.SetupCalc(indepVarDict)               
3459                # values and sigs for current value of dependent var
3460                calcobj.depVal = row[indx]
3461                calcobj.depSig = colSigs[indx][j]
3462                calcObjList.append(calcobj)
3463        # varied parameters
3464        varyList = varyValueDict.keys()
3465        values = varyValues = [varyValueDict[key] for key in varyList]
3466        if not varyList:
3467            print 'no variables to refine!'
3468            return
3469        try:
3470            result = so.leastsq(ParEqEval,varyValues,full_output=True,   #ftol=Ftol,
3471                                args=(calcObjList,varyList)
3472                                )
3473            values = result[0]
3474            covar = result[1]
3475            if covar is None:
3476                raise Exception
3477            esdDict = {}
3478            for i,avar in enumerate(varyList):
3479                esdDict[avar] = np.sqrt(covar[i,i])
3480        except:
3481            print('====> Fit failed')
3482            return
3483        print('==== Fit Results ====')
3484        for obj in eqObjList:
3485            obj.UpdateVariedVars(varyList,values)
3486            ind = '      '
3487            print('  '+obj.GetDepVar()+' = '+obj.expression)
3488            for var in obj.assgnVars:
3489                print(ind+var+' = '+obj.assgnVars[var])
3490            for var in obj.freeVars:
3491                avar = "::"+obj.freeVars[var][0]
3492                val = obj.freeVars[var][1]
3493                if obj.freeVars[var][2]:
3494                    print(ind+var+' = '+avar + " = " + G2mth.ValEsd(val,esdDict[avar]))
3495                else:
3496                    print(ind+var+' = '+avar + " =" + G2mth.ValEsd(val,0))
3497        # create a plot for each parametric variable
3498        for fitnum,obj in enumerate(eqObjList):
3499            calcobj = G2obj.ExpressionCalcObj(obj)
3500            # lookup dependent var position
3501            indx = colLabels.index(obj.GetDepVar())
3502            # assemble a list of the independent variables
3503            indepVars = obj.GetIndependentVars()           
3504            # loop over each datapoint
3505            fitvals = []
3506            for j,row in enumerate(zip(*colList)):
3507                calcobj.SetupCalc(
3508                    {var:row[i] for i,var in enumerate(colLabels) if var in indepVars}
3509                    )
3510                fitvals.append(calcobj.EvalExpression())
3511            G2plt.PlotSelectedSequence(
3512                G2frame,[indx],GetColumnInfo,SelectXaxis,
3513                fitnum,fitvals)
3514
3515    def SingleParEqFit(eqObj):
3516        DoParEqFit(None,eqObj)
3517
3518    def DelParFitEq(event):
3519        'Ask the user to select function to delete'
3520        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
3521        selected = G2G.ItemSelector(
3522            txtlst,G2frame.dataFrame,
3523            multiple=True,
3524            title='Select a parametric equation(s) to remove',
3525            header='Delete equation')
3526        if selected is None: return
3527        Controls['SeqParFitEqList'] = [obj for i,obj in enumerate(Controls['SeqParFitEqList']) if i not in selected]
3528        EnableParFitEqMenus()
3529        if Controls['SeqParFitEqList']: DoParEqFit(event)
3530       
3531    def EditParFitEq(event):
3532        'Edit an existing parametric equation'
3533        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
3534        if len(txtlst) == 1:
3535            selected = 0
3536        else:
3537            selected = G2G.ItemSelector(
3538                txtlst,G2frame.dataFrame,
3539                multiple=False,
3540                title='Select a parametric equation to edit',
3541                header='Edit equation')
3542        if selected is not None:
3543            dlg = G2exG.ExpressionDialog(
3544                G2frame.dataDisplay,indepVarDict,
3545                Controls['SeqParFitEqList'][selected],
3546                depVarDict=depVarDict,
3547                header="Edit the formula for this minimization function",
3548                ExtraButton=['Fit',SingleParEqFit])
3549            newobj = dlg.Show(True)
3550            if newobj:
3551                Controls['SeqParFitEqList'][selected] = newobj
3552                EnableParFitEqMenus()
3553            if Controls['SeqParFitEqList']: DoParEqFit(event)
3554
3555    def AddNewParFitEq(event):
3556        'Create a new parametric equation to be fit to sequential results'
3557
3558        # compile the variable names used in previous freevars to avoid accidental name collisions
3559        usedvarlist = []
3560        for obj in Controls['SeqParFitEqList']:
3561            for var in obj.freeVars:
3562                if obj.freeVars[var][0] not in usedvarlist: usedvarlist.append(obj.freeVars[var][0])
3563
3564        dlg = G2exG.ExpressionDialog(
3565            G2frame.dataDisplay,indepVarDict,
3566            depVarDict=depVarDict,
3567            header='Define an equation to minimize in the parametric fit',
3568            ExtraButton=['Fit',SingleParEqFit],
3569            usedVars=usedvarlist)
3570        obj = dlg.Show(True)
3571        dlg.Destroy()
3572        if obj:
3573            Controls['SeqParFitEqList'].append(obj)
3574            EnableParFitEqMenus()
3575            if Controls['SeqParFitEqList']: DoParEqFit(event)
3576               
3577    def CopyParFitEq(event):
3578        'Copy an existing parametric equation to be fit to sequential results'
3579        # compile the variable names used in previous freevars to avoid accidental name collisions
3580        usedvarlist = []
3581        for obj in Controls['SeqParFitEqList']:
3582            for var in obj.freeVars:
3583                if obj.freeVars[var][0] not in usedvarlist: usedvarlist.append(obj.freeVars[var][0])
3584        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
3585        if len(txtlst) == 1:
3586            selected = 0
3587        else:
3588            selected = G2G.ItemSelector(
3589                txtlst,G2frame.dataFrame,
3590                multiple=False,
3591                title='Select a parametric equation to copy',
3592                header='Copy equation')
3593        if selected is not None:
3594            newEqn = copy.deepcopy(Controls['SeqParFitEqList'][selected])
3595            for var in newEqn.freeVars:
3596                newEqn.freeVars[var][0] = G2obj.MakeUniqueLabel(newEqn.freeVars[var][0],usedvarlist)
3597            dlg = G2exG.ExpressionDialog(
3598                G2frame.dataDisplay,indepVarDict,
3599                newEqn,
3600                depVarDict=depVarDict,
3601                header="Edit the formula for this minimization function",
3602                ExtraButton=['Fit',SingleParEqFit])
3603            newobj = dlg.Show(True)
3604            if newobj:
3605                Controls['SeqParFitEqList'].append(newobj)
3606                EnableParFitEqMenus()
3607            if Controls['SeqParFitEqList']: DoParEqFit(event)
3608                                           
3609    def GridSetToolTip(row,col):
3610        '''Routine to show standard uncertainties for each element in table
3611        as a tooltip
3612        '''
3613        if colSigs[col]:
3614            return u'\u03c3 = '+str(colSigs[col][row])
3615        return ''
3616       
3617    def GridColLblToolTip(col):
3618        '''Define a tooltip for a column. This will be the user-entered value
3619        (from data['variableLabels']) or the default name
3620        '''
3621        if col < 0 or col > len(colLabels):
3622            print 'Illegal column #',col
3623            return
3624        var = colLabels[col]
3625        return variableLabels.get(var,G2obj.fmtVarDescr(var))
3626       
3627    def SetLabelString(event):
3628        '''Define or edit the label for a column in the table, to be used
3629        as a tooltip and for plotting
3630        '''
3631        col = event.GetCol()
3632        if col < 0 or col > len(colLabels):
3633            return
3634        var = colLabels[col]
3635        lbl = variableLabels.get(var,G2obj.fmtVarDescr(var))
3636        dlg = G2G.SingleStringDialog(G2frame.dataFrame,'Set variable label',
3637                                 'Set a new name for variable '+var,lbl,size=(400,-1))
3638        if dlg.Show():
3639            variableLabels[var] = dlg.GetValue()
3640        dlg.Destroy()
3641
3642    def DoSequentialExport(event):
3643        '''Event handler for all Sequential Export menu items
3644        '''
3645        vals = G2frame.dataFrame.SeqExportLookup.get(event.GetId())
3646        if vals is None:
3647            print('Error: Id not found. This should not happen!')
3648        G2IO.ExportSequential(G2frame,data,*vals)
3649   
3650    #def GridRowLblToolTip(row): return 'Row ='+str(row)
3651   
3652    # lookup table for unique cell parameters by symmetry
3653    cellGUIlist = [
3654        [['m3','m3m'],(0,)],
3655        [['3R','3mR'],(0,3)],
3656        [['3','3m1','31m','6/m','6/mmm','4/m','4/mmm'],(0,2)],
3657        [['mmm'],(0,1,2)],
3658        [['2/m'+'a'],(0,1,2,3)],
3659        [['2/m'+'b'],(0,1,2,4)],
3660        [['2/m'+'c'],(0,1,2,5)],
3661        [['-1'],(0,1,2,3,4,5)],
3662        ]
3663    # cell labels
3664    cellUlbl = ('a','b','c',u'\u03B1',u'\u03B2',u'\u03B3') # unicode a,b,c,alpha,beta,gamma
3665
3666    #======================================================================
3667    # start processing sequential results here (UpdateSeqResults)
3668    #======================================================================
3669    if not data:
3670        print 'No sequential refinement results'
3671        return
3672    variableLabels = data.get('variableLabels',{})
3673    data['variableLabels'] = variableLabels
3674    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
3675    Controls = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.root,'Controls'))
3676    # create a place to store Pseudo Vars & Parametric Fit functions, if not present
3677    if 'SeqPseudoVars' not in Controls: Controls['SeqPseudoVars'] = {}
3678    if 'SeqParFitEqList' not in Controls: Controls['SeqParFitEqList'] = []
3679    histNames = data['histNames']
3680    if G2frame.dataDisplay:
3681        G2frame.dataDisplay.Destroy()
3682    if not G2frame.dataFrame.GetStatusBar():
3683        Status = G2frame.dataFrame.CreateStatusBar()
3684        Status.SetStatusText("Select column to export; Double click on column to plot data; on row for Covariance")
3685    sampleParms = GetSampleParms()
3686
3687    # make dict of varied atom coords keyed by absolute position
3688    newAtomDict = data[histNames[0]].get('newAtomDict',{}) # dict with atom positions; relative & absolute
3689    # Possible error: the next might need to be data[histNames[0]]['varyList']
3690    # error will arise if there constraints on coordinates?
3691    atomLookup = {newAtomDict[item][0]:item for item in newAtomDict if item in data['varyList']}
3692   
3693    # make dict of varied cell parameters equivalents
3694    ESDlookup = {} # provides the Dij term for each Ak term (where terms are refined)
3695    Dlookup = {} # provides the Ak term for each Dij term (where terms are refined)
3696    # N.B. These Dij vars are missing a histogram #
3697    newCellDict = data[histNames[0]].get('newCellDict',{})
3698    for item in newCellDict:
3699        if item in data['varyList']:
3700            ESDlookup[newCellDict[item][0]] = item
3701            Dlookup[item] = newCellDict[item][0]
3702    # add coordinate equivalents to lookup table
3703    for parm in atomLookup:
3704        Dlookup[atomLookup[parm]] = parm
3705        ESDlookup[parm] = atomLookup[parm]
3706
3707    # get unit cell & symmetry for all phases & initial stuff for later use
3708    RecpCellTerms = {}
3709    SGdata = {}
3710    uniqCellIndx = {}
3711    initialCell = {}
3712    RcellLbls = {}
3713    zeroDict = {}
3714    for phase in Phases:
3715        phasedict = Phases[phase]
3716        pId = phasedict['pId']
3717        pfx = str(pId)+'::' # prefix for A values from phase
3718        RcellLbls[pId] = [pfx+'A'+str(i) for i in range(6)]
3719        RecpCellTerms[pId] = G2lat.cell2A(phasedict['General']['Cell'][1:7])
3720        zeroDict[pId] = dict(zip(RcellLbls[pId],6*[0.,]))
3721        SGdata[pId] = phasedict['General']['SGData']
3722        laue = SGdata[pId]['SGLaue']
3723        if laue == '2/m':
3724            laue += SGdata[pId]['SGUniq']
3725        for symlist,celllist in cellGUIlist:
3726            if laue in symlist:
3727                uniqCellIndx[pId] = celllist
3728                break
3729        else: # should not happen
3730            uniqCellIndx[pId] = range(6)
3731        for i in uniqCellIndx[pId]:
3732            initialCell[str(pId)+'::A'+str(i)] =  RecpCellTerms[pId][i]
3733
3734    SetDataMenuBar(G2frame,G2frame.dataFrame.SequentialMenu)
3735    G2frame.dataFrame.SetLabel('Sequential refinement results')
3736    if not G2frame.dataFrame.GetStatusBar():
3737        Status = G2frame.dataFrame.CreateStatusBar()
3738        Status.SetStatusText('')
3739    G2frame.dataFrame.Bind(wx.EVT_MENU, OnRenameSelSeq, id=wxID_RENAMESEQSEL)
3740    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSelSeq, id=wxID_SAVESEQSEL)
3741    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSelSeqCSV, id=wxID_SAVESEQSELCSV)
3742    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSeqCSV, id=wxID_SAVESEQCSV)
3743    G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlotSelSeq, id=wxID_PLOTSEQSEL)
3744    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAveSelSeq, id=wxID_AVESEQSEL)
3745    G2frame.dataFrame.Bind(wx.EVT_MENU, OnReOrgSelSeq, id=wxID_ORGSEQSEL)
3746    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewPseudoVar, id=wxADDSEQVAR)
3747    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewDistPseudoVar, id=wxADDSEQDIST)
3748    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewAnglePseudoVar, id=wxADDSEQANGLE)
3749    G2frame.dataFrame.Bind(wx.EVT_MENU, DelPseudoVar, id=wxDELSEQVAR)
3750    G2frame.dataFrame.Bind(wx.EVT_MENU, EditPseudoVar, id=wxEDITSEQVAR)
3751    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewParFitEq, id=wxADDPARFIT)
3752    G2frame.dataFrame.Bind(wx.EVT_MENU, CopyParFitEq, id=wxCOPYPARFIT)
3753    G2frame.dataFrame.Bind(wx.EVT_MENU, DelParFitEq, id=wxDELPARFIT)
3754    G2frame.dataFrame.Bind(wx.EVT_MENU, EditParFitEq, id=wxEDITPARFIT)
3755    G2frame.dataFrame.Bind(wx.EVT_MENU, DoParEqFit, id=wxDOPARFIT)
3756
3757    for id in G2frame.dataFrame.SeqExportLookup:       
3758        G2frame.dataFrame.Bind(wx.EVT_MENU, DoSequentialExport, id=id)
3759
3760    EnablePseudoVarMenus()
3761    EnableParFitEqMenus()
3762
3763    # scan for locations where the variables change
3764    VaryListChanges = [] # histograms where there is a change
3765    combinedVaryList = []
3766    firstValueDict = {}
3767    vallookup = {}
3768    posdict = {}
3769    prevVaryList = []
3770    foundNames = []
3771    for i,name in enumerate(histNames):
3772        if name not in data:
3773            print("Error: "+name+" not found!")
3774            continue
3775        foundNames.append(name)
3776        for var,val,sig in zip(data[name]['varyList'],data[name]['variables'],data[name]['sig']):
3777            svar = striphist(var,'*') # wild-carded
3778            if svar not in combinedVaryList:
3779                # add variables to list as they appear
3780                combinedVaryList.append(svar)
3781                firstValueDict[svar] = (val,sig)
3782        if prevVaryList != data[name]['varyList']: # this refinement has a different refinement list from previous
3783            prevVaryList = data[name]['varyList']
3784            vallookup[name] = dict(zip(data[name]['varyList'],data[name]['variables']))
3785            posdict[name] = {}
3786            for var in data[name]['varyList']:
3787                svar = striphist(var,'*')
3788                posdict[name][combinedVaryList.index(svar)] = svar
3789            VaryListChanges.append(name)
3790    if len(VaryListChanges) > 1:
3791        G2frame.dataFrame.SequentialFile.Enable(wxID_ORGSEQSEL,True)
3792    else:
3793        G2frame.dataFrame.SequentialFile.Enable(wxID_ORGSEQSEL,False)
3794    #-----------------------------------------------------------------------------------
3795    # build up the data table by columns -----------------------------------------------
3796    histNames = foundNames
3797    nRows = len(histNames)
3798    colList = [nRows*[True]]
3799    colSigs = [None]
3800    colLabels = ['Use']
3801    Types = [wg.GRID_VALUE_BOOL]
3802    # start with Rwp values
3803    if 'IMG ' not in histNames[0][:4]:
3804        colList += [[data[name]['Rvals']['Rwp'] for name in histNames]]
3805        colSigs += [None]
3806        colLabels += ['Rwp']
3807        Types += [wg.GRID_VALUE_FLOAT+':10,3',]
3808    # add % change in Chi^2 in last cycle
3809    if histNames[0][:4] not in ['SASD','IMG '] and Controls.get('ShowCell'):
3810        colList += [[100.*data[name]['Rvals'].get('DelChi2',-1) for name in histNames]]
3811        colSigs += [None]
3812        colLabels += [u'\u0394\u03C7\u00B2 (%)']
3813        Types += [wg.GRID_VALUE_FLOAT,]
3814    deltaChiCol = len(colLabels)-1
3815    # add changing sample parameters to table
3816    for key in sampleParms:
3817        colList += [sampleParms[key]]
3818        colSigs += [None]
3819        colLabels += [key]
3820        Types += [wg.GRID_VALUE_FLOAT,]
3821    sampleDict = {}
3822    for i,name in enumerate(histNames):
3823        sampleDict[name] = dict(zip(sampleParms.keys(),[sampleParms[key][i] for key in sampleParms.keys()])) 
3824    # add unique cell parameters TODO: review this where the cell symmetry changes (when possible)
3825    if Controls.get('ShowCell',False):
3826        for pId in sorted(RecpCellTerms):
3827            pfx = str(pId)+'::' # prefix for A values from phase
3828            cells = []
3829            cellESDs = []
3830            colLabels += [pfx+cellUlbl[i] for i in uniqCellIndx[pId]]
3831            colLabels += [pfx+'Vol']
3832            Types += (1+len(uniqCellIndx[pId]))*[wg.GRID_VALUE_FLOAT,]
3833            for name in histNames:
3834                covData = {
3835                    'varyList': [Dlookup.get(striphist(v),v) for v in data[name]['varyList']],
3836                    'covMatrix': data[name]['covMatrix']
3837                    }
3838                A = RecpCellTerms[pId][:] # make copy of starting A values
3839                # update with refined values
3840                for i in range(6):
3841                    var = str(pId)+'::A'+str(i)
3842                    if var in ESDlookup:
3843                        val = data[name]['newCellDict'][ESDlookup[var]][1] # get refined value
3844                        A[i] = val # override with updated value
3845                # apply symmetry
3846                Albls = [pfx+'A'+str(i) for i in range(6)]
3847                cellDict = dict(zip(Albls,A))
3848                A,zeros = G2stIO.cellFill(pfx,SGdata[pId],cellDict,zeroDict[pId])
3849                # convert to direct cell & add only unique values to table
3850                c = G2lat.A2cell(A)
3851                vol = G2lat.calc_V(A)
3852                cE = G2stIO.getCellEsd(pfx,SGdata[pId],A,covData)
3853                cells += [[c[i] for i in uniqCellIndx[pId]]+[vol]]
3854                cellESDs += [[cE[i] for i in uniqCellIndx[pId]]+[cE[-1]]]
3855            colList += zip(*cells)
3856            colSigs += zip(*cellESDs)
3857    # sort out the variables in their selected order
3858    varcols = 0
3859    for d in posdict.itervalues():
3860        varcols = max(varcols,max(d.keys())+1)
3861    # get labels for each column
3862    for i in range(varcols):
3863        lbl = ''
3864        for h in VaryListChanges:
3865            if posdict[h].get(i):
3866                if posdict[h].get(i) in lbl: continue
3867                if lbl != "": lbl += '/'
3868                lbl += posdict[h].get(i)
3869        colLabels.append(lbl)
3870    Types += varcols*[wg.GRID_VALUE_FLOAT]
3871    vals = []
3872    esds = []
3873    varsellist = None        # will be a list of variable names in the order they are selected to appear
3874    # tabulate values for each hist, leaving None for blank columns
3875    for name in histNames:
3876        if name in posdict:
3877            varsellist = [posdict[name].get(i) for i in range(varcols)]
3878            # translate variable names to how they will be used in the headings
3879            vs = [striphist(v,'*') for v in data[name]['varyList']]
3880            # determine the index for each column (or None) in the data[]['variables'] and ['sig'] lists
3881            sellist = [vs.index(v) if v is not None else None for v in varsellist]
3882            #sellist = [i if striphist(v,'*') in varsellist else None for i,v in enumerate(data[name]['varyList'])]
3883        if not varsellist: raise Exception()
3884        vals.append([data[name]['variables'][s] if s is not None else None for s in sellist])
3885        esds.append([data[name]['sig'][s] if s is not None else None for s in sellist])
3886        #GSASIIpath.IPyBreak()
3887    colList += zip(*vals)
3888    colSigs += zip(*esds)
3889    # compute and add weight fractions to table if varied
3890    for phase in Phases:
3891        var = str(Phases[phase]['pId'])+':*:Scale'
3892        if var not in combinedVaryList: continue
3893        wtFrList = []
3894        sigwtFrList = []
3895        for i,name in enumerate(histNames):
3896            wtFrSum = 0.
3897            for phase1 in Phases:
3898                wtFrSum += Phases[phase1]['Histograms'][name]['Scale'][0]*Phases[phase1]['General']['Mass']
3899            var = str(Phases[phase]['pId'])+':'+str(i)+':Scale'
3900            wtFr = Phases[phase]['Histograms'][name]['Scale'][0]*Phases[phase]['General']['Mass']/wtFrSum
3901            wtFrList.append(wtFr)
3902            if var in data[name]['varyList']:
3903                sig = data[name]['sig'][data[name]['varyList'].index(var)]*wtFr/Phases[phase]['Histograms'][name]['Scale'][0]
3904            else:
3905                sig = 0.0
3906            sigwtFrList.append(sig)
3907        colLabels.append(str(Phases[phase]['pId'])+':*:WgtFrac')
3908        colList += [wtFrList]
3909        colSigs += [sigwtFrList]
3910               
3911    # tabulate constrained variables, removing histogram numbers if needed
3912    # from parameter label
3913    depValDict = {}
3914    depSigDict = {}
3915    for name in histNames:
3916        for var in data[name].get('depParmDict',{}):
3917            val,sig = data[name]['depParmDict'][var]
3918            svar = striphist(var,'*')
3919            if svar not in depValDict:
3920               depValDict[svar] = [val]
3921               depSigDict[svar] = [sig]
3922            else:
3923               depValDict[svar].append(val)
3924               depSigDict[svar].append(sig)
3925    # add the dependent constrained variables to the table
3926    for var in sorted(depValDict):
3927        if len(depValDict[var]) != len(histNames): continue
3928        colLabels.append(var)
3929        Types += [wg.GRID_VALUE_FLOAT,]
3930        colSigs += [depSigDict[var]]
3931        colList += [depValDict[var]]
3932
3933    # add atom parameters to table
3934    colLabels += atomLookup.keys()
3935    Types += len(atomLookup)*[wg.GRID_VALUE_FLOAT]
3936    for parm in sorted(atomLookup):
3937        colList += [[data[name]['newAtomDict'][atomLookup[parm]][1] for name in histNames]]
3938        if atomLookup[parm] in data[histNames[0]]['varyList']:
3939            col = data[histNames[0]]['varyList'].index(atomLookup[parm])
3940            colSigs += [[data[name]['sig'][col] for name in histNames]]
3941        else:
3942            colSigs += [None] # should not happen
3943    # evaluate Pseudovars, their ESDs and add them to grid
3944    for expr in Controls['SeqPseudoVars']:
3945        obj = Controls['SeqPseudoVars'][expr]
3946        calcobj = G2obj.ExpressionCalcObj(obj)
3947        valList = []
3948        esdList = []
3949        for seqnum,name in enumerate(histNames):
3950            sigs = data[name]['sig']
3951            G2mv.InitVars()
3952            parmDict = data[name].get('parmDict')
3953            constraintInfo = data[name].get('constraintInfo',[[],[],{},[],seqnum])
3954            groups,parmlist,constrDict,fixedList,ihst = constraintInfo
3955            varyList = data[name]['varyList']
3956            parmDict = data[name]['parmDict']
3957            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmDict,SeqHist=ihst)
3958            if 'Dist' in expr:
3959                derivs = G2mth.CalcDistDeriv(obj.distance_dict,obj.distance_atoms, parmDict)
3960                pId = obj.distance_dict['pId']
3961                aId,bId = obj.distance_atoms
3962                varyNames = ['%d::dA%s:%d'%(pId,ip,aId) for ip in ['x','y','z']]
3963                varyNames += ['%d::dA%s:%d'%(pId,ip,bId) for ip in ['x','y','z']]
3964                VCoV = G2mth.getVCov(varyNames,varyList,data[name]['covMatrix'])
3965                esdList.append(np.sqrt(np.inner(derivs,np.inner(VCoV,derivs.T)) ))
3966#                GSASIIpath.IPyBreak()
3967            elif 'Angle' in expr:
3968                derivs = G2mth.CalcAngleDeriv(obj.angle_dict,obj.angle_atoms, parmDict)
3969                pId = obj.angle_dict['pId']
3970                aId,bId = obj.angle_atoms
3971                varyNames = ['%d::dA%s:%d'%(pId,ip,aId) for ip in ['x','y','z']]
3972                varyNames += ['%d::dA%s:%d'%(pId,ip,bId[0]) for ip in ['x','y','z']]
3973                varyNames += ['%d::dA%s:%d'%(pId,ip,bId[1]) for ip in ['x','y','z']]
3974                VCoV = G2mth.getVCov(varyNames,varyList,data[name]['covMatrix'])
3975                esdList.append(np.sqrt(np.inner(derivs,np.inner(VCoV,derivs.T)) ))
3976            else:
3977                derivs = np.array(
3978                    [EvalPSvarDeriv(calcobj,parmDict.copy(),sampleDict[name],var,ESD)
3979                     for var,ESD in zip(varyList,sigs)])
3980                esdList.append(np.sqrt(
3981                    np.inner(derivs,np.inner(data[name]['covMatrix'],derivs.T)) ))
3982            PSvarDict = parmDict.copy()
3983            PSvarDict.update(sampleDict[name])
3984            UpdateParmDict(PSvarDict)
3985            calcobj.UpdateDict(PSvarDict)
3986            valList.append(calcobj.EvalExpression())
3987#            if calcobj.su is not None: esdList[-1] = calcobj.su
3988        if not esdList:
3989            esdList = None
3990        colList += [valList]
3991        colSigs += [esdList]
3992        colLabels += [expr]
3993        Types += [wg.GRID_VALUE_FLOAT,]
3994    #---- table build done -------------------------------------------------------------
3995
3996    # Make dict needed for creating & editing pseudovars (PSvarDict).
3997    name = histNames[0]
3998    parmDict = data[name].get('parmDict',{})
3999    PSvarDict = parmDict.copy()
4000    PSvarDict.update(sampleParms)
4001    UpdateParmDict(PSvarDict)
4002    # Also dicts of dependent (depVarDict) & independent vars (indepVarDict)
4003    # for Parametric fitting from the data table
4004    parmDict = dict(zip(colLabels,zip(*colList)[0])) # scratch dict w/all values in table
4005    parmDict.update(
4006        {var:val for var,val in data[name].get('newCellDict',{}).values()} #  add varied reciprocal cell terms
4007    )
4008    name = histNames[0]
4009
4010    #******************************************************************************
4011    # create a set of values for example evaluation of pseudovars and
4012    # this does not work for refinements that have differing numbers of variables.
4013    #raise Exception
4014    indepVarDict = {}     #  values in table w/o ESDs
4015    depVarDict = {}
4016    for i,var in enumerate(colLabels):
4017        if var == 'Use': continue
4018        if colList[i][0] is None:
4019            val,sig = firstValueDict.get(var,[None,None])
4020        elif colSigs[i]:
4021            val,sig = colList[i][0],colSigs[i][0]
4022        else:
4023            val,sig = colList[i][0],None
4024        if val is None:
4025            continue
4026        elif sig is None:
4027            indepVarDict[var] = val
4028        elif striphist(var) not in Dlookup:
4029            depVarDict[var] = val
4030    # add recip cell coeff. values
4031    depVarDict.update({var:val for var,val in data[name].get('newCellDict',{}).values()})
4032
4033    G2frame.dataFrame.currentGrids = []
4034    G2frame.dataDisplay = G2G.GSGrid(parent=G2frame.dataFrame)
4035    G2frame.SeqTable = G2G.Table(
4036        [list(cl) for cl in zip(*colList)],     # convert from columns to rows
4037        colLabels=colLabels,rowLabels=histNames,types=Types)
4038    G2frame.dataDisplay.SetTable(G2frame.SeqTable, True)
4039    #G2frame.dataDisplay.EnableEditing(False)
4040    # make all but first column read-only
4041    for c in range(1,len(colLabels)):
4042        for r in range(nRows):
4043            G2frame.dataDisplay.SetCellReadOnly(r,c)
4044    G2frame.dataDisplay.Bind(wg.EVT_GRID_LABEL_LEFT_DCLICK, PlotSelect)
4045    G2frame.dataDisplay.Bind(wg.EVT_GRID_LABEL_RIGHT_CLICK, SetLabelString)
4046    G2frame.dataDisplay.SetRowLabelSize(8*len(histNames[0]))       #pretty arbitrary 8
4047    G2frame.dataDisplay.SetMargins(0,0)
4048    G2frame.dataDisplay.AutoSizeColumns(True)
4049    if prevSize:
4050        G2frame.dataFrame.setSizePosLeft(prevSize)
4051    else:
4052        G2frame.dataFrame.setSizePosLeft([700,350])
4053    # highlight unconverged shifts
4054    if histNames[0][:4] not in ['SASD','IMG ']:
4055        for row,name in enumerate(histNames):
4056            deltaChi = G2frame.SeqTable.GetValue(row,deltaChiCol)
4057            if deltaChi > 10.:
4058                G2frame.dataDisplay.SetCellStyle(row,deltaChiCol,color=wx.Colour(255,0,0))
4059            elif deltaChi > 1.0:
4060                G2frame.dataDisplay.SetCellStyle(row,deltaChiCol,color=wx.Colour(255,255,0))
4061    G2frame.dataDisplay.InstallGridToolTip(GridSetToolTip,GridColLblToolTip)
4062    G2frame.dataDisplay.SendSizeEvent() # resize needed on mac
4063    G2frame.dataDisplay.Refresh() # shows colored text on mac
4064   
4065################################################################################
4066#####  Main PWDR panel
4067################################################################################           
4068       
4069def UpdatePWHKPlot(G2frame,kind,item):
4070    '''Called when the histogram main tree entry is called. Displays the
4071    histogram weight factor, refinement statistics for the histogram
4072    and the range of data for a simulation.
4073
4074    Also invokes a plot of the histogram.
4075    '''
4076    def onEditSimRange(event):
4077        'Edit simulation range'
4078        inp = [
4079            min(data[1][0]),
4080            max(data[1][0]),
4081            None
4082            ]
4083        inp[2] = (inp[1] - inp[0])/(len(data[1][0])-1.)
4084        names = ('start angle', 'end angle', 'step size')
4085        dlg = G2G.ScrolledMultiEditor(
4086            G2frame,[inp] * len(inp), range(len(inp)), names,
4087            header='Edit simulation range',
4088            minvals=(0.001,0.001,0.0001),
4089            maxvals=(180.,180.,.1),
4090            )
4091        dlg.CenterOnParent()
4092        val = dlg.ShowModal()
4093        dlg.Destroy()
4094        if val != wx.ID_OK: return
4095        if inp[0] > inp[1]:
4096            end,start,step = inp
4097        else:               
4098            start,end,step = inp
4099        step = abs(step)
4100        N = int((end-start)/step)+1
4101        newdata = np.linspace(start,end,N,True)
4102        if len(newdata) < 2: return # too small a range - reject
4103        data[1] = [newdata,np.zeros_like(newdata),np.ones_like(newdata),
4104            np.zeros_like(newdata),np.zeros_like(newdata),np.zeros_like(newdata)]
4105        Tmin = newdata[0]
4106        Tmax = newdata[-1]
4107        G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,item,'Limits'),
4108            [(Tmin,Tmax),[Tmin,Tmax]])
4109        UpdatePWHKPlot(G2frame,kind,item) # redisplay data screen
4110
4111    def OnPlot3DHKL(event):
4112        refList = data[1]['RefList']
4113        FoMax = np.max(refList.T[8+Super])
4114        Hmin = np.array([int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))])
4115        Hmax = np.array([int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))])
4116        Vpoint = np.array([int(np.mean(refList.T[0])),int(np.mean(refList.T[1])),int(np.mean(refList.T[2]))])
4117        controls = {'Type' : 'Fosq','Iscale' : False,'HKLmax' : Hmax,'HKLmin' : Hmin,'Zone':False,'viewKey':'L',
4118            'FoMax' : FoMax,'Scale' : 1.0,'Drawing':{'viewPoint':[Vpoint,[]],'default':Vpoint[:],
4119            'backColor':[0,0,0],'depthFog':False,'Zclip':10.0,'cameraPos':10.,'Zstep':0.05,'viewUp':[0,1,0],
4120            'Scale':1.0,'oldxy':[],'viewDir':[0,0,1]},'Super':Super,'SuperVec':SuperVec}
4121        G2plt.Plot3DSngl(G2frame,newPlot=True,Data=controls,hklRef=refList,Title=phaseName)
4122       
4123    def OnPlotAll3DHKL(event):
4124        choices = GetPatternTreeDataNames(G2frame,['HKLF',])
4125        dlg = G2G.G2MultiChoiceDialog(G2frame, 'Select reflection sets to plot',
4126            'Use data',choices)
4127        try:
4128            if dlg.ShowModal() == wx.ID_OK:
4129                refNames = [choices[i] for i in dlg.GetSelections()]
4130            else:
4131                return
4132        finally:
4133            dlg.Destroy()
4134        refList = np.zeros(0)
4135        for name in refNames:
4136            Id = GetPatternTreeItemId(G2frame,G2frame.root, name)
4137            reflData = G2frame.PatternTree.GetItemPyData(Id)[1]
4138            if len(refList):
4139                refList = np.concatenate((refList,reflData['RefList']))
4140            else:
4141                refList = reflData['RefList']
4142           
4143        FoMax = np.max(refList.T[8+Super])
4144        Hmin = np.array([int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))])
4145        Hmax = np.array([int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))])
4146        Vpoint = [int(np.mean(refList.T[0])),int(np.mean(refList.T[1])),int(np.mean(refList.T[2]))]
4147        controls = {'Type' : 'Fosq','Iscale' : False,'HKLmax' : Hmax,'HKLmin' : Hmin,'Zone':False,'viewKey':'L',
4148            'FoMax' : FoMax,'Scale' : 1.0,'Drawing':{'viewPoint':[Vpoint,[]],'default':Vpoint[:],
4149            'backColor':[0,0,0],'depthFog':False,'Zclip':10.0,'cameraPos':10.,'Zstep':0.05,'viewUp':[0,1,0],
4150            'Scale':1.0,'oldxy':[],'viewDir':[1,0,0]},'Super':Super,'SuperVec':SuperVec}
4151        G2plt.Plot3DSngl(G2frame,newPlot=True,Data=controls,hklRef=refList,Title=phaseName)
4152                 
4153    def OnMergeHKL(event):
4154        Name = G2frame.PatternTree.GetItemText(G2frame.PatternId)
4155        Inst = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,
4156            G2frame.PatternId,'Instrument Parameters'))
4157        CId = GetPatternTreeItemId(G2frame,G2frame.PatternId,'Comments')
4158        if CId:
4159            Comments = G2frame.PatternTree.GetItemPyData(CId)
4160        else:
4161            Comments = []
4162        refList = np.copy(data[1]['RefList'])
4163        Comments.append(' Merging %d reflections from %s'%(len(refList),Name))
4164        dlg = MergeDialog(G2frame,data)
4165        try:
4166            if dlg.ShowModal() == wx.ID_OK:
4167                Trans,Cent,Laue = dlg.GetSelection()
4168            else:
4169                return
4170        finally:
4171            dlg.Destroy()
4172        Super = data[1]['Super']
4173        refList,badRefs = G2lat.transposeHKLF(Trans,Super,refList)
4174        if len(badRefs):    #do I want to list badRefs?
4175            G2frame.ErrorDialog('Failed transformation','Matrix yields fractional hkl indices')
4176            return
4177        Comments.append(" Transformation M*H = H' applied; M=")
4178        Comments.append(str(Trans))
4179        refList = G2lat.LaueUnique(Laue,refList)
4180        dlg = wx.ProgressDialog('Build HKL dictonary','',len(refList)+1, 
4181            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE)
4182        HKLdict = {}
4183        for ih,hkl in enumerate(refList):
4184            if str(hkl[:3+Super]) not in HKLdict:
4185                HKLdict[str(hkl[:3+Super])] = [hkl[:3+Super],[hkl[3+Super:],]]
4186            else:
4187                HKLdict[str(hkl[:3+Super])][1].append(hkl[3+Super:])
4188            dlg.Update(ih)
4189        dlg.Destroy()
4190        mergeRef = []
4191        dlg = wx.ProgressDialog('Processing merge','',len(HKLdict)+1, 
4192            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE)
4193        sumDf = 0.
4194        sumFo = 0.
4195        for ih,hkl in enumerate(HKLdict):
4196            HKL = HKLdict[hkl]
4197            newHKL = list(HKL[0])+list(HKL[1][0])
4198            if len(HKL[1]) > 1:
4199                fos = np.array(HKL[1])
4200                wFo = 1/fos[:,3]**2
4201                Fo = np.average(fos[:,2],weights=wFo)
4202                std = np.std(fos[:,2])
4203                sig = np.sqrt(np.mean(fos[:,3])**2+std**2)
4204                sumFo += np.sum(fos[:,2])
4205                sumDf += np.sum(np.abs(fos[:,2]-Fo))
4206                dlg.Update(ih)
4207                newHKL[5+Super] = Fo
4208                newHKL[6+Super] = sig
4209                newHKL[8+Super] = Fo
4210            if newHKL[5+Super] > 0.:
4211                mergeRef.append(list(newHKL)) 
4212        dlg.Destroy()
4213        if Super:
4214            mergeRef = G2mth.sortArray(G2mth.sortArray(G2mth.sortArray(G2mth.sortArray(mergeRef,3),2),1),0)
4215        else:
4216            mergeRef = G2mth.sortArray(G2mth.sortArray(G2mth.sortArray(mergeRef,2),1),0)
4217        mergeRef = np.array(mergeRef)
4218        if sumFo:
4219            mtext = ' merge R = %6.2f%s for %d reflections in %s'%(100.*sumDf/sumFo,'%',mergeRef.shape[0],Laue)
4220            print mtext
4221            Comments.append(mtext)
4222        else:
4223            print 'nothing to merge for %s reflections'%(mergeRef.shape[0])
4224        HKLFlist = []
4225        newName = Name+' '+Laue
4226        if G2frame.PatternTree.GetCount():
4227            item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
4228            while item:
4229                name = G2frame.PatternTree.GetItemText(item)
4230                if name.startswith('HKLF ') and name not in HKLFlist:
4231                    HKLFlist.append(name)
4232                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
4233        newName = G2obj.MakeUniqueLabel(newName,HKLFlist)
4234        newData = copy.deepcopy(data)
4235        newData[0]['ranId'] = ran.randint(0,sys.maxint)
4236        newData[1]['RefList'] = mergeRef
4237        Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=newName)
4238        G2frame.PatternTree.SetItemPyData(
4239            G2frame.PatternTree.AppendItem(Id,text='Comments'),Comments)
4240        G2frame.PatternTree.SetItemPyData(Id,newData)
4241        G2frame.PatternTree.SetItemPyData(
4242            G2frame.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
4243        G2frame.PatternTree.SetItemPyData(
4244            G2frame.PatternTree.AppendItem(Id,text='Reflection List'),{})  #dummy entry for GUI use
4245                   
4246    def OnErrorAnalysis(event):
4247        G2plt.PlotDeltSig(G2frame,kind)
4248       
4249    def OnWtFactor(event):
4250        event.Skip()
4251        try:
4252            val = float(wtval.GetValue())
4253        except ValueError:
4254            val = data[0]['wtFactor']
4255        data[0]['wtFactor'] = val
4256        wtval.SetValue('%.3f'%(val))
4257       
4258#    def OnCompression(event):
4259#        data[0] = int(comp.GetValue())
4260       
4261    def onCopyPlotCtrls(event):
4262        '''Respond to menu item to copy multiple sections from a histogram.
4263        Need this here to pass on the G2frame object.
4264        '''
4265        G2pdG.CopyPlotCtrls(G2frame)
4266
4267    def onCopySelectedItems(event):
4268        '''Respond to menu item to copy multiple sections from a histogram.
4269        Need this here to pass on the G2frame object.
4270        '''
4271        G2pdG.CopySelectedHistItems(G2frame)
4272           
4273    data = G2frame.PatternTree.GetItemPyData(item)
4274#patches
4275    if not data:
4276        return
4277    if 'wtFactor' not in data[0]:
4278        data[0] = {'wtFactor':1.0}
4279#    if kind == 'PWDR' and 'Compression' not in data[0]:
4280#        data[0]['Compression'] = 1
4281