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

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

merge trunk changes to 2984

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