source: trunk/GSASIIgrid.py @ 2684

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

Image Parameters: split menus, more xfer angles options

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