source: trunk/GSASIIgrid.py @ 2450

Last change on this file since 2450 was 2450, checked in by vondreele, 7 years ago

fix error in matrix in getCellEsd & RetDistAngle?
Add new atom drawing option for sphere of enclosure

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