source: trunk/GSASIIgrid.py @ 2516

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

revise import to not assume Bank 1 with multibank instparm files; deal with unicode problem in CIF files; improve atoms use of selection from menu; add Pawley menu variable selection (plenty more to do)

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