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

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

fixes to window resize issues

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