source: trunk/GSASIIgrid.py @ 2520

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

remove unused & commented Srr fctr & deriv routines
Mag moment derivatives semireasonable but not close
transform nucl - mag ok if no off diag terms in transform matrix

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