source: branch/2frame/GSASIIgrid.py @ 2898

Last change on this file since 2898 was 2898, checked in by toby, 4 years ago

fix scroll bars on tree panel, start debugging on data panel

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