source: trunk/GSASIIgrid.py @ 2546

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

cleanup of all GSASII main routines - remove unused variables, dead code, etc.

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