source: trunk/GSASIIgrid.py @ 2451

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

Add target atom type selection to sphere of enclosure

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