source: trunk/GSASIIgrid.py @ 2576

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

begin powder selection option
image zoom retained between image controls & mask selection

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